]> git.saurik.com Git - apple/configd.git/blob - configd.tproj/session.c
cbb5b961c77a2df59e162b03a9934df8fbbff7e1
[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; /* # of allocated sessions */
46 static int lastSession = -1; /* # of last used session */
47
48 /* CFMachPortInvalidation runloop */
49 static CFRunLoopRef sessionRunLoop = NULL;
50
51 /* temp session */
52 static serverSessionRef temp_session = NULL;
53
54
55 static void
56 CFMachPortInvalidateSessionCallback(CFMachPortRef port, void *info)
57 {
58 CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent();
59
60 // Bear trap
61 if (!_SC_CFEqual(currentRunLoop, sessionRunLoop)) {
62 _SC_crash("SCDynamicStore CFMachPort invalidation error",
63 CFSTR("CFMachPort invalidated"),
64 CFSTR("An SCDynamicStore CFMachPort has incorrectly been invalidated."));
65 }
66 }
67
68
69 __private_extern__
70 serverSessionRef
71 getSession(mach_port_t server)
72 {
73 int i;
74
75 if (server == MACH_PORT_NULL) {
76 SCLog(TRUE, LOG_ERR, CFSTR("Excuse me, why is getSession() being called with an invalid port?"));
77 return NULL;
78 }
79
80 /* look for matching session (note: slot 0 is the "server" port) */
81 for (i = 1; i <= lastSession; i++) {
82 serverSessionRef thisSession = sessions[i];
83
84 if (thisSession == NULL) {
85 /* found an empty slot, skip it */
86 continue;
87 }
88
89 if (thisSession->key == server) {
90 /* we've seen this server before */
91 return thisSession;
92 }
93
94 if ((thisSession->store != NULL) &&
95 (((SCDynamicStorePrivateRef)thisSession->store)->notifySignalTask == server)) {
96 /* we've seen this task port before */
97 return thisSession;
98 }
99 }
100
101 /* no sessions available */
102 return NULL;
103 }
104
105
106 __private_extern__
107 serverSessionRef
108 tempSession(mach_port_t server, CFStringRef name, audit_token_t auditToken)
109 {
110 static dispatch_once_t once;
111 SCDynamicStorePrivateRef storePrivate;
112
113 if (sessions[0]->key != server) {
114 // if not SCDynamicStore "server" port
115 return NULL;
116 }
117
118 dispatch_once(&once, ^{
119 temp_session = sessions[0]; /* use "server" session */
120 (void) __SCDynamicStoreOpen(&temp_session->store, NULL);
121 });
122
123 /* save audit token */
124 temp_session->auditToken = auditToken;
125 temp_session->callerEUID = -1; /* not "root" */
126 temp_session->callerRootAccess = UNKNOWN;
127 temp_session->callerWriteAccess = UNKNOWN;
128
129 /* save name */
130 storePrivate = (SCDynamicStorePrivateRef)temp_session->store;
131 if (storePrivate->name != NULL) CFRelease(storePrivate->name);
132 storePrivate->name = CFRetain(name);
133
134 return temp_session;
135 }
136
137
138 __private_extern__
139 serverSessionRef
140 addSession(mach_port_t server, CFStringRef (*copyDescription)(const void *info))
141 {
142 CFMachPortContext context = { 0, NULL, NULL, NULL, NULL };
143 mach_port_t mp = server;
144 int n = -1;
145
146 /* save current (SCDynamicStore) runloop */
147 if (sessionRunLoop == NULL) {
148 sessionRunLoop = CFRunLoopGetCurrent();
149 }
150
151 if (nSessions <= 0) {
152 /* if first session (the "server" port) */
153 n = 0; /* use slot "0" */
154 lastSession = 0; /* last used slot */
155
156 nSessions = 64;
157 sessions = malloc(nSessions * sizeof(serverSessionRef));
158 } else {
159 int i;
160
161 /* check to see if we already have an open session (note: slot 0 is the "server" port) */
162 for (i = 1; i <= lastSession; i++) {
163 serverSessionRef thisSession = sessions[i];
164
165 if (thisSession == NULL) {
166 /* found an empty slot */
167 if (n < 0) {
168 /* keep track of the first [empty] slot */
169 n = i;
170 }
171
172 /* and keep looking for a matching session */
173 continue;
174 }
175
176 if (thisSession->key == server) {
177 /* we've seen this server before */
178 return NULL;
179 }
180
181 if ((thisSession->store != NULL) &&
182 (((SCDynamicStorePrivateRef)thisSession->store)->notifySignalTask == server)) {
183 /* we've seen this task port before */
184 return NULL;
185 }
186 }
187
188 /* add a new session */
189 if (n < 0) {
190 /* if no empty slots */
191 n = ++lastSession;
192 if (lastSession >= nSessions) {
193 /* expand the session list */
194 nSessions *= 2;
195 sessions = reallocf(sessions, (nSessions * sizeof(serverSessionRef)));
196 }
197 }
198
199 // create mach port for SCDynamicStore client
200 mp = MACH_PORT_NULL;
201 (void) mach_port_allocate(mach_task_self(),
202 MACH_PORT_RIGHT_RECEIVE,
203 &mp);
204 }
205
206 // allocate a new session for this server
207 sessions[n] = malloc(sizeof(serverSession));
208 bzero(sessions[n], sizeof(serverSession));
209
210 // create server port
211 context.info = sessions[n];
212 context.copyDescription = copyDescription;
213
214 //
215 // Note: we create the CFMachPort *before* we insert a send
216 // right present to ensure that CF does not establish
217 // its dead name notification.
218 //
219 sessions[n]->serverPort = _SC_CFMachPortCreateWithPort("SCDynamicStore/session",
220 mp,
221 configdCallback,
222 &context);
223
224 //
225 // Set bear trap (an invalidation callback) to catch other
226 // threads stomping on us
227 //
228 CFMachPortSetInvalidationCallBack(sessions[n]->serverPort,
229 CFMachPortInvalidateSessionCallback);
230
231 if (n > 0) {
232 // insert send right that will be moved to the client
233 (void) mach_port_insert_right(mach_task_self(),
234 mp,
235 mp,
236 MACH_MSG_TYPE_MAKE_SEND);
237 }
238
239 sessions[n]->key = mp;
240 // sessions[n]->serverRunLoopSource = NULL;
241 // sessions[n]->store = NULL;
242 sessions[n]->callerEUID = 1; /* not "root" */
243 sessions[n]->callerRootAccess = UNKNOWN;
244 sessions[n]->callerWriteAccess = UNKNOWN;
245
246 return sessions[n];
247 }
248
249
250 __private_extern__
251 void
252 cleanupSession(mach_port_t server)
253 {
254 int i;
255
256 for (i = 1; i <= lastSession; i++) {
257 CFStringRef sessionKey;
258 serverSessionRef thisSession = sessions[i];
259
260 if (thisSession == NULL) {
261 /* found an empty slot, skip it */
262 continue;
263 }
264
265 if (thisSession->key == server) {
266 /*
267 * session entry still exists.
268 */
269
270 if (_configd_trace) {
271 SCTrace(TRUE, _configd_trace, CFSTR("cleanup : %5d\n"), server);
272 }
273
274 /*
275 * Close any open connections including cancelling any outstanding
276 * notification requests and releasing any locks.
277 */
278 __MACH_PORT_DEBUG(TRUE, "*** cleanupSession", server);
279 (void) __SCDynamicStoreClose(&thisSession->store);
280 __MACH_PORT_DEBUG(TRUE, "*** cleanupSession (after __SCDynamicStoreClose)", server);
281
282 /*
283 * Our send right has already been removed. Remove our receive right.
284 */
285 (void) mach_port_mod_refs(mach_task_self(), server, MACH_PORT_RIGHT_RECEIVE, -1);
286
287 /*
288 * We don't need any remaining information in the
289 * sessionData dictionary, remove it.
290 */
291 sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), server);
292 CFDictionaryRemoveValue(sessionData, sessionKey);
293 CFRelease(sessionKey);
294
295 /*
296 * get rid of the per-session structure.
297 */
298 free(thisSession);
299 sessions[i] = NULL;
300
301 if (i == lastSession) {
302 /* we are removing the last session, update last used slot */
303 while (--lastSession > 0) {
304 if (sessions[lastSession] != NULL) {
305 break;
306 }
307 }
308 }
309
310 return;
311 }
312 }
313
314 SCLog(TRUE, LOG_ERR, CFSTR("MACH_NOTIFY_NO_SENDERS w/no session, port = %d"), server);
315 __MACH_PORT_DEBUG(TRUE, "*** cleanupSession w/no session", server);
316 return;
317 }
318
319
320 __private_extern__
321 void
322 listSessions(FILE *f)
323 {
324 int i;
325
326 SCPrint(TRUE, f, CFSTR("Current sessions :\n"));
327 for (i = 0; i <= lastSession; i++) {
328 serverSessionRef thisSession = sessions[i];
329
330 if (thisSession == NULL) {
331 continue;
332 }
333
334 SCPrint(TRUE, f, CFSTR("\t%d : port = 0x%x"), i, thisSession->key);
335
336 if (thisSession->store != NULL) {
337 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)thisSession->store;
338
339 if (storePrivate->notifySignalTask != TASK_NULL) {
340 SCPrint(TRUE, f, CFSTR(", task = %d"), storePrivate->notifySignalTask);
341 }
342 }
343
344 if (sessionData != NULL) {
345 CFDictionaryRef info;
346 CFStringRef key;
347
348 key = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), thisSession->key);
349 info = CFDictionaryGetValue(sessionData, key);
350 CFRelease(key);
351 if (info != NULL) {
352 CFStringRef name;
353
354 name = CFDictionaryGetValue(info, kSCDName);
355 if (name != NULL) {
356 SCPrint(TRUE, f, CFSTR(", name = %@"), name);
357 }
358 }
359 }
360
361 if (thisSession->serverPort != NULL) {
362 SCPrint(TRUE, f, CFSTR("\n\t\t%@"), thisSession->serverPort);
363 }
364
365 if (thisSession->serverRunLoopSource != NULL) {
366 SCPrint(TRUE, f, CFSTR("\n\t\t%@"), thisSession->serverRunLoopSource);
367 }
368
369 SCPrint(TRUE, f, CFSTR("\n"));
370 }
371
372 SCPrint(TRUE, f, CFSTR("\n"));
373 return;
374 }
375
376
377 #if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
378
379 #include <Security/Security.h>
380 #include <Security/SecTask.h>
381
382 static CFStringRef
383 sessionName(serverSessionRef session)
384 {
385 CFDictionaryRef info;
386 CFStringRef name = NULL;
387 CFStringRef sessionKey;
388
389 sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), session->key);
390 info = CFDictionaryGetValue(sessionData, sessionKey);
391 CFRelease(sessionKey);
392
393 if (info != NULL) {
394 name = CFDictionaryGetValue(info, kSCDName);
395 }
396
397 return (name != NULL) ? name : CFSTR("???");
398 }
399
400
401 static Boolean
402 hasEntitlement(serverSessionRef session, CFStringRef entitlement)
403 {
404 Boolean hasEntitlement = FALSE;
405 SecTaskRef task;
406
407 /* Create the security task from the audit token. */
408 task = SecTaskCreateWithAuditToken(NULL, session->auditToken);
409 if (task != NULL) {
410 CFErrorRef error = NULL;
411 CFTypeRef value;
412
413 /* Get the value for the entitlement. */
414 value = SecTaskCopyValueForEntitlement(task, entitlement, &error);
415 if (value != NULL) {
416 if (isA_CFBoolean(value)) {
417 if (CFBooleanGetValue(value)) {
418 /* if client DOES have entitlement */
419 hasEntitlement = TRUE;
420 }
421 } else {
422 SCLog(TRUE, LOG_ERR,
423 CFSTR("hasEntitlement: entitlement not valid: %@"),
424 sessionName(session));
425 }
426
427 CFRelease(value);
428 }
429 if (error != NULL) {
430 SCLog(TRUE,
431 (value == NULL) ? LOG_ERR : LOG_DEBUG,
432 CFSTR("hasEntitlement SecTaskCopyValueForEntitlement() %s, error domain=%@, error code=%lx"),
433 (value == NULL) ? "failed" : "warned",
434 CFErrorGetDomain(error),
435 CFErrorGetCode(error));
436 CFRelease(error);
437 }
438
439 CFRelease(task);
440 } else {
441 SCLog(TRUE, LOG_ERR,
442 CFSTR("hasEntitlement SecTaskCreateWithAuditToken() failed: %@"),
443 sessionName(session));
444 }
445
446 return hasEntitlement;
447 }
448
449 #endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
450
451
452 __private_extern__
453 Boolean
454 hasRootAccess(serverSessionRef session)
455 {
456 if (session->callerRootAccess == UNKNOWN) {
457 /*
458 * get the credentials associated with the caller.
459 */
460 audit_token_to_au32(session->auditToken,
461 NULL, // auidp
462 &session->callerEUID, // euid
463 NULL, // egid
464 NULL, // ruid
465 NULL, // rgid
466 NULL, // pid
467 NULL, // asid
468 NULL); // tid
469
470 session->callerRootAccess = (session->callerEUID == 0) ? YES : NO;
471 }
472
473 return (session->callerRootAccess == YES) ? TRUE : FALSE;
474 }
475
476
477 __private_extern__
478 Boolean
479 hasWriteAccess(serverSessionRef session)
480 {
481 if (session->callerWriteAccess == UNKNOWN) {
482 /* assume that the client DOES NOT have the entitlement */
483 session->callerWriteAccess = NO;
484
485 if (hasRootAccess(session)) {
486 // grant write access to eUID==0 processes
487 session->callerWriteAccess = YES;
488 }
489 #if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
490 else if (hasEntitlement(session, kSCWriteEntitlementName)) {
491 // grant write access to "entitled" processes
492 session->callerWriteAccess = YES;
493 }
494 #endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
495 }
496
497 return (session->callerWriteAccess == YES) ? TRUE : FALSE;
498 }
499
500
501 __private_extern__
502 Boolean
503 hasPathAccess(serverSessionRef session, const char *path)
504 {
505 pid_t pid;
506 char realPath[PATH_MAX];
507
508 if (realpath(path, realPath) == NULL) {
509 SCLog(TRUE, LOG_DEBUG, CFSTR("hasPathAccess realpath() failed: %s"), strerror(errno));
510 return FALSE;
511 }
512
513 audit_token_to_au32(session->auditToken,
514 NULL, // auidp
515 NULL, // euid
516 NULL, // egid
517 NULL, // ruid
518 NULL, // rgid
519 &pid, // pid
520 NULL, // asid
521 NULL); // tid
522
523 if (sandbox_check(pid, "file-write-data", SANDBOX_FILTER_PATH, realPath) > 0) {
524 SCLog(TRUE, LOG_DEBUG, CFSTR("hasPathAccess sandbox access denied: %s"), strerror(errno));
525 return FALSE;
526 }
527
528 return TRUE;
529 }