]> git.saurik.com Git - apple/configd.git/blob - configd.tproj/session.c
configd-596.15.tar.gz
[apple/configd.git] / configd.tproj / session.c
1 /*
2 * Copyright (c) 2000, 2001, 2003-2005, 2007-2013 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 <SystemConfiguration/SystemConfiguration.h>
35 #include "configd.h"
36 #include "configd_server.h"
37 #include "pattern.h"
38 #include "session.h"
39
40 #include <unistd.h>
41 #include <bsm/libbsm.h>
42 #include <sandbox.h>
43
44 #if !TARGET_IPHONE_SIMULATOR || (defined(IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED) && (IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED >= 1090))
45 #define HAVE_MACHPORT_GUARDS
46 #endif
47
48
49 /* information maintained for each active session */
50 static serverSessionRef *sessions = NULL;
51 static int nSessions = 0; /* # of allocated sessions */
52 static int lastSession = -1; /* # of last used session */
53
54 /* CFMachPortInvalidation runloop */
55 static CFRunLoopRef sessionRunLoop = NULL;
56
57 /* temp session */
58 static serverSessionRef temp_session = NULL;
59
60
61 __private_extern__
62 serverSessionRef
63 getSession(mach_port_t server)
64 {
65 int i;
66
67 if (server == MACH_PORT_NULL) {
68 SCLog(TRUE, LOG_ERR, CFSTR("Excuse me, why is getSession() being called with an invalid port?"));
69 return NULL;
70 }
71
72 /* look for matching session (note: slot 0 is the "server" port) */
73 for (i = 1; i <= lastSession; i++) {
74 serverSessionRef thisSession = sessions[i];
75
76 if (thisSession == NULL) {
77 /* found an empty slot, skip it */
78 continue;
79 }
80
81 if (thisSession->key == server) {
82 /* we've seen this server before */
83 return thisSession;
84 }
85
86 if ((thisSession->store != NULL) &&
87 (((SCDynamicStorePrivateRef)thisSession->store)->notifySignalTask == server)) {
88 /* we've seen this task port before */
89 return thisSession;
90 }
91 }
92
93 /* no sessions available */
94 return NULL;
95 }
96
97
98 __private_extern__
99 serverSessionRef
100 tempSession(mach_port_t server, CFStringRef name, audit_token_t auditToken)
101 {
102 static dispatch_once_t once;
103 SCDynamicStorePrivateRef storePrivate;
104
105 if (sessions[0]->key != server) {
106 // if not SCDynamicStore "server" port
107 return NULL;
108 }
109
110 dispatch_once(&once, ^{
111 temp_session = sessions[0]; /* use "server" session */
112 (void) __SCDynamicStoreOpen(&temp_session->store, NULL);
113 });
114
115 /* save audit token, caller entitlements */
116 temp_session->auditToken = auditToken;
117 temp_session->callerEUID = 1; /* not "root" */
118 temp_session->callerRootAccess = UNKNOWN;
119 #if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080/*FIXME*/)
120 if ((temp_session->callerWriteEntitlement != NULL) &&
121 (temp_session->callerWriteEntitlement != kCFNull)) {
122 CFRelease(temp_session->callerWriteEntitlement);
123 }
124 temp_session->callerWriteEntitlement = kCFNull; /* UNKNOWN */
125 #endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080/*FIXME*/)
126
127 /* save name */
128 storePrivate = (SCDynamicStorePrivateRef)temp_session->store;
129 if (storePrivate->name != NULL) CFRelease(storePrivate->name);
130 storePrivate->name = CFRetain(name);
131
132 return temp_session;
133 }
134
135
136 __private_extern__
137 serverSessionRef
138 addSession(mach_port_t server, CFStringRef (*copyDescription)(const void *info))
139 {
140 CFMachPortContext context = { 0, NULL, NULL, NULL, NULL };
141 kern_return_t kr;
142 mach_port_t mp = server;
143 int n = -1;
144 serverSessionRef newSession = NULL;
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
159 // allocate a new session for "the" server
160 newSession = calloc(1, sizeof(serverSession));
161 } else {
162 int i;
163 #ifdef HAVE_MACHPORT_GUARDS
164 mach_port_options_t opts;
165 #endif // HAVE_MACHPORT_GUARDS
166
167 /* check to see if we already have an open session (note: slot 0 is the "server" port) */
168 for (i = 1; i <= lastSession; i++) {
169 serverSessionRef thisSession = sessions[i];
170
171 if (thisSession == NULL) {
172 /* found an empty slot */
173 if (n < 0) {
174 /* keep track of the first [empty] slot */
175 n = i;
176 }
177
178 /* and keep looking for a matching session */
179 continue;
180 }
181
182 if (thisSession->key == server) {
183 /* we've seen this server before */
184 return NULL;
185 }
186
187 if ((thisSession->store != NULL) &&
188 (((SCDynamicStorePrivateRef)thisSession->store)->notifySignalTask == server)) {
189 /* we've seen this task port before */
190 return NULL;
191 }
192 }
193
194 /* add a new session */
195 if (n < 0) {
196 /* if no empty slots */
197 n = ++lastSession;
198 if (lastSession >= nSessions) {
199 /* expand the session list */
200 nSessions *= 2;
201 sessions = reallocf(sessions, (nSessions * sizeof(serverSessionRef)));
202 }
203 }
204
205 // allocate a session for this client
206 newSession = calloc(1, sizeof(serverSession));
207
208 // create mach port for SCDynamicStore client
209 mp = MACH_PORT_NULL;
210
211 retry_allocate :
212
213 #ifdef HAVE_MACHPORT_GUARDS
214 bzero(&opts, sizeof(opts));
215 opts.flags = MPO_CONTEXT_AS_GUARD;
216
217 kr = mach_port_construct(mach_task_self(), &opts, newSession, &mp);
218 #else // HAVE_MACHPORT_GUARDS
219 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
220 #endif // HAVE_MACHPORT_GUARDS
221
222 if (kr != KERN_SUCCESS) {
223 char *err = NULL;
224
225 SCLog(TRUE, LOG_ERR, CFSTR("addSession: could not allocate mach port: %s"), mach_error_string(kr));
226 if ((kr == KERN_NO_SPACE) || (kr == KERN_RESOURCE_SHORTAGE)) {
227 sleep(1);
228 goto retry_allocate;
229 }
230
231 (void) asprintf(&err, "addSession: could not allocate mach port: %s", mach_error_string(kr));
232 _SC_crash(err != NULL ? err : "addSession: could not allocate mach port",
233 NULL,
234 NULL);
235 if (err != NULL) free(err);
236
237 free(newSession);
238 return NULL;
239 }
240 }
241
242 // create server port
243 context.info = newSession;
244 context.copyDescription = copyDescription;
245
246 //
247 // Note: we create the CFMachPort *before* we insert a send
248 // right present to ensure that CF does not establish
249 // its dead name notification.
250 //
251 newSession->serverPort = _SC_CFMachPortCreateWithPort("SCDynamicStore/session",
252 mp,
253 configdCallback,
254 &context);
255
256 if (n > 0) {
257 // insert send right that will be moved to the client
258 kr = mach_port_insert_right(mach_task_self(),
259 mp,
260 mp,
261 MACH_MSG_TYPE_MAKE_SEND);
262 if (kr != KERN_SUCCESS) {
263 /*
264 * We can't insert a send right into our own port! This should
265 * only happen if someone stomped on OUR port (so let's leave
266 * the port alone).
267 */
268 SCLog(TRUE, LOG_ERR, CFSTR("addSession mach_port_insert_right(): %s"), mach_error_string(kr));
269
270 free(newSession);
271 return NULL;
272 }
273 }
274
275 sessions[n] = newSession;
276 sessions[n]->key = mp;
277 // sessions[n]->serverRunLoopSource = NULL;
278 // sessions[n]->store = NULL;
279 sessions[n]->callerEUID = 1; /* not "root" */
280 sessions[n]->callerRootAccess = UNKNOWN;
281 #if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080/*FIXME*/)
282 sessions[n]->callerWriteEntitlement = kCFNull; /* UNKNOWN */
283 #endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080/*FIXME*/)
284
285 return newSession;
286 }
287
288
289 __private_extern__
290 void
291 cleanupSession(mach_port_t server)
292 {
293 int i;
294
295 for (i = 1; i <= lastSession; i++) {
296 CFStringRef sessionKey;
297 serverSessionRef thisSession = sessions[i];
298
299 if (thisSession == NULL) {
300 /* found an empty slot, skip it */
301 continue;
302 }
303
304 if (thisSession->key == server) {
305 /*
306 * session entry still exists.
307 */
308
309 if (_configd_trace) {
310 SCTrace(TRUE, _configd_trace, CFSTR("cleanup : %5d\n"), server);
311 }
312
313 /*
314 * Close any open connections including cancelling any outstanding
315 * notification requests and releasing any locks.
316 */
317 __MACH_PORT_DEBUG(TRUE, "*** cleanupSession", server);
318 (void) __SCDynamicStoreClose(&thisSession->store);
319 __MACH_PORT_DEBUG(TRUE, "*** cleanupSession (after __SCDynamicStoreClose)", server);
320
321 /*
322 * Our send right has already been removed. Remove our receive right.
323 */
324 #ifdef HAVE_MACHPORT_GUARDS
325 (void) mach_port_destruct(mach_task_self(), server, 0, thisSession);
326 #else // HAVE_MACHPORT_GUARDS
327 (void) mach_port_mod_refs(mach_task_self(), server, MACH_PORT_RIGHT_RECEIVE, -1);
328 #endif // HAVE_MACHPORT_GUARDS
329
330 #if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080/*FIXME*/)
331 /*
332 * release any entitlement info
333 */
334 if ((thisSession->callerWriteEntitlement != NULL) &&
335 (thisSession->callerWriteEntitlement != kCFNull)) {
336 CFRelease(thisSession->callerWriteEntitlement);
337 }
338 #endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080/*FIXME*/)
339
340 /*
341 * We don't need any remaining information in the
342 * sessionData dictionary, remove it.
343 */
344 sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), server);
345 CFDictionaryRemoveValue(sessionData, sessionKey);
346 CFRelease(sessionKey);
347
348 /*
349 * get rid of the per-session structure.
350 */
351 free(thisSession);
352 sessions[i] = NULL;
353
354 if (i == lastSession) {
355 /* we are removing the last session, update last used slot */
356 while (--lastSession > 0) {
357 if (sessions[lastSession] != NULL) {
358 break;
359 }
360 }
361 }
362
363 return;
364 }
365 }
366
367 SCLog(TRUE, LOG_ERR, CFSTR("MACH_NOTIFY_NO_SENDERS w/no session, port = %d"), server);
368 __MACH_PORT_DEBUG(TRUE, "*** cleanupSession w/no session", server);
369 return;
370 }
371
372
373 __private_extern__
374 void
375 listSessions(FILE *f)
376 {
377 int i;
378
379 SCPrint(TRUE, f, CFSTR("Current sessions :\n"));
380 for (i = 0; i <= lastSession; i++) {
381 serverSessionRef thisSession = sessions[i];
382
383 if (thisSession == NULL) {
384 continue;
385 }
386
387 SCPrint(TRUE, f, CFSTR("\t%d : port = 0x%x"), i, thisSession->key);
388
389 if (thisSession->store != NULL) {
390 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)thisSession->store;
391
392 if (storePrivate->notifySignalTask != TASK_NULL) {
393 SCPrint(TRUE, f, CFSTR(", task = %d"), storePrivate->notifySignalTask);
394 }
395 }
396
397 if (sessionData != NULL) {
398 CFDictionaryRef info;
399 CFStringRef key;
400
401 key = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), thisSession->key);
402 info = CFDictionaryGetValue(sessionData, key);
403 CFRelease(key);
404 if (info != NULL) {
405 CFStringRef name;
406
407 name = CFDictionaryGetValue(info, kSCDName);
408 if (name != NULL) {
409 SCPrint(TRUE, f, CFSTR(", name = %@"), name);
410 }
411 }
412 }
413
414 if (thisSession->serverPort != NULL) {
415 SCPrint(TRUE, f, CFSTR("\n\t\t%@"), thisSession->serverPort);
416 }
417
418 if (thisSession->serverRunLoopSource != NULL) {
419 SCPrint(TRUE, f, CFSTR("\n\t\t%@"), thisSession->serverRunLoopSource);
420 }
421
422 SCPrint(TRUE, f, CFSTR("\n"));
423 }
424
425 SCPrint(TRUE, f, CFSTR("\n"));
426 return;
427 }
428
429
430 #if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080/*FIXME*/)
431
432 #include <Security/Security.h>
433 #include <Security/SecTask.h>
434
435 static CFStringRef
436 sessionName(serverSessionRef session)
437 {
438 CFDictionaryRef info;
439 CFStringRef name = NULL;
440 CFStringRef sessionKey;
441
442 sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), session->key);
443 info = CFDictionaryGetValue(sessionData, sessionKey);
444 CFRelease(sessionKey);
445
446 if (info != NULL) {
447 name = CFDictionaryGetValue(info, kSCDName);
448 }
449
450 return (name != NULL) ? name : CFSTR("???");
451 }
452
453 static CFTypeRef
454 copyEntitlement(serverSessionRef session, CFStringRef entitlement)
455 {
456 SecTaskRef task;
457 CFTypeRef value = NULL;
458
459 // Create the security task from the audit token
460 task = SecTaskCreateWithAuditToken(NULL, session->auditToken);
461 if (task != NULL) {
462 CFErrorRef error = NULL;
463
464 // Get the value for the entitlement
465 value = SecTaskCopyValueForEntitlement(task, entitlement, &error);
466 if ((value == NULL) && (error != NULL)) {
467 CFIndex code = CFErrorGetCode(error);
468 CFStringRef domain = CFErrorGetDomain(error);
469
470 if (!CFEqual(domain, kCFErrorDomainMach) ||
471 ((code != kIOReturnInvalid) && (code != kIOReturnNotFound))) {
472 // if unexpected error
473 SCLog(TRUE, LOG_ERR,
474 CFSTR("SecTaskCopyValueForEntitlement(,\"%@\",) failed, error = %@ : %@"),
475 entitlement,
476 error,
477 sessionName(session));
478 }
479 CFRelease(error);
480 }
481
482 CFRelease(task);
483 } else {
484 SCLog(TRUE, LOG_ERR,
485 CFSTR("SecTaskCreateWithAuditToken() failed: %@"),
486 sessionName(session));
487 }
488
489 return value;
490 }
491
492 #endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080/*FIXME*/)
493
494
495 static pid_t
496 sessionPid(serverSessionRef session)
497 {
498 pid_t caller_pid;
499
500 #if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
501 caller_pid = audit_token_to_pid(session->auditToken);
502 #else // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
503 audit_token_to_au32(session->auditToken,
504 NULL, // auidp
505 NULL, // euid
506 NULL, // egid
507 NULL, // ruid
508 NULL, // rgid
509 &caller_pid, // pid
510 NULL, // asid
511 NULL); // tid
512 #endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
513
514 return caller_pid;
515 }
516
517
518 __private_extern__
519 Boolean
520 hasRootAccess(serverSessionRef session)
521 {
522 #if !TARGET_IPHONE_SIMULATOR
523
524 if (session->callerRootAccess == UNKNOWN) {
525 #if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
526 session->callerEUID = audit_token_to_euid(session->auditToken);
527 #else // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
528 audit_token_to_au32(session->auditToken,
529 NULL, // auidp
530 &session->callerEUID, // euid
531 NULL, // egid
532 NULL, // ruid
533 NULL, // rgid
534 NULL, // pid
535 NULL, // asid
536 NULL); // tid
537 #endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
538 session->callerRootAccess = (session->callerEUID == 0) ? YES : NO;
539 }
540
541 return (session->callerRootAccess == YES) ? TRUE : FALSE;
542
543 #else // !TARGET_IPHONE_SIMULATOR
544
545 /*
546 * assume that all processes interacting with
547 * the iOS Simulator "configd" are OK.
548 */
549 return TRUE;
550
551 #endif // !TARGET_IPHONE_SIMULATOR
552 }
553
554
555 __private_extern__
556 Boolean
557 hasWriteAccess(serverSessionRef session, CFStringRef key)
558 {
559 Boolean isSetup;
560
561 // need to special case writing "Setup:" keys
562 isSetup = CFStringHasPrefix(key, kSCDynamicStoreDomainSetup);
563
564 if (hasRootAccess(session)) {
565 pid_t pid;
566
567 // grant write access to eUID==0 processes
568
569 pid = sessionPid(session);
570 if (isSetup && (pid != getpid())) {
571 /*
572 * WAIT!!!
573 *
574 * This is NOT configd (or a plugin) trying to
575 * write to an SCDynamicStore "Setup:" key. In
576 * general, this is unwise and we should at the
577 * very least complain.
578 */
579 SCLog(TRUE, LOG_ERR,
580 CFSTR("*** Non-configd process (pid=%d) attempting to modify \"%@\" ***"),
581 pid,
582 key);
583 }
584
585 return TRUE;
586 }
587
588 if (isSetup) {
589 /*
590 * STOP!!!
591 *
592 * This is a non-root process trying to write to
593 * an SCDynamicStore "Setup:" key. This is not
594 * something we should ever allow (regardless of
595 * any entitlements).
596 */
597 SCLog(TRUE, LOG_ERR,
598 CFSTR("*** Non-root process (pid=%d) attempting to modify \"%@\" ***"),
599 sessionPid(session),
600 key);
601
602 //return FALSE; // return FALSE when rdar://9811832 has beed fixed
603 }
604
605 #if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080/*FIXME*/)
606 if (session->callerWriteEntitlement == kCFNull) {
607 session->callerWriteEntitlement = copyEntitlement(session,
608 kSCWriteEntitlementName);
609 }
610
611 if (session->callerWriteEntitlement == NULL) {
612 return FALSE;
613 }
614
615 if (isA_CFBoolean(session->callerWriteEntitlement) &&
616 CFBooleanGetValue(session->callerWriteEntitlement)) {
617 // grant write access to "entitled" processes
618 return TRUE;
619 }
620
621 if (isA_CFDictionary(session->callerWriteEntitlement)) {
622 CFArrayRef keys;
623 CFArrayRef patterns;
624
625 keys = CFDictionaryGetValue(session->callerWriteEntitlement, CFSTR("keys"));
626 if (isA_CFArray(keys)) {
627 if (CFArrayContainsValue(keys,
628 CFRangeMake(0, CFArrayGetCount(keys)),
629 key)) {
630 // if key matches one of the entitlement "keys", grant
631 // write access
632 return TRUE;
633 }
634 }
635
636 patterns = CFDictionaryGetValue(session->callerWriteEntitlement, CFSTR("patterns"));
637 if (isA_CFArray(patterns)) {
638 CFIndex i;
639 CFIndex n = CFArrayGetCount(patterns);
640
641 for (i = 0; i < n; i++) {
642 CFStringRef pattern;
643
644 pattern = CFArrayGetValueAtIndex(patterns, i);
645 if (isA_CFString(pattern)) {
646 if (patternKeyMatches(pattern, key)) {
647 // if key matches one of the entitlement
648 // "patterns", grant write access
649 return TRUE;
650 }
651 }
652 }
653 }
654 }
655 #endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080/*FIXME*/)
656
657 return FALSE;
658 }
659
660
661 __private_extern__
662 Boolean
663 hasPathAccess(serverSessionRef session, const char *path)
664 {
665 pid_t pid;
666 char realPath[PATH_MAX];
667
668 if (realpath(path, realPath) == NULL) {
669 SCLog(TRUE, LOG_DEBUG, CFSTR("hasPathAccess realpath() failed: %s"), strerror(errno));
670 return FALSE;
671 }
672
673 #if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
674 pid = audit_token_to_pid(session->auditToken);
675 #else // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
676 audit_token_to_au32(session->auditToken,
677 NULL, // auidp
678 NULL, // euid
679 NULL, // egid
680 NULL, // ruid
681 NULL, // rgid
682 &pid, // pid
683 NULL, // asid
684 NULL); // tid
685 #endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
686 if (sandbox_check(pid, // pid
687 "file-write-data", // operation
688 SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, // sandbox_filter_type
689 realPath) > 0) { // ...
690 SCLog(TRUE, LOG_DEBUG, CFSTR("hasPathAccess sandbox access denied: %s"), strerror(errno));
691 return FALSE;
692 }
693
694 return TRUE;
695 }