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