]> git.saurik.com Git - apple/configd.git/blob - configd.tproj/session.c
configd-395.10.tar.gz
[apple/configd.git] / configd.tproj / session.c
1 /*
2 * Copyright (c) 2000, 2001, 2003-2005, 2007-2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
29 *
30 * March 24, 2000 Allan Nathanson <ajn@apple.com>
31 * - initial revision
32 */
33
34 #include "configd.h"
35 #include "configd_server.h"
36 #include "session.h"
37
38 #include <unistd.h>
39 #include <bsm/libbsm.h>
40 #include <sandbox.h>
41
42
43 /* information maintained for each active session */
44 static serverSessionRef *sessions = NULL;
45 static int nSessions = 0;
46
47 /* CFMachPortInvalidation runloop */
48 static CFRunLoopRef sessionRunLoop = NULL;
49
50
51 static void
52 CFMachPortInvalidateSessionCallback(CFMachPortRef port, void *info)
53 {
54 CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent();
55
56 // Bear trap
57 if (!_SC_CFEqual(currentRunLoop, sessionRunLoop)) {
58 _SC_crash("SCDynamicStore CFMachPort invalidation error",
59 CFSTR("CFMachPort invalidated"),
60 CFSTR("An SCDynamicStore CFMachPort has incorrectly been invalidated."));
61 }
62 }
63
64
65 __private_extern__
66 serverSessionRef
67 getSession(mach_port_t server)
68 {
69 int i;
70
71 if (server == MACH_PORT_NULL) {
72 SCLog(TRUE, LOG_ERR, CFSTR("Excuse me, why is getSession() being called with an invalid port?"));
73 return NULL;
74 }
75
76 for (i = 0; i < nSessions; i++) {
77 serverSessionRef thisSession = sessions[i];
78
79 if (thisSession == NULL) {
80 /* found an empty slot, skip it */
81 continue;
82 } else if (thisSession->key == server) {
83 return thisSession; /* we've seen this server before */
84 } else if (thisSession->store &&
85 (((SCDynamicStorePrivateRef)thisSession->store)->notifySignalTask == server)) {
86 return thisSession;
87 }
88 }
89
90 /* no sessions available */
91 return NULL;
92 }
93
94
95 __private_extern__
96 serverSessionRef
97 addSession(mach_port_t server, CFStringRef (*copyDescription)(const void *info))
98 {
99 CFMachPortContext context = { 0, NULL, NULL, NULL, NULL };
100 mach_port_t mp = server;
101 int n = -1;
102
103 /* save current (SCDynamicStore) runloop */
104 if (sessionRunLoop == NULL) {
105 sessionRunLoop = CFRunLoopGetCurrent();
106 }
107
108 if (nSessions <= 0) {
109 /* new session (actually, the first) found */
110 sessions = malloc(sizeof(serverSessionRef));
111 n = 0;
112 nSessions = 1;
113 } else {
114 int i;
115
116 for (i = 0; i < nSessions; i++) {
117 if (sessions[i] == NULL) {
118 /* found an empty slot, use it */
119 n = i;
120 break;
121 }
122 }
123 /* new session identified */
124 if (n < 0) {
125 /* no empty slots, add one to the list */
126 n = nSessions++;
127 sessions = reallocf(sessions, ((nSessions) * sizeof(serverSessionRef)));
128 }
129 }
130
131 // allocate a new session for this server
132 sessions[n] = malloc(sizeof(serverSession));
133 bzero(sessions[n], sizeof(serverSession));
134
135 // create server port
136 context.info = sessions[n];
137 context.copyDescription = copyDescription;
138
139 if (server == MACH_PORT_NULL) {
140 // create mach port for SCDynamicStore client
141 (void) mach_port_allocate(mach_task_self(),
142 MACH_PORT_RIGHT_RECEIVE,
143 &mp);
144 }
145
146 //
147 // Note: we create the CFMachPort *before* we insert a send
148 // right present to ensure that CF does not establish
149 // its dead name notification.
150 //
151 sessions[n]->serverPort = _SC_CFMachPortCreateWithPort("SCDynamicStore/session",
152 mp,
153 configdCallback,
154 &context);
155
156 //
157 // Set bear trap (an invalidation callback) to catch other
158 // threads stomping on us
159 //
160 CFMachPortSetInvalidationCallBack(sessions[n]->serverPort,
161 CFMachPortInvalidateSessionCallback);
162
163 if (server == MACH_PORT_NULL) {
164 // insert send right that will be moved to the client
165 (void) mach_port_insert_right(mach_task_self(),
166 mp,
167 mp,
168 MACH_MSG_TYPE_MAKE_SEND);
169 }
170
171 sessions[n]->key = mp;
172 // sessions[n]->serverRunLoopSource = NULL;
173 // sessions[n]->store = NULL;
174 sessions[n]->callerEUID = 1; /* not "root" */
175 sessions[n]->callerRootAccess = UNKNOWN;
176 sessions[n]->callerWriteAccess = UNKNOWN;
177
178 return sessions[n];
179 }
180
181
182 __private_extern__
183 void
184 removeSession(mach_port_t server)
185 {
186 int i;
187 serverSessionRef thisSession;
188 CFStringRef sessionKey;
189
190 for (i = 0; i < nSessions; i++) {
191 thisSession = sessions[i];
192
193 if (thisSession == NULL) {
194 /* found an empty slot, skip it */
195 continue;
196 } else if (thisSession->key == server) {
197 /*
198 * We don't need any remaining information in the
199 * sessionData dictionary, remove it.
200 */
201 sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), server);
202 CFDictionaryRemoveValue(sessionData, sessionKey);
203 CFRelease(sessionKey);
204
205 /*
206 * Lastly, get rid of the per-session structure.
207 */
208 free(thisSession);
209 sessions[i] = NULL;
210
211 return;
212 }
213 }
214
215 return;
216 }
217
218
219 __private_extern__
220 void
221 cleanupSession(mach_port_t server)
222 {
223 int i;
224
225 for (i = 0; i < nSessions; i++) {
226 serverSessionRef thisSession = sessions[i];
227
228 if ((thisSession != NULL) && (thisSession->key == server)) {
229 /*
230 * session entry still exists.
231 */
232
233 if (_configd_trace) {
234 SCTrace(TRUE, _configd_trace, CFSTR("cleanup : %5d\n"), server);
235 }
236
237 /*
238 * Ensure that any changes made while we held the "lock"
239 * are discarded.
240 */
241 if ((storeLocked > 0) &&
242 ((SCDynamicStorePrivateRef)thisSession->store)->locked) {
243 /*
244 * swap store and associated data which, after
245 * being closed, will result in the restoration
246 * of the original pre-"locked" data.
247 */
248 _swapLockedStoreData();
249 }
250
251 /*
252 * Close any open connections including cancelling any outstanding
253 * notification requests and releasing any locks.
254 */
255 __MACH_PORT_DEBUG(TRUE, "*** cleanupSession", server);
256 (void) __SCDynamicStoreClose(&thisSession->store, TRUE);
257 __MACH_PORT_DEBUG(TRUE, "*** cleanupSession (after __SCDynamicStoreClose)", server);
258
259 /*
260 * Our send right has already been removed. Remove our receive right.
261 */
262 (void) mach_port_mod_refs(mach_task_self(), server, MACH_PORT_RIGHT_RECEIVE, -1);
263
264 /*
265 * Lastly, remove the session entry.
266 */
267 removeSession(server);
268
269 return;
270 }
271 }
272
273 SCLog(TRUE, LOG_ERR, CFSTR("MACH_NOTIFY_NO_SENDERS w/no session, port = %d"), server);
274 __MACH_PORT_DEBUG(TRUE, "*** cleanupSession w/no session", server);
275 return;
276 }
277
278
279 __private_extern__
280 void
281 listSessions(FILE *f)
282 {
283 int i;
284
285 SCPrint(TRUE, f, CFSTR("Current sessions :\n"));
286 for (i = 0; i < nSessions; i++) {
287 serverSessionRef thisSession = sessions[i];
288
289 if (thisSession == NULL) {
290 continue;
291 }
292
293 SCPrint(TRUE, f, CFSTR("\t%d : port = 0x%x"), i, thisSession->key);
294
295 if (thisSession->store != NULL) {
296 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)thisSession->store;
297
298 if (storePrivate->notifySignalTask != TASK_NULL) {
299 SCPrint(TRUE, f, CFSTR(", task = %d"), storePrivate->notifySignalTask);
300 }
301 }
302
303 if (sessionData != NULL) {
304 CFDictionaryRef info;
305 CFStringRef key;
306
307 key = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), thisSession->key);
308 info = CFDictionaryGetValue(sessionData, key);
309 CFRelease(key);
310 if (info != NULL) {
311 CFStringRef name;
312
313 name = CFDictionaryGetValue(info, kSCDName);
314 if (name != NULL) {
315 SCPrint(TRUE, f, CFSTR(", name = %@"), name);
316 }
317 }
318 }
319
320 if (thisSession->serverPort != NULL) {
321 SCPrint(TRUE, f, CFSTR("\n\t\t%@"), thisSession->serverPort);
322 }
323
324 if (thisSession->serverRunLoopSource != NULL) {
325 SCPrint(TRUE, f, CFSTR("\n\t\t%@"), thisSession->serverRunLoopSource);
326 }
327
328 SCPrint(TRUE, f, CFSTR("\n"));
329 }
330
331 SCPrint(TRUE, f, CFSTR("\n"));
332 return;
333 }
334
335
336 #if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
337
338 #include <Security/Security.h>
339 #include <Security/SecTask.h>
340
341 static CFStringRef
342 sessionName(serverSessionRef session)
343 {
344 CFDictionaryRef info;
345 CFStringRef name = NULL;
346 CFStringRef sessionKey;
347
348 sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), session->key);
349 info = CFDictionaryGetValue(sessionData, sessionKey);
350 CFRelease(sessionKey);
351
352 if (info != NULL) {
353 name = CFDictionaryGetValue(info, kSCDName);
354 }
355
356 return (name != NULL) ? name : CFSTR("???");
357 }
358
359
360 static Boolean
361 hasEntitlement(serverSessionRef session, CFStringRef entitlement)
362 {
363 Boolean hasEntitlement = FALSE;
364 SecTaskRef task;
365
366 /* Create the security task from the audit token. */
367 task = SecTaskCreateWithAuditToken(NULL, session->auditToken);
368 if (task != NULL) {
369 CFErrorRef error = NULL;
370 CFTypeRef value;
371
372 /* Get the value for the entitlement. */
373 value = SecTaskCopyValueForEntitlement(task, entitlement, &error);
374 if (value != NULL) {
375 if (isA_CFBoolean(value)) {
376 if (CFBooleanGetValue(value)) {
377 /* if client DOES have entitlement */
378 hasEntitlement = TRUE;
379 }
380 } else {
381 SCLog(TRUE, LOG_ERR,
382 CFSTR("hasEntitlement: entitlement not valid: %@"),
383 sessionName(session));
384 }
385
386 CFRelease(value);
387 }
388 if (error != NULL) {
389 SCLog(TRUE,
390 (value == NULL) ? LOG_ERR : LOG_DEBUG,
391 CFSTR("hasEntitlement SecTaskCopyValueForEntitlement() %s, error domain=%@, error code=%lx"),
392 (value == NULL) ? "failed" : "warned",
393 CFErrorGetDomain(error),
394 CFErrorGetCode(error));
395 CFRelease(error);
396 }
397
398 CFRelease(task);
399 } else {
400 SCLog(TRUE, LOG_ERR,
401 CFSTR("hasEntitlement SecTaskCreateWithAuditToken() failed: %@"),
402 sessionName(session));
403 }
404
405 return hasEntitlement;
406 }
407
408 #endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
409
410
411 __private_extern__
412 Boolean
413 hasRootAccess(serverSessionRef session)
414 {
415 if (session->callerRootAccess == UNKNOWN) {
416 /*
417 * get the credentials associated with the caller.
418 */
419 audit_token_to_au32(session->auditToken,
420 NULL, // auidp
421 &session->callerEUID, // euid
422 NULL, // egid
423 NULL, // ruid
424 NULL, // rgid
425 NULL, // pid
426 NULL, // asid
427 NULL); // tid
428
429 session->callerRootAccess = (session->callerEUID == 0) ? YES : NO;
430 }
431
432 return (session->callerRootAccess == YES) ? TRUE : FALSE;
433 }
434
435
436 __private_extern__
437 Boolean
438 hasWriteAccess(serverSessionRef session)
439 {
440 if (session->callerWriteAccess == UNKNOWN) {
441 /* assume that the client DOES NOT have the entitlement */
442 session->callerWriteAccess = NO;
443
444 if (hasRootAccess(session)) {
445 // grant write access to eUID==0 processes
446 session->callerWriteAccess = YES;
447 }
448 #if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
449 else if (hasEntitlement(session, kSCWriteEntitlementName)) {
450 // grant write access to "entitled" processes
451 session->callerWriteAccess = YES;
452 }
453 #endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
454 }
455
456 return (session->callerWriteAccess == YES) ? TRUE : FALSE;
457 }
458
459
460 __private_extern__
461 Boolean
462 hasPathAccess(serverSessionRef session, const char *path)
463 {
464 pid_t pid;
465 char realPath[PATH_MAX];
466
467 if (realpath(path, realPath) == NULL) {
468 SCLog(TRUE, LOG_DEBUG, CFSTR("hasPathAccess realpath() failed: %s"), strerror(errno));
469 return FALSE;
470 }
471
472 audit_token_to_au32(session->auditToken,
473 NULL, // auidp
474 NULL, // euid
475 NULL, // egid
476 NULL, // ruid
477 NULL, // rgid
478 &pid, // pid
479 NULL, // asid
480 NULL); // tid
481
482 if (sandbox_check(pid, "file-write-data", SANDBOX_FILTER_PATH, realPath) > 0) {
483 SCLog(TRUE, LOG_DEBUG, CFSTR("hasPathAccess sandbox access denied: %s"), strerror(errno));
484 return FALSE;
485 }
486
487 return TRUE;
488 }