]> git.saurik.com Git - apple/configd.git/blob - configd.tproj/session.c
configd-1061.0.2.tar.gz
[apple/configd.git] / configd.tproj / session.c
1 /*
2 * Copyright (c) 2000, 2001, 2003-2005, 2007-2019 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 <os/state_private.h>
43 #include <sandbox.h>
44
45 #if !TARGET_OS_SIMULATOR || (defined(IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED) && (IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED >= 1090))
46 #define HAVE_MACHPORT_GUARDS
47 #endif
48
49
50 /* information maintained for the main listener */
51 static serverSessionRef server_session = NULL;
52
53 /*
54 * information maintained for each active session
55 * Note: sync w/sessionQueue()
56 */
57 static CFMutableDictionaryRef client_sessions = NULL;
58
59
60 static dispatch_queue_t
61 sessionQueue(void)
62 {
63 static dispatch_once_t once;
64 static dispatch_queue_t q;
65
66 dispatch_once(&once, ^{
67 // allocate mapping between [client] session mach port and session info
68 client_sessions = CFDictionaryCreateMutable(NULL,
69 0,
70 NULL, // use the actual mach_port_t as the key
71 &kCFTypeDictionaryValueCallBacks);
72
73 // and a queue to synchronize access to the mapping
74 q = dispatch_queue_create("SCDynamicStore/sessions", NULL);
75 });
76
77 return q;
78 }
79
80
81 #pragma mark -
82 #pragma mark __serverSession object
83
84 static CFStringRef __serverSessionCopyDescription (CFTypeRef cf);
85 static void __serverSessionDeallocate (CFTypeRef cf);
86
87 static const CFRuntimeClass __serverSessionClass = {
88 0, // version
89 "serverSession", // className
90 NULL, // init
91 NULL, // copy
92 __serverSessionDeallocate, // dealloc
93 NULL, // equal
94 NULL, // hash
95 NULL, // copyFormattingDesc
96 __serverSessionCopyDescription // copyDebugDesc
97 };
98
99 static CFTypeID __serverSessionTypeID = _kCFRuntimeNotATypeID;
100
101
102 static CFStringRef
103 __serverSessionCopyDescription(CFTypeRef cf)
104 {
105 CFAllocatorRef allocator = CFGetAllocator(cf);
106 CFMutableStringRef result;
107 serverSessionRef session = (serverSessionRef)cf;
108
109 result = CFStringCreateMutable(allocator, 0);
110 CFStringAppendFormat(result, NULL, CFSTR("<serverSession %p [%p]> {"), cf, allocator);
111
112 // add client port
113 CFStringAppendFormat(result, NULL, CFSTR("port = 0x%x (%d)"), session->key, session->key);
114
115 // add session info
116 if (session->name != NULL) {
117 CFStringAppendFormat(result, NULL, CFSTR(", name = %@"), session->name);
118 }
119
120 CFStringAppendFormat(result, NULL, CFSTR("}"));
121 return result;
122 }
123
124
125 static void
126 __serverSessionDeallocate(CFTypeRef cf)
127 {
128 #pragma unused(cf)
129 serverSessionRef session = (serverSessionRef)cf;
130
131 if (session->changedKeys != NULL) CFRelease(session->changedKeys);
132 if (session->name != NULL) CFRelease(session->name);
133 if (session->sessionKeys != NULL) CFRelease(session->sessionKeys);
134
135 return;
136 }
137
138
139 static serverSessionRef
140 __serverSessionCreate(CFAllocatorRef allocator, mach_port_t server)
141 {
142 static dispatch_once_t once;
143 serverSessionRef session;
144 uint32_t size;
145
146 // initialize runtime
147 dispatch_once(&once, ^{
148 __serverSessionTypeID = _CFRuntimeRegisterClass(&__serverSessionClass);
149 });
150
151 // allocate session
152 size = sizeof(serverSession) - sizeof(CFRuntimeBase);
153 session = (serverSessionRef)_CFRuntimeCreateInstance(allocator,
154 __serverSessionTypeID,
155 size,
156 NULL);
157 if (session == NULL) {
158 return NULL;
159 }
160
161 // if needed, allocate a mach port for SCDynamicStore client
162 if (server == MACH_PORT_NULL) {
163 kern_return_t kr;
164 mach_port_t mp = MACH_PORT_NULL;
165 #ifdef HAVE_MACHPORT_GUARDS
166 mach_port_options_t opts;
167 #endif // HAVE_MACHPORT_GUARDS
168
169 retry_allocate :
170
171 #ifdef HAVE_MACHPORT_GUARDS
172 memset(&opts, 0, sizeof(opts));
173 opts.flags = MPO_CONTEXT_AS_GUARD;
174
175 kr = mach_port_construct(mach_task_self(), &opts, (mach_port_context_t)session, &mp);
176 #else // HAVE_MACHPORT_GUARDS
177 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
178 #endif // HAVE_MACHPORT_GUARDS
179
180 if (kr != KERN_SUCCESS) {
181 char *err = NULL;
182
183 SC_log(LOG_NOTICE, "could not allocate mach port: %s", mach_error_string(kr));
184 if ((kr == KERN_NO_SPACE) || (kr == KERN_RESOURCE_SHORTAGE)) {
185 sleep(1);
186 goto retry_allocate;
187 }
188
189 (void) asprintf(&err, "Could not allocate mach port: %s", mach_error_string(kr));
190 _SC_crash(err != NULL ? err : "Could not allocate new session (mach) port",
191 NULL,
192 NULL);
193 if (err != NULL) free(err);
194 CFRelease(session);
195 return NULL;
196 }
197
198 // insert send right that will be moved to the client
199 kr = mach_port_insert_right(mach_task_self(),
200 mp,
201 mp,
202 MACH_MSG_TYPE_MAKE_SEND);
203 if (kr != KERN_SUCCESS) {
204 /*
205 * We can't insert a send right into our own port! This should
206 * only happen if someone stomped on OUR port (so let's leave
207 * the port alone).
208 */
209 SC_log(LOG_ERR, "mach_port_insert_right() failed: %s", mach_error_string(kr));
210 CFRelease(session);
211 return NULL;
212 }
213
214 server = mp;
215 }
216
217 session->callerEUID = 1; /* not "root" */
218 session->callerRootAccess = UNKNOWN;
219 session->callerWriteEntitlement = kCFNull; /* UNKNOWN */
220 session->key = server;
221 // session->store = NULL;
222
223 return session;
224 }
225
226
227 #pragma mark -
228 #pragma mark SCDynamicStore state handler
229
230
231 static void
232 addSessionReference(const void *key, const void *value, void *context)
233 {
234 #pragma unused(key)
235 CFMutableDictionaryRef dict = (CFMutableDictionaryRef)context;
236 serverSessionRef session = (serverSessionRef)value;
237
238 if (session->name != NULL) {
239 int cnt;
240 CFNumberRef num;
241
242 if (!CFDictionaryGetValueIfPresent(dict,
243 session->name,
244 (const void **)&num) ||
245 !CFNumberGetValue(num, kCFNumberIntType, &cnt)) {
246 // if first session
247 cnt = 0;
248 }
249 cnt++;
250 num = CFNumberCreate(NULL, kCFNumberIntType, &cnt);
251 CFDictionarySetValue(dict, session->name, num);
252 CFRelease(num);
253 }
254
255 return;
256 }
257
258
259 static void
260 add_state_handler()
261 {
262 os_state_block_t state_block;
263
264 state_block = ^os_state_data_t(os_state_hints_t hints) {
265 #pragma unused(hints)
266 CFDataRef data = NULL;
267 CFIndex n;
268 Boolean ok;
269 os_state_data_t state_data;
270 size_t state_data_size;
271 CFIndex state_len;
272
273 n = CFDictionaryGetCount(client_sessions);
274 if (n < 500) {
275 CFStringRef str;
276
277 str = CFStringCreateWithFormat(NULL, NULL, CFSTR("n = %ld"), n);
278 ok = _SCSerialize(str, &data, NULL, NULL);
279 CFRelease(str);
280 } else {
281 CFMutableDictionaryRef dict;
282
283 dict = CFDictionaryCreateMutable(NULL,
284 0,
285 &kCFTypeDictionaryKeyCallBacks,
286 &kCFTypeDictionaryValueCallBacks);
287 CFDictionaryApplyFunction(client_sessions, addSessionReference, dict);
288 ok = _SCSerialize(dict, &data, NULL, NULL);
289 CFRelease(dict);
290 }
291
292 state_len = (ok && (data != NULL)) ? CFDataGetLength(data) : 0;
293 state_data_size = OS_STATE_DATA_SIZE_NEEDED(state_len);
294 if (state_data_size > MAX_STATEDUMP_SIZE) {
295 SC_log(LOG_ERR, "SCDynamicStore/sessions : state data too large (%zd > %zd)",
296 state_data_size,
297 (size_t)MAX_STATEDUMP_SIZE);
298 if (data != NULL) CFRelease(data);
299 return NULL;
300 }
301
302 state_data = calloc(1, state_data_size);
303 if (state_data == NULL) {
304 SC_log(LOG_ERR, "SCDynamicStore/sessions: could not allocate state data");
305 if (data != NULL) CFRelease(data);
306 return NULL;
307 }
308
309 state_data->osd_type = OS_STATE_DATA_SERIALIZED_NSCF_OBJECT;
310 state_data->osd_data_size = (uint32_t)state_len;
311 strlcpy(state_data->osd_title, "SCDynamicStore/sessions", sizeof(state_data->osd_title));
312 if (state_len > 0) {
313 memcpy(state_data->osd_data, CFDataGetBytePtr(data), state_len);
314 }
315 if (data != NULL) CFRelease(data);
316
317 return state_data;
318 };
319
320 (void) os_state_add_handler(sessionQueue(), state_block);
321 return;
322 }
323
324
325 #pragma mark -
326 #pragma mark SCDynamicStore session management
327
328
329 __private_extern__
330 serverSessionRef
331 getSession(mach_port_t server)
332 {
333 __block serverSessionRef session;
334
335 assert(server != MACH_PORT_NULL);
336 dispatch_sync(sessionQueue(), ^{
337 session = (serverSessionRef)CFDictionaryGetValue(client_sessions,
338 (const void *)(uintptr_t)server);
339 });
340
341 return session;
342 }
343
344
345 __private_extern__
346 serverSessionRef
347 getSessionNum(CFNumberRef serverNum)
348 {
349 union {
350 mach_port_t mp;
351 uint64_t val;
352 } server;
353 serverSessionRef session;
354
355 (void) CFNumberGetValue(serverNum, kCFNumberSInt64Type, &server.val);
356 session = getSession(server.mp);
357
358 return session;
359 }
360
361
362 __private_extern__
363 serverSessionRef
364 getSessionStr(CFStringRef serverKey)
365 {
366 mach_port_t server;
367 serverSessionRef session;
368 char str[16];
369
370 (void) _SC_cfstring_to_cstring(serverKey, str, sizeof(str), kCFStringEncodingASCII);
371 server = atoi(str);
372 session = getSession(server);
373
374 return session;
375 }
376
377
378 __private_extern__
379 serverSessionRef
380 tempSession(mach_port_t server, CFStringRef name, audit_token_t auditToken)
381 {
382 static dispatch_once_t once;
383 SCDynamicStorePrivateRef storePrivate; /* temp session */
384 static serverSession temp_session;
385
386 dispatch_once(&once, ^{
387 temp_session = *server_session; /* use "server" session clone */
388 (void) __SCDynamicStoreOpen(&temp_session.store, NULL);
389 });
390
391 if (temp_session.key != server) {
392 // if not SCDynamicStore "server" port
393 return NULL;
394 }
395
396 /* save audit token, caller entitlements */
397 temp_session.auditToken = auditToken;
398 temp_session.callerEUID = 1; /* not "root" */
399 temp_session.callerRootAccess = UNKNOWN;
400 if ((temp_session.callerWriteEntitlement != NULL) &&
401 (temp_session.callerWriteEntitlement != kCFNull)) {
402 CFRelease(temp_session.callerWriteEntitlement);
403 }
404 temp_session.callerWriteEntitlement = kCFNull; /* UNKNOWN */
405
406 /* save name */
407 storePrivate = (SCDynamicStorePrivateRef)temp_session.store;
408 if (storePrivate->name != NULL) CFRelease(storePrivate->name);
409 storePrivate->name = CFRetain(name);
410
411 return &temp_session;
412 }
413
414
415 __private_extern__
416 void
417 addSession(serverSessionRef session, Boolean isMain)
418 {
419 session->serverChannel = dispatch_mach_create_f("configd/SCDynamicStore",
420 server_queue(),
421 (void *)session,
422 server_mach_channel_handler);
423 if (!isMain) {
424 // if not main SCDynamicStore port, watch for exit
425 dispatch_mach_request_no_senders(session->serverChannel);
426 }
427 #if TARGET_OS_SIMULATOR
428 // simulators don't support MiG QoS propagation yet
429 dispatch_set_qos_class_fallback(session->serverChannel, QOS_CLASS_USER_INITIATED);
430 #else
431 dispatch_set_qos_class_fallback(session->serverChannel, QOS_CLASS_BACKGROUND);
432 #endif
433 dispatch_mach_connect(session->serverChannel, session->key, MACH_PORT_NULL, NULL);
434 return;
435 }
436
437
438 __private_extern__
439 serverSessionRef
440 addClient(mach_port_t server, audit_token_t audit_token)
441 {
442
443 __block serverSessionRef newSession = NULL;
444
445 dispatch_sync(sessionQueue(), ^{
446 Boolean ok;
447
448 // check to see if we already have an open session
449 ok = CFDictionaryContainsKey(client_sessions,
450 (const void *)(uintptr_t)server);
451 if (ok) {
452 // if we've already added a session for this port
453 return;
454 }
455
456 // allocate a new session for "the" server
457 newSession = __serverSessionCreate(NULL, MACH_PORT_NULL);
458 if (newSession != NULL) {
459 // and add a port --> session mapping
460 CFDictionarySetValue(client_sessions,
461 (const void *)(uintptr_t)newSession->key,
462 newSession);
463
464 // save the audit_token in case we need to check the callers credentials
465 newSession->auditToken = audit_token;
466
467 CFRelease(newSession); // reference held by dictionary
468 }
469 });
470
471 if (newSession != NULL) {
472 addSession(newSession, FALSE);
473 }
474
475 return newSession;
476 }
477
478
479 __private_extern__
480 serverSessionRef
481 addServer(mach_port_t server)
482 {
483 // allocate a session for "the" server
484 server_session = __serverSessionCreate(NULL, server);
485 addSession(server_session, TRUE);
486
487 // add a state dump handler
488 add_state_handler();
489
490 return server_session;
491 }
492
493
494 __private_extern__
495 void
496 cleanupSession(serverSessionRef session)
497 {
498 mach_port_t server = session->key;
499
500 SC_trace("cleanup : %5d", server);
501
502 /*
503 * Close any open connections including cancelling any outstanding
504 * notification requests and releasing any locks.
505 */
506 __MACH_PORT_DEBUG(TRUE, "*** cleanupSession", server);
507 (void) __SCDynamicStoreClose(&session->store);
508 __MACH_PORT_DEBUG(TRUE, "*** cleanupSession (after __SCDynamicStoreClose)", server);
509
510 /*
511 * Our send right has already been removed. Remove our receive right.
512 */
513 #ifdef HAVE_MACHPORT_GUARDS
514 (void) mach_port_destruct(mach_task_self(), server, 0, (mach_port_context_t)session);
515 #else // HAVE_MACHPORT_GUARDS
516 (void) mach_port_mod_refs(mach_task_self(), server, MACH_PORT_RIGHT_RECEIVE, -1);
517 #endif // HAVE_MACHPORT_GUARDS
518
519 /*
520 * release any entitlement info
521 */
522 if ((session->callerWriteEntitlement != NULL) &&
523 (session->callerWriteEntitlement != kCFNull)) {
524 CFRelease(session->callerWriteEntitlement);
525 }
526
527 /*
528 * get rid of the per-session structure.
529 */
530 dispatch_sync(sessionQueue(), ^{
531 CFDictionaryRemoveValue(client_sessions,
532 (const void *)(uintptr_t)server);
533 });
534
535 return;
536 }
537
538
539 __private_extern__
540 void
541 closeSession(serverSessionRef session)
542 {
543 /*
544 * cancel and release the mach channel
545 */
546 if (session->serverChannel != NULL) {
547 dispatch_mach_cancel(session->serverChannel);
548 dispatch_release(session->serverChannel);
549 session->serverChannel = NULL;
550 }
551
552 return;
553 }
554
555
556 typedef struct ReportSessionInfo {
557 FILE *f;
558 int n;
559 } ReportSessionInfo, *ReportSessionInfoRef;
560
561 static void
562 printOne(const void *key, const void *value, void *context)
563 {
564 #pragma unused(key)
565 ReportSessionInfoRef reportInfo = (ReportSessionInfoRef)context;
566 serverSessionRef session = (serverSessionRef)value;
567
568 SCPrint(TRUE, reportInfo->f, CFSTR(" %d : port = 0x%x"), ++reportInfo->n, session->key);
569 SCPrint(TRUE, reportInfo->f, CFSTR(", name = %@"), session->name);
570 if (session->changedKeys != NULL) {
571 SCPrint(TRUE, reportInfo->f, CFSTR("\n changedKeys = %@"), session->changedKeys);
572 }
573 if (session->sessionKeys != NULL) {
574 SCPrint(TRUE, reportInfo->f, CFSTR("\n sessionKeys = %@"), session->sessionKeys);
575 }
576 SCPrint(TRUE, reportInfo->f, CFSTR("\n"));
577 return;
578 }
579
580
581 __private_extern__
582 void
583 listSessions(FILE *f)
584 {
585 dispatch_sync(sessionQueue(), ^{
586 ReportSessionInfo reportInfo = { .f = f, .n = 0 };
587
588 SCPrint(TRUE, f, CFSTR("Current sessions :\n"));
589 CFDictionaryApplyFunction(client_sessions,
590 printOne,
591 (void *)&reportInfo);
592 SCPrint(TRUE, f, CFSTR("\n"));
593 });
594 return;
595 }
596
597
598 #include <Security/Security.h>
599 #include <Security/SecTask.h>
600
601 static CFTypeRef
602 copyEntitlement(serverSessionRef session, CFStringRef entitlement)
603 {
604 SecTaskRef task;
605 CFTypeRef value = NULL;
606
607 // Create the security task from the audit token
608 task = SecTaskCreateWithAuditToken(NULL, session->auditToken);
609 if (task != NULL) {
610 CFErrorRef error = NULL;
611
612 // Get the value for the entitlement
613 value = SecTaskCopyValueForEntitlement(task, entitlement, &error);
614 if ((value == NULL) && (error != NULL)) {
615 CFIndex code = CFErrorGetCode(error);
616 CFStringRef domain = CFErrorGetDomain(error);
617
618 if (!CFEqual(domain, kCFErrorDomainMach) ||
619 ((code != kIOReturnInvalid) && (code != kIOReturnNotFound))) {
620 // if unexpected error
621 SC_log(LOG_NOTICE, "SecTaskCopyValueForEntitlement(,\"%@\",) failed, error = %@ : %@",
622 entitlement,
623 error,
624 session->name);
625 }
626 CFRelease(error);
627 }
628
629 CFRelease(task);
630 } else {
631 SC_log(LOG_NOTICE, "SecTaskCreateWithAuditToken() failed: %@",
632 session->name);
633 }
634
635 return value;
636 }
637
638
639 static pid_t
640 sessionPid(serverSessionRef session)
641 {
642 pid_t caller_pid;
643
644 #if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
645 caller_pid = audit_token_to_pid(session->auditToken);
646 #else // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
647 audit_token_to_au32(session->auditToken,
648 NULL, // auidp
649 NULL, // euid
650 NULL, // egid
651 NULL, // ruid
652 NULL, // rgid
653 &caller_pid, // pid
654 NULL, // asid
655 NULL); // tid
656 #endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
657
658 return caller_pid;
659 }
660
661
662 __private_extern__
663 Boolean
664 hasRootAccess(serverSessionRef session)
665 {
666 #if !TARGET_OS_SIMULATOR
667
668 if (session->callerRootAccess == UNKNOWN) {
669 #if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
670 session->callerEUID = audit_token_to_euid(session->auditToken);
671 #else // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
672 audit_token_to_au32(session->auditToken,
673 NULL, // auidp
674 &session->callerEUID, // euid
675 NULL, // egid
676 NULL, // ruid
677 NULL, // rgid
678 NULL, // pid
679 NULL, // asid
680 NULL); // tid
681 #endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
682 session->callerRootAccess = (session->callerEUID == 0) ? YES : NO;
683 }
684
685 return (session->callerRootAccess == YES) ? TRUE : FALSE;
686
687 #else // !TARGET_OS_SIMULATOR
688 #pragma unused(session)
689
690 /*
691 * assume that all processes interacting with
692 * the iOS Simulator "configd" are OK.
693 */
694 return TRUE;
695
696 #endif // !TARGET_OS_SIMULATOR
697 }
698
699
700 __private_extern__
701 Boolean
702 hasWriteAccess(serverSessionRef session, const char *op, CFStringRef key)
703 {
704 Boolean isSetup;
705
706 // need to special case writing "Setup:" keys
707 isSetup = CFStringHasPrefix(key, kSCDynamicStoreDomainSetup);
708
709 if (hasRootAccess(session)) {
710 pid_t pid;
711
712 // grant write access to eUID==0 processes
713
714 pid = sessionPid(session);
715 if (isSetup && (pid != getpid())) {
716 /*
717 * WAIT!!!
718 *
719 * This is NOT configd (or a plugin) trying to
720 * write to an SCDynamicStore "Setup:" key. In
721 * general, this is unwise and we should at the
722 * very least complain.
723 */
724 SC_log(LOG_NOTICE, "*** Non-configd process (pid=%d) attempting to %s \"%@\" ***",
725 pid,
726 op,
727 key);
728 }
729
730 return TRUE;
731 }
732
733 if (isSetup) {
734 /*
735 * STOP!!!
736 *
737 * This is a non-root process trying to write to
738 * an SCDynamicStore "Setup:" key. This is not
739 * something we should ever allow (regardless of
740 * any entitlements).
741 */
742 SC_log(LOG_NOTICE, "*** Non-root process (pid=%d) attempting to modify \"%@\" ***",
743 sessionPid(session),
744 key);
745
746 return FALSE;
747 }
748
749 if (session->callerWriteEntitlement == kCFNull) {
750 session->callerWriteEntitlement = copyEntitlement(session,
751 kSCWriteEntitlementName);
752 }
753
754 if (session->callerWriteEntitlement == NULL) {
755 return FALSE;
756 }
757
758 if (isA_CFBoolean(session->callerWriteEntitlement) &&
759 CFBooleanGetValue(session->callerWriteEntitlement)) {
760 // grant write access to "entitled" processes
761 return TRUE;
762 }
763
764 if (isA_CFDictionary(session->callerWriteEntitlement)) {
765 CFArrayRef keys;
766 CFArrayRef patterns;
767
768 keys = CFDictionaryGetValue(session->callerWriteEntitlement, CFSTR("keys"));
769 if (isA_CFArray(keys)) {
770 if (CFArrayContainsValue(keys,
771 CFRangeMake(0, CFArrayGetCount(keys)),
772 key)) {
773 // if key matches one of the entitlement "keys", grant
774 // write access
775 return TRUE;
776 }
777 }
778
779 patterns = CFDictionaryGetValue(session->callerWriteEntitlement, CFSTR("patterns"));
780 if (isA_CFArray(patterns)) {
781 CFIndex i;
782 CFIndex n = CFArrayGetCount(patterns);
783
784 for (i = 0; i < n; i++) {
785 CFStringRef pattern;
786
787 pattern = CFArrayGetValueAtIndex(patterns, i);
788 if (isA_CFString(pattern)) {
789 if (patternKeyMatches(pattern, key)) {
790 // if key matches one of the entitlement
791 // "patterns", grant write access
792 return TRUE;
793 }
794 }
795 }
796 }
797 }
798
799 return FALSE;
800 }
801
802
803 __private_extern__
804 Boolean
805 hasPathAccess(serverSessionRef session, const char *path)
806 {
807 pid_t pid;
808 char realPath[PATH_MAX];
809
810 if (realpath(path, realPath) == NULL) {
811 SC_log(LOG_INFO, "realpath() failed: %s", strerror(errno));
812 return FALSE;
813 }
814
815 #if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
816 pid = audit_token_to_pid(session->auditToken);
817 #else // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
818 audit_token_to_au32(session->auditToken,
819 NULL, // auidp
820 NULL, // euid
821 NULL, // egid
822 NULL, // ruid
823 NULL, // rgid
824 &pid, // pid
825 NULL, // asid
826 NULL); // tid
827 #endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
828 if (sandbox_check(pid, // pid
829 "file-write-data", // operation
830 SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, // sandbox_filter_type
831 realPath) > 0) { // ...
832 SC_log(LOG_INFO, "sandbox access denied: %s", strerror(errno));
833 return FALSE;
834 }
835
836 return TRUE;
837 }