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