]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/helper/SCHelper_server.c
configd-1109.101.1.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / helper / SCHelper_server.c
1 /*
2 * Copyright (c) 2005-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 #include <getopt.h>
25 #include <grp.h>
26 #include <pthread.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <bsm/libbsm.h>
30 #include <servers/bootstrap.h>
31 #include <sys/types.h>
32 #include <sysexits.h>
33
34 //#define DEBUG_MACH_PORT_ALLOCATIONS
35
36 #include <CoreFoundation/CoreFoundation.h>
37 #include <CoreFoundation/CFRuntime.h>
38 #include <Security/Security.h>
39 #include <Security/SecTask.h>
40
41 #define SC_LOG_HANDLE __log_SCHelper
42 #define SC_LOG_HANDLE_TYPE static
43 #include "SCPreferencesInternal.h"
44 #include "SCHelper_client.h"
45 #include "helper_types.h"
46
47 #pragma mark -
48 #pragma mark SCHelper session managment
49
50
51 //
52 // entitlement used to control read (or write) access to a given "prefsID"
53 //
54 #define kSCReadEntitlementName CFSTR("com.apple.SystemConfiguration.SCPreferences-read-access")
55 #define kSCWriteEntitlementName CFSTR("com.apple.SystemConfiguration.SCPreferences-write-access")
56
57 //
58 // entitlement used to allow limited [VPN configuration] write access to the "preferences.plist"
59 //
60 #define kSCVPNFilterEntitlementName CFSTR("com.apple.networking.vpn.configuration")
61
62 typedef enum { NO = 0, YES, UNKNOWN } lazyBoolean;
63
64 typedef const struct __SCHelperSession * SCHelperSessionRef;
65
66 typedef struct {
67
68 // base CFType information
69 CFRuntimeBase cfBase;
70
71 // per session lock
72 pthread_mutex_t lock;
73
74 // authorization
75 AuthorizationRef authorization;
76 Boolean use_entitlement;
77
78 // session mach port
79 mach_port_t port;
80 CFMachPortRef mp;
81
82 // Mach security audit trailer for evaluating credentials
83 audit_token_t auditToken;
84
85 // write access entitlement associated with this session
86 lazyBoolean callerReadAccess;
87 lazyBoolean callerWriteAccess;
88
89 // configuration filtering
90 lazyBoolean isSetChange; // only network "set" changes
91 lazyBoolean isVPNChange; // only VPN configuration changes
92 CFArrayRef vpnTypes;
93
94 // preferences
95 SCPreferencesRef prefs;
96
97 /* backtraces */
98 CFMutableSetRef backtraces;
99
100 } SCHelperSessionPrivate, *SCHelperSessionPrivateRef;
101
102
103 static CFStringRef __SCHelperSessionCopyDescription (CFTypeRef cf);
104 static void __SCHelperSessionDeallocate (CFTypeRef cf);
105
106
107 static void __SCHelperSessionLogBacktrace (const void *value, void *context);
108
109
110 static CFTypeID __kSCHelperSessionTypeID = _kCFRuntimeNotATypeID;
111 static Boolean debug = FALSE;
112 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
113 static CFRunLoopRef main_runLoop = NULL;
114 static CFMutableSetRef sessions = NULL;
115 static int sessions_closed = 0; // count of sessions recently closed
116 static int sessions_generation = 0;
117 static pthread_mutex_t sessions_lock = PTHREAD_MUTEX_INITIALIZER;
118
119
120 #pragma mark -
121 #pragma mark Logging
122
123
124 static os_log_t
125 __log_SCHelper(void)
126 {
127 static os_log_t log = NULL;
128
129 if (log == NULL) {
130 log = os_log_create("com.apple.SystemConfiguration", "SCPreferences");
131 }
132
133 return log;
134 }
135
136
137 #pragma mark -
138 #pragma mark Helper session management
139
140
141 #if !TARGET_OS_IPHONE
142 static Boolean
143 __SCHelperSessionUseEntitlement(SCHelperSessionRef session)
144 {
145 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
146
147 return sessionPrivate->use_entitlement;
148 }
149 #endif //!TARGET_OS_IPHONE
150
151
152 static AuthorizationRef
153 __SCHelperSessionGetAuthorization(SCHelperSessionRef session)
154 {
155 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
156
157 return sessionPrivate->authorization;
158 }
159
160
161 static Boolean
162 __SCHelperSessionSetAuthorization(SCHelperSessionRef session, CFTypeRef authorizationData)
163 {
164 Boolean ok = TRUE;
165 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
166
167 /*
168 * On OSX, the authorizationData can either be a CFData-wrapped/externalized
169 * "authorization" or a CFString indicating that we should check entitlements.
170 *
171 * On iOS, the authorizationData is a CFString indicating that we should
172 * check entitlements.
173 */
174 pthread_mutex_lock(&sessionPrivate->lock);
175
176 if (sessionPrivate->authorization != NULL) {
177 #if !TARGET_OS_IPHONE
178 if (!__SCHelperSessionUseEntitlement(session)) {
179 OSStatus status;
180
181 status = AuthorizationFree(sessionPrivate->authorization, kAuthorizationFlagDefaults);
182 // status = AuthorizationFree(sessionPrivate->authorization, kAuthorizationFlagDestroyRights);
183 if (status != errAuthorizationSuccess) {
184 SC_log(LOG_DEBUG, "AuthorizationFree() failed: status = %d",
185 (int)status);
186 }
187 } else {
188 CFRelease(sessionPrivate->authorization);
189 }
190 #else //!TARGET_OS_IPHONE
191 CFRelease(sessionPrivate->authorization);
192 #endif //!TARGET_OS_IPHONE
193 sessionPrivate->authorization = NULL;
194 sessionPrivate->use_entitlement = FALSE;
195 }
196
197 #if !TARGET_OS_IPHONE
198 if (isA_CFData(authorizationData)) {
199 AuthorizationExternalForm extForm;
200
201 if (CFDataGetLength(authorizationData) == sizeof(extForm.bytes)) {
202 OSStatus status;
203
204 memcpy(extForm.bytes, CFDataGetBytePtr(authorizationData), sizeof(extForm.bytes));
205 status = AuthorizationCreateFromExternalForm(&extForm,
206 &sessionPrivate->authorization);
207 if (status != errAuthorizationSuccess) {
208 SC_log(LOG_NOTICE, "AuthorizationCreateFromExternalForm() failed: status = %d",
209 (int)status);
210 sessionPrivate->authorization = NULL;
211 ok = FALSE;
212 }
213 }
214 } else if (isA_CFString(authorizationData)) {
215 sessionPrivate->authorization = (void *)CFRetain(authorizationData);
216 sessionPrivate->use_entitlement = TRUE;
217 }
218 #else //!TARGET_OS_IPHONE
219 if (isA_CFString(authorizationData)) {
220 sessionPrivate->authorization = (void *)CFRetain(authorizationData);
221 sessionPrivate->use_entitlement = TRUE;
222 }
223 #endif //!TARGET_OS_IPHONE
224
225 pthread_mutex_unlock(&sessionPrivate->lock);
226
227 return ok;
228 }
229
230
231 static SCPreferencesRef
232 __SCHelperSessionGetPreferences(SCHelperSessionRef session)
233 {
234 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
235
236 return sessionPrivate->prefs;
237 }
238
239
240 static void
241 __SCHelperSessionSetThreadName(SCHelperSessionRef session)
242 {
243 char *caller = NULL;
244 char name[64];
245 char *path = NULL;
246 char *path_s = NULL;
247 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
248
249 if (sessionPrivate->mp == NULL) {
250 return;
251 }
252
253 if (sessionPrivate->prefs != NULL) {
254 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)sessionPrivate->prefs;
255
256 if (prefsPrivate->name != NULL) {
257 caller = _SC_cfstring_to_cstring(prefsPrivate->name,
258 NULL,
259 0,
260 kCFStringEncodingUTF8);
261 }
262
263 path = (prefsPrivate->newPath != NULL) ? prefsPrivate->newPath : prefsPrivate->path;
264 if (path != NULL) {
265 path_s = strrchr(path, '/');
266 if (path_s != NULL) {
267 path = path_s;
268 }
269 }
270 }
271
272 if (caller != NULL) {
273 snprintf(name, sizeof(name), "SESSION|%p|%s|%s%s",
274 (void *)(uintptr_t)CFMachPortGetPort(sessionPrivate->mp),
275 caller,
276 (path_s != NULL) ? "*/" : "",
277 (path != NULL) ? path : "?");
278 CFAllocatorDeallocate(NULL, caller);
279 } else {
280 snprintf(name, sizeof(name), "SESSION|%p",
281 (void *)(uintptr_t)CFMachPortGetPort(sessionPrivate->mp));
282 }
283
284 pthread_setname_np(name);
285
286 return;
287 }
288
289
290 static Boolean
291 __SCHelperSessionSetPreferences(SCHelperSessionRef session, SCPreferencesRef prefs)
292 {
293 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
294
295 pthread_mutex_lock(&sessionPrivate->lock);
296
297 if (prefs != NULL) {
298 CFRetain(prefs);
299 }
300 if (sessionPrivate->prefs != NULL) {
301 SC_log(LOG_INFO, "%p : close", session);
302 CFRelease(sessionPrivate->prefs);
303 }
304 if (prefs != NULL) {
305 SC_log(LOG_INFO, "%p : open, prefs = %@", session, prefs);
306 }
307 sessionPrivate->prefs = prefs;
308
309 __SCHelperSessionSetThreadName(session);
310
311 pthread_mutex_unlock(&sessionPrivate->lock);
312
313 return TRUE;
314 }
315
316
317 static void
318 __SCHelperSessionSetNetworkSetFilter(SCHelperSessionRef session, Boolean setChange)
319 {
320 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
321
322 pthread_mutex_lock(&sessionPrivate->lock);
323
324 sessionPrivate->isSetChange = setChange ? YES : NO;
325
326 pthread_mutex_unlock(&sessionPrivate->lock);
327
328 return;
329 }
330
331
332 static Boolean
333 __SCHelperSessionUseNetworkSetFilter(SCHelperSessionRef session)
334 {
335 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
336
337 return (sessionPrivate->isSetChange == YES) ? TRUE : FALSE;
338 }
339
340
341 static Boolean
342 __SCHelperSessionSetVPNFilter(SCHelperSessionRef session, Boolean vpnChange, CFArrayRef vpnTypes)
343 {
344 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
345
346 pthread_mutex_lock(&sessionPrivate->lock);
347
348 sessionPrivate->isVPNChange = vpnChange ? YES : NO;
349
350 if (vpnTypes != NULL) {
351 CFRetain(vpnTypes);
352 }
353 if (sessionPrivate->vpnTypes != NULL) {
354 CFRelease(sessionPrivate->vpnTypes);
355 }
356 sessionPrivate->vpnTypes = vpnTypes;
357
358 pthread_mutex_unlock(&sessionPrivate->lock);
359
360 return TRUE;
361 }
362
363
364 static Boolean
365 __SCHelperSessionUseVPNFilter(SCHelperSessionRef session, CFArrayRef *vpnTypes)
366 {
367 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
368
369 *vpnTypes = sessionPrivate->vpnTypes;
370 return (sessionPrivate->vpnTypes != NULL) ? TRUE : FALSE;
371 }
372
373
374 static void
375 __SCHelperSessionLog(const void *value, void *context)
376 {
377 SCHelperSessionRef session = (SCHelperSessionRef)value;
378 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
379 FILE **logFile = (FILE **)context;
380
381 pthread_mutex_lock(&sessionPrivate->lock);
382
383 if ((sessionPrivate->mp != NULL) && (sessionPrivate->prefs != NULL)) {
384 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)sessionPrivate->prefs;
385
386 SC_log(LOG_INFO, " %p {port = %p, caller = %@, path = %s%s}",
387 session,
388 (void *)(uintptr_t)CFMachPortGetPort(sessionPrivate->mp),
389 prefsPrivate->name,
390 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path,
391 prefsPrivate->locked ? ", locked" : "");
392
393 if ((sessionPrivate->backtraces != NULL) &&
394 (CFSetGetCount(sessionPrivate->backtraces) > 0)) {
395 // log/report all collected backtraces
396 CFSetApplyFunction(sessionPrivate->backtraces,
397 __SCHelperSessionLogBacktrace,
398 (void *)logFile);
399
400 // to ensure that we don't log the same backtraces multiple
401 // times we remove any reported traces
402 CFRelease(sessionPrivate->backtraces);
403 sessionPrivate->backtraces = NULL;
404 }
405 }
406
407 pthread_mutex_unlock(&sessionPrivate->lock);
408
409 return;
410 }
411
412
413 #pragma mark -
414
415
416 static const CFRuntimeClass __SCHelperSessionClass = {
417 0, // version
418 "SCHelperSession", // className
419 NULL, // init
420 NULL, // copy
421 __SCHelperSessionDeallocate, // dealloc
422 NULL, // equal
423 NULL, // hash
424 NULL, // copyFormattingDesc
425 __SCHelperSessionCopyDescription // copyDebugDesc
426 };
427
428
429 static CFStringRef
430 __SCHelperSessionCopyDescription(CFTypeRef cf)
431 {
432 CFAllocatorRef allocator = CFGetAllocator(cf);
433 CFMutableStringRef result;
434 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)cf;
435
436 pthread_mutex_lock(&sessionPrivate->lock);
437
438 result = CFStringCreateMutable(allocator, 0);
439 CFStringAppendFormat(result, NULL, CFSTR("<SCHelperSession %p [%p]> {"), cf, allocator);
440 CFStringAppendFormat(result, NULL, CFSTR("authorization = %p"), sessionPrivate->authorization);
441 if (sessionPrivate->mp != NULL) {
442 CFStringAppendFormat(result, NULL,
443 CFSTR(", mp = %p (port = 0x%x)"),
444 sessionPrivate->mp,
445 CFMachPortGetPort(sessionPrivate->mp));
446 }
447 if (sessionPrivate->prefs != NULL) {
448 CFStringAppendFormat(result, NULL, CFSTR(", prefs = %@"), sessionPrivate->prefs);
449 }
450 CFStringAppendFormat(result, NULL, CFSTR("}"));
451
452 pthread_mutex_unlock(&sessionPrivate->lock);
453
454 return result;
455 }
456
457
458 static void
459 __SCHelperSessionDeallocate(CFTypeRef cf)
460 {
461 SCHelperSessionRef session = (SCHelperSessionRef)cf;
462 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
463
464 // we're releasing "a" session so take the global lock...
465 pthread_mutex_lock(&sessions_lock);
466
467 // release resources
468 __SCHelperSessionSetAuthorization(session, NULL);
469 __SCHelperSessionSetPreferences(session, NULL);
470 __SCHelperSessionSetNetworkSetFilter(session, FALSE);
471 __SCHelperSessionSetVPNFilter(session, FALSE, NULL);
472 pthread_mutex_destroy(&sessionPrivate->lock);
473 if (sessionPrivate->backtraces != NULL) {
474 CFRelease(sessionPrivate->backtraces);
475 }
476
477 // we no longer need/want to track this session
478 CFSetRemoveValue(sessions, sessionPrivate);
479 sessions_generation++;
480 sessions_closed++;
481
482 // release the global lock, wake up the main runloop, all done
483 pthread_mutex_unlock(&sessions_lock);
484 CFRunLoopWakeUp(main_runLoop);
485
486 return;
487 }
488
489
490 static void
491 __SCHelperSessionInitialize(void)
492 {
493 __kSCHelperSessionTypeID = _CFRuntimeRegisterClass(&__SCHelperSessionClass);
494 return;
495 }
496
497
498 static SCHelperSessionRef
499 __SCHelperSessionCreate(CFAllocatorRef allocator)
500 {
501 SCHelperSessionPrivateRef sessionPrivate;
502 uint32_t size;
503
504 // initialize runtime
505 pthread_once(&initialized, __SCHelperSessionInitialize);
506
507 // allocate session
508 size = sizeof(SCHelperSessionPrivate) - sizeof(CFRuntimeBase);
509 sessionPrivate = (SCHelperSessionPrivateRef)_CFRuntimeCreateInstance(allocator,
510 __kSCHelperSessionTypeID,
511 size,
512 NULL);
513 if (sessionPrivate == NULL) {
514 return NULL;
515 }
516
517 /* initialize non-zero/NULL members */
518 if (pthread_mutex_init(&sessionPrivate->lock, NULL) != 0) {
519 SC_log(LOG_NOTICE, "pthread_mutex_init(): failure to initialize per session lock");
520 CFRelease(sessionPrivate);
521 return NULL;
522 }
523
524 pthread_mutex_lock(&sessionPrivate->lock);
525 sessionPrivate->callerReadAccess = UNKNOWN;
526 sessionPrivate->callerWriteAccess = UNKNOWN;
527 sessionPrivate->isSetChange = UNKNOWN;
528 sessionPrivate->isVPNChange = UNKNOWN;
529 pthread_mutex_unlock(&sessionPrivate->lock);
530
531 // keep track this session
532 pthread_mutex_lock(&sessions_lock);
533 if (sessions == NULL) {
534 const CFSetCallBacks mySetCallBacks = { 0, NULL, NULL, CFCopyDescription, CFEqual, CFHash };
535
536 // create a non-retaining set
537 sessions = CFSetCreateMutable(NULL, 0, &mySetCallBacks);
538 }
539 CFSetAddValue(sessions, sessionPrivate);
540 sessions_generation++;
541 pthread_mutex_unlock(&sessions_lock);
542
543 return (SCHelperSessionRef)sessionPrivate;
544 }
545
546
547 #pragma mark -
548
549
550 static SCHelperSessionRef
551 __SCHelperSessionFindWithPort(mach_port_t port)
552 {
553 SCHelperSessionRef session = NULL;
554
555 // keep track this session
556 pthread_mutex_lock(&sessions_lock);
557 if (sessions != NULL) {
558 CFIndex i;
559 CFIndex n = CFSetGetCount(sessions);
560 const void * vals_q[16];
561 const void ** vals = vals_q;
562
563 if (n > (CFIndex)(sizeof(vals_q) / sizeof(SCHelperSessionRef)))
564 vals = CFAllocatorAllocate(NULL, n * sizeof(CFStringRef), 0);
565 CFSetGetValues(sessions, vals);
566 for (i = 0; i < n; i++) {
567 SCHelperSessionPrivateRef sessionPrivate;
568
569 sessionPrivate = (SCHelperSessionPrivateRef)vals[i];
570 if (sessionPrivate->port == port) {
571 session = (SCHelperSessionRef)sessionPrivate;
572 break;
573 }
574 }
575 if (vals != vals_q)
576 CFAllocatorDeallocate(NULL, vals);
577 }
578 pthread_mutex_unlock(&sessions_lock);
579
580 return session;
581 }
582
583
584 #pragma mark -
585 #pragma mark Session backtrace logging
586
587
588 static void
589 __SCHelperSessionAddBacktrace(SCHelperSessionRef session, CFStringRef backtrace, const char * command)
590 {
591 CFStringRef logEntry;
592 SCPreferencesRef prefs;
593 SCPreferencesPrivateRef prefsPrivate;
594 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
595
596 prefs = __SCHelperSessionGetPreferences((SCHelperSessionRef)sessionPrivate);
597 if (prefs == NULL) {
598 // if no prefs
599 return;
600 }
601 prefsPrivate = (SCPreferencesPrivateRef)prefs;
602
603 logEntry = CFStringCreateWithFormat(NULL, NULL,
604 CFSTR("%@ [%s]: %s\n\n%@"),
605 prefsPrivate->name,
606 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path,
607 command,
608 backtrace);
609
610 pthread_mutex_lock(&sessionPrivate->lock);
611
612 if (sessionPrivate->backtraces == NULL) {
613 sessionPrivate->backtraces = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
614 }
615 CFSetSetValue(sessionPrivate->backtraces, logEntry);
616
617 pthread_mutex_unlock(&sessionPrivate->lock);
618
619 CFRelease(logEntry);
620 return;
621 }
622
623
624 static void
625 __SCHelperSessionLogBacktrace(const void *value, void *context)
626 {
627 CFSetRef backtrace = (CFSetRef)value;
628 FILE **logFile = (FILE **)context;
629
630 if (*logFile == NULL) {
631 char path[PATH_MAX];
632 struct tm tm_now;
633 struct timeval tv_now;
634
635 (void)gettimeofday(&tv_now, NULL);
636 (void)localtime_r(&tv_now.tv_sec, &tm_now);
637
638 snprintf(path,
639 sizeof(path),
640 _SC_CRASH_DIR "/" "SCHelper-%4d-%02d-%02d-%02d%02d%02d.log",
641 tm_now.tm_year + 1900,
642 tm_now.tm_mon + 1,
643 tm_now.tm_mday,
644 tm_now.tm_hour,
645 tm_now.tm_min,
646 tm_now.tm_sec);
647
648 *logFile = fopen(path, "a");
649 if (*logFile == NULL) {
650 // if log file could not be created
651 return;
652 }
653
654 SC_log(LOG_INFO, "created backtrace log: %s", path);
655 }
656
657 SCPrint(TRUE, *logFile, CFSTR("%@\n"), backtrace);
658 return;
659 }
660
661
662 #pragma mark -
663 #pragma mark Helpers
664
665
666 #define HELPER_STATUS_NO_REPLY UINT32_MAX
667
668
669 /*
670 * EXIT
671 * (in) data = N/A
672 * (out) status = SCError()
673 * (out) reply = N/A
674 */
675 static Boolean
676 do_Exit(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
677 {
678 #pragma unused(session)
679 #pragma unused(info)
680 #pragma unused(data)
681 #pragma unused(reply)
682 *status = HELPER_STATUS_NO_REPLY;
683 return FALSE;
684 }
685
686
687 /*
688 * AUTHORIZE
689 * (in) data = authorizationDict (in 2 flavors)
690 * kSCHelperAuthAuthorization - use provided AuthorizationExternalForm
691 * kSCHelperAuthCallerInfo - use entitlement
692 * (out) status = OSStatus
693 * (out) reply = N/A
694 */
695 static Boolean
696 do_Auth(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
697 {
698 #pragma unused(info)
699 #pragma unused(reply)
700 CFDictionaryRef authorizationDict;
701 #if !TARGET_OS_IPHONE
702 CFDataRef authorizationData = NULL;
703 #endif // !TARGET_OS_IPHONE
704 Boolean ok = FALSE;
705
706 if (!_SCUnserialize((CFPropertyListRef*)&authorizationDict, data, NULL, 0)) {
707 return FALSE;
708 }
709
710 if (authorizationDict == NULL) {
711 return FALSE;
712 }
713
714 if (!isA_CFDictionary(authorizationDict)) {
715 CFRelease(authorizationDict);
716 return FALSE;
717 }
718
719 #if !TARGET_OS_IPHONE
720 authorizationData = CFDictionaryGetValue(authorizationDict, kSCHelperAuthAuthorization);
721 if (authorizationData != NULL && isA_CFData(authorizationData)) {
722 ok = __SCHelperSessionSetAuthorization(session, authorizationData);
723 } else
724 #endif // !TARGET_OS_IPHONE
725 {
726 CFStringRef authorizationInfo;
727
728 authorizationInfo = CFDictionaryGetValue(authorizationDict, kSCHelperAuthCallerInfo);
729 if (authorizationInfo != NULL && isA_CFString(authorizationInfo)) {
730 ok = __SCHelperSessionSetAuthorization(session, authorizationInfo);
731 }
732 }
733
734 CFRelease(authorizationDict);
735 *status = ok ? 0 : 1;
736 return TRUE;
737 }
738
739
740 #if !TARGET_OS_IPHONE
741
742
743 /*
744 * SCHELPER_MSG_KEYCHAIN_COPY
745 * (in) data = unique_id
746 * (out) status = SCError()
747 * (out) reply = password
748 */
749 static Boolean
750 do_keychain_copy(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
751 {
752 #pragma unused(info)
753 Boolean ok = FALSE;
754 SCPreferencesRef prefs;
755 CFStringRef unique_id = NULL;
756
757 if ((data != NULL) && !_SCUnserializeString(&unique_id, data, NULL, 0)) {
758 return FALSE;
759 }
760
761 if (unique_id != NULL) {
762 if (isA_CFString(unique_id)) {
763 prefs = __SCHelperSessionGetPreferences(session);
764 *reply = _SCPreferencesSystemKeychainPasswordItemCopy(prefs, unique_id);
765 if (*reply == NULL) {
766 *status = SCError();
767 }
768 ok = TRUE;
769 }
770
771 CFRelease(unique_id);
772 }
773
774 return ok;
775 }
776
777
778 /*
779 * SCHELPER_MSG_KEYCHAIN_EXISTS
780 * (in) data = unique_id
781 * (out) status = SCError()
782 * (out) reply = N/A
783 */
784 static Boolean
785 do_keychain_exists(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
786 {
787 #pragma unused(info)
788 #pragma unused(reply)
789 Boolean ok = FALSE;
790 SCPreferencesRef prefs;
791 CFStringRef unique_id = NULL;
792
793 if ((data != NULL) && !_SCUnserializeString(&unique_id, data, NULL, 0)) {
794 return FALSE;
795 }
796
797 if (unique_id != NULL) {
798 if (isA_CFString(unique_id)) {
799 prefs = __SCHelperSessionGetPreferences(session);
800 ok = _SCPreferencesSystemKeychainPasswordItemExists(prefs, unique_id);
801 if (!ok) {
802 *status = SCError();
803 }
804 }
805
806 CFRelease(unique_id);
807 }
808
809 return ok;
810 }
811
812
813 /*
814 * SCHELPER_MSG_KEYCHAIN_REMOVE
815 * (in) data = unique_id
816 * (out) status = SCError()
817 * (out) reply = N/A
818 */
819 static Boolean
820 do_keychain_remove(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
821 {
822 #pragma unused(info)
823 #pragma unused(reply)
824 Boolean ok = FALSE;
825 SCPreferencesRef prefs;
826 CFStringRef unique_id = NULL;
827
828 if ((data != NULL) && !_SCUnserializeString(&unique_id, data, NULL, 0)) {
829 return FALSE;
830 }
831
832 if (unique_id != NULL) {
833 if (isA_CFString(unique_id)) {
834 prefs = __SCHelperSessionGetPreferences(session);
835 ok = _SCPreferencesSystemKeychainPasswordItemRemove(prefs, unique_id);
836 if (!ok) {
837 *status = SCError();
838 }
839 }
840
841 CFRelease(unique_id);
842 }
843
844 return ok;
845 }
846
847
848 /*
849 * SCHELPER_MSG_KEYCHAIN_SET
850 * (in) data = options dictionary
851 * (out) status = SCError()
852 * (out) reply = N/A
853 */
854 static Boolean
855 do_keychain_set(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
856 {
857 #pragma unused(info)
858 #pragma unused(reply)
859 CFStringRef account;
860 CFStringRef description;
861 CFArrayRef executablePaths = NULL;
862 CFStringRef label;
863 Boolean ok;
864 CFDictionaryRef options = NULL;
865 CFDataRef password;
866 SCPreferencesRef prefs;
867 CFStringRef unique_id;
868
869 if ((data != NULL) && !_SCUnserialize((CFPropertyListRef *)&options, data, NULL, 0)) {
870 return FALSE;
871 }
872
873 if (options != NULL) {
874 if (!isA_CFDictionary(options)) {
875 CFRelease(options);
876 return FALSE;
877 }
878 } else {
879 return FALSE;
880 }
881
882 if (CFDictionaryGetValueIfPresent(options,
883 kSCKeychainOptionsAllowedExecutables,
884 (const void **)&executablePaths)) {
885 CFMutableArrayRef executableURLs;
886 CFIndex i;
887 CFIndex n;
888 CFMutableDictionaryRef newOptions;
889
890 executableURLs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
891 n = CFArrayGetCount(executablePaths);
892 for (i = 0; i < n; i++) {
893 CFDataRef path;
894 CFURLRef url;
895
896 path = CFArrayGetValueAtIndex(executablePaths, i);
897 url = CFURLCreateFromFileSystemRepresentation(NULL,
898 CFDataGetBytePtr(path),
899 CFDataGetLength(path),
900 FALSE);
901 if (url != NULL) {
902 CFArrayAppendValue(executableURLs, url);
903 CFRelease(url);
904 }
905 }
906
907 newOptions = CFDictionaryCreateMutableCopy(NULL, 0, options);
908 CFDictionarySetValue(newOptions, kSCKeychainOptionsAllowedExecutables, executableURLs);
909 CFRelease(executableURLs);
910
911 CFRelease(options);
912 options = newOptions;
913 }
914
915 unique_id = CFDictionaryGetValue(options, kSCKeychainOptionsUniqueID);
916 label = CFDictionaryGetValue(options, kSCKeychainOptionsLabel);
917 description = CFDictionaryGetValue(options, kSCKeychainOptionsDescription);
918 account = CFDictionaryGetValue(options, kSCKeychainOptionsAccount);
919 password = CFDictionaryGetValue(options, kSCKeychainOptionsPassword);
920
921 prefs = __SCHelperSessionGetPreferences(session);
922 ok = _SCPreferencesSystemKeychainPasswordItemSet(prefs,
923 unique_id,
924 label,
925 description,
926 account,
927 password,
928 options);
929 CFRelease(options);
930 if (!ok) {
931 *status = SCError();
932 }
933
934 return TRUE;
935 }
936
937
938 #endif // !TARGET_OS_IPHONE
939
940
941 /*
942 * SCHELPER_MSG_INTERFACE_REFRESH
943 * (in) data = ifName
944 * (out) status = SCError()
945 * (out) reply = N/A
946 */
947 static Boolean
948 do_interface_refresh(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
949 {
950 #pragma unused(session)
951 #pragma unused(info)
952 #pragma unused(reply)
953 CFStringRef ifName = NULL;
954 Boolean ok = FALSE;
955
956 if ((data != NULL) && !_SCUnserializeString(&ifName, data, NULL, 0)) {
957 *status = kSCStatusInvalidArgument;
958 SC_log(LOG_NOTICE, "interface name not valid");
959 return FALSE;
960 }
961
962 if (ifName == NULL) {
963 *status = kSCStatusInvalidArgument;
964 SC_log(LOG_NOTICE, "interface name not valid");
965 return FALSE;
966 }
967
968 if (isA_CFString(ifName)) {
969 ok = _SCNetworkInterfaceForceConfigurationRefresh(ifName);
970 if (!ok) {
971 *status = SCError();
972 SC_log(LOG_NOTICE, "interface \"%@\" not refreshed: %s",
973 ifName,
974 SCErrorString(*status));
975 }
976 } else {
977 *status = kSCStatusInvalidArgument;
978 SC_log(LOG_NOTICE, "interface name not valid");
979 }
980
981 CFRelease(ifName);
982
983 return ok;
984 }
985
986
987 /*
988 * OPEN
989 * (in) data = prefsID
990 * (out) status = SCError()
991 * (out) reply = N/A
992 */
993 static Boolean
994 do_prefs_Open(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
995 {
996 #pragma unused(info)
997 #pragma unused(reply)
998 CFStringRef name;
999 CFDictionaryRef options;
1000 CFNumberRef pid;
1001 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
1002 CFDictionaryRef prefsInfo = NULL;
1003 CFStringRef prefsID;
1004 CFStringRef prefsName;
1005 CFStringRef proc_name;
1006
1007 if (prefs != NULL) {
1008 return FALSE;
1009 }
1010
1011 if ((data != NULL) && !_SCUnserialize((CFPropertyListRef *)&prefsInfo, data, NULL, 0)) {
1012 SC_log(LOG_NOTICE, "data not valid, %@", data);
1013 return FALSE;
1014 }
1015
1016 if ((prefsInfo == NULL) || !isA_CFDictionary(prefsInfo)) {
1017 SC_log(LOG_NOTICE, "info not valid");
1018 if (prefsInfo != NULL) CFRelease(prefsInfo);
1019 return FALSE;
1020 }
1021
1022 // get [optional] prefsID
1023 prefsID = CFDictionaryGetValue(prefsInfo, CFSTR("prefsID"));
1024 prefsID = isA_CFString(prefsID);
1025 if (prefsID != NULL) {
1026 if (CFStringHasPrefix(prefsID, CFSTR("/")) ||
1027 CFStringHasPrefix(prefsID, CFSTR("../")) ||
1028 CFStringHasSuffix(prefsID, CFSTR("/..")) ||
1029 (CFStringFind(prefsID, CFSTR("/../"), 0).location != kCFNotFound)) {
1030 // if we're trying to escape from the preferences directory
1031 SC_log(LOG_NOTICE, "prefsID (%@) not valid", prefsID);
1032 CFRelease(prefsInfo);
1033 *status = kSCStatusInvalidArgument;
1034 return TRUE;
1035 }
1036 }
1037
1038 // get [optional] options
1039 options = CFDictionaryGetValue(prefsInfo, CFSTR("options"));
1040 options = isA_CFDictionary(options);
1041
1042 // get preferences session "name"
1043 name = CFDictionaryGetValue(prefsInfo, CFSTR("name"));
1044 if (!isA_CFString(name)) {
1045 SC_log(LOG_NOTICE, "session \"name\" not valid");
1046 CFRelease(prefsInfo);
1047 return FALSE;
1048 }
1049
1050 // get PID of caller
1051 pid = CFDictionaryGetValue(prefsInfo, CFSTR("PID"));
1052 if (!isA_CFNumber(pid)) {
1053 SC_log(LOG_NOTICE, "PID not valid");
1054 CFRelease(prefsInfo);
1055 return FALSE;
1056 }
1057
1058 // get process name of caller
1059 proc_name = CFDictionaryGetValue(prefsInfo, CFSTR("PROC_NAME"));
1060 if (!isA_CFString(proc_name)) {
1061 SC_log(LOG_NOTICE, "process name not valid");
1062 CFRelease(prefsInfo);
1063 return FALSE;
1064 }
1065
1066 // build [helper] preferences "name" (used for debugging) and estabish
1067 // a preferences session.
1068 prefsName = CFStringCreateWithFormat(NULL, NULL,
1069 CFSTR("%@(%@):%@"),
1070 proc_name,
1071 pid,
1072 name);
1073
1074 prefs = SCPreferencesCreateWithOptions(NULL, prefsName, prefsID, NULL, options);
1075 CFRelease(prefsName);
1076 CFRelease(prefsInfo);
1077
1078 __SCHelperSessionSetPreferences(session, prefs);
1079
1080 if (prefs != NULL) {
1081 #ifdef NOTYET
1082 Boolean ok;
1083 CFRunLoopRef rl = CFRunLoopGetCurrent();
1084
1085 // [temporarily] schedule notifications to ensure that we can use
1086 // the SCDynamicStore to track helper sessions
1087 ok = SCPreferencesScheduleWithRunLoop(prefs, rl, kCFRunLoopDefaultMode);
1088 if (ok) {
1089 (void)SCPreferencesUnscheduleFromRunLoop(prefs, rl, kCFRunLoopDefaultMode);
1090 }
1091 #endif // NOTYET
1092
1093 // the session now holds a references to the SCPreferencesRef
1094 CFRelease(prefs);
1095 } else {
1096 *status = SCError();
1097 }
1098
1099 return TRUE;
1100 }
1101
1102
1103 /*
1104 * ACCESS
1105 * (in) data = N/A
1106 * (out) status = SCError()
1107 * (out) reply = current signature + current preferences
1108 */
1109 static Boolean
1110 do_prefs_Access(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
1111 {
1112 #pragma unused(info)
1113 #pragma unused(data)
1114 Boolean ok;
1115 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
1116 CFDataRef signature;
1117
1118 if (prefs == NULL) {
1119 return FALSE;
1120 }
1121
1122 signature = SCPreferencesGetSignature(prefs);
1123 if (signature != NULL) {
1124 const void * dictKeys[2];
1125 const void * dictVals[2];
1126 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
1127 CFDictionaryRef replyDict;
1128
1129 dictKeys[0] = CFSTR("signature");
1130 dictVals[0] = signature;
1131
1132 dictKeys[1] = CFSTR("preferences");
1133 dictVals[1] = prefsPrivate->prefs;
1134
1135 replyDict = CFDictionaryCreate(NULL,
1136 (const void **)&dictKeys,
1137 (const void **)&dictVals,
1138 sizeof(dictKeys)/sizeof(dictKeys[0]),
1139 &kCFTypeDictionaryKeyCallBacks,
1140 &kCFTypeDictionaryValueCallBacks);
1141
1142 ok = _SCSerialize(replyDict, reply, NULL, NULL);
1143 CFRelease(replyDict);
1144 if (!ok) {
1145 return FALSE;
1146 }
1147 } else {
1148 *status = SCError();
1149 }
1150
1151 return TRUE;
1152 }
1153
1154
1155 /*
1156 * LOCK
1157 * (in) data = client prefs signature (NULL if check not needed)
1158 * (out) status = SCError()
1159 * (out) reply = N/A
1160 */
1161 static Boolean
1162 do_prefs_Lock(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
1163 {
1164 #pragma unused(reply)
1165 CFDataRef clientSignature = (CFDataRef)data;
1166 Boolean ok;
1167 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
1168 Boolean wait = (info == (void *)FALSE) ? FALSE : TRUE;
1169
1170 if (prefs == NULL) {
1171 return FALSE;
1172 }
1173
1174 ok = SCPreferencesLock(prefs, wait);
1175 if (!ok) {
1176 *status = SCError();
1177 return TRUE;
1178 }
1179
1180 if (clientSignature != NULL) {
1181 CFDataRef serverSignature;
1182
1183 serverSignature = SCPreferencesGetSignature(prefs);
1184 if (!CFEqual(clientSignature, serverSignature)) {
1185 (void)SCPreferencesUnlock(prefs);
1186 *status = kSCStatusStale;
1187 }
1188 }
1189
1190 return TRUE;
1191 }
1192
1193
1194 /*
1195 * COMMIT
1196 * (in) data = new preferences (NULL if commit w/no changes)
1197 * (out) status = SCError()
1198 * (out) reply = new signature
1199 */
1200 static Boolean
1201 do_prefs_Commit(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
1202 {
1203 #pragma unused(info)
1204 Boolean ok;
1205 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
1206 CFPropertyListRef prefsData = NULL;
1207 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
1208 Boolean saveAccessed;
1209 Boolean saveChanged;
1210 CFMutableDictionaryRef savePrefs = NULL;
1211 Boolean saveValid = FALSE;
1212 Boolean useSetFilter;
1213 Boolean useVPNFilter;
1214 CFArrayRef vpnTypes = NULL;
1215
1216 if (prefs == NULL) {
1217 return FALSE;
1218 }
1219
1220 if (data == NULL) {
1221 // if commit with no changes
1222 goto commit;
1223 }
1224
1225 ok = _SCUnserialize(&prefsData, data, NULL, 0);
1226 if (!ok) {
1227 return FALSE;
1228 }
1229
1230 if (!isA_CFDictionary(prefsData)) {
1231 *status = kSCStatusFailed;
1232 ok = FALSE;
1233 goto done;
1234 }
1235
1236 useSetFilter = __SCHelperSessionUseNetworkSetFilter(session);
1237 useVPNFilter = __SCHelperSessionUseVPNFilter(session, &vpnTypes);
1238 if (useSetFilter || useVPNFilter) {
1239 ok = FALSE;
1240
1241 if (prefsPrivate->prefs != NULL) {
1242 CFIndex c;
1243 CFMutableDictionaryRef prefsNew = NULL;
1244 CFMutableDictionaryRef prefsOld = NULL;
1245 CFMutableDictionaryRef prefsSave = prefsPrivate->prefs;
1246
1247 for (c = 0; c < 2; c++) {
1248
1249 switch (c) {
1250 case 0 :
1251 prefsPrivate->prefs = CFDictionaryCreateMutableCopy(NULL, 0, prefsSave);
1252 break;
1253 case 1 :
1254 prefsPrivate->prefs = CFDictionaryCreateMutableCopy(NULL, 0, prefsData);
1255 break;
1256 }
1257
1258 if (useSetFilter) {
1259 // filter out current network set selection
1260 (void) SCPreferencesRemoveValue(prefs, kSCPrefCurrentSet);
1261 } else if (useVPNFilter) {
1262 CFRange range = CFRangeMake(0, CFArrayGetCount(vpnTypes));
1263 CFArrayRef services;
1264
1265 // filter out VPN services of the specified type
1266 services = SCNetworkServiceCopyAll(prefs);
1267 if (services != NULL) {
1268 CFIndex i;
1269 CFIndex n = CFArrayGetCount(services);
1270
1271 for (i = 0; i < n; i++) {
1272 SCNetworkServiceRef service;
1273
1274 service = CFArrayGetValueAtIndex(services, i);
1275 if (_SCNetworkServiceIsVPN(service)) {
1276 SCNetworkInterfaceRef child;
1277 CFStringRef childType = NULL;
1278 SCNetworkInterfaceRef interface;
1279 CFStringRef interfaceType;
1280
1281 interface = SCNetworkServiceGetInterface(service);
1282 interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
1283 child = SCNetworkInterfaceGetInterface(interface);
1284 if (child != NULL) {
1285 childType = SCNetworkInterfaceGetInterfaceType(child);
1286 }
1287 if (CFEqual(interfaceType, kSCNetworkInterfaceTypeVPN) &&
1288 (childType != NULL) &&
1289 CFArrayContainsValue(vpnTypes, range, childType)) {
1290 // filter out VPN service
1291 (void) SCNetworkServiceRemove(service);
1292 } else {
1293 // mark all other VPN services "enabled"
1294 (void) SCNetworkServiceSetEnabled(service, TRUE);
1295 }
1296 }
1297 }
1298
1299 CFRelease(services);
1300 }
1301 }
1302
1303 switch (c) {
1304 case 0 :
1305 prefsOld = prefsPrivate->prefs;
1306 break;
1307 case 1 :
1308 prefsNew = prefsPrivate->prefs;
1309 break;
1310 }
1311 }
1312
1313 // compare the filtered configurations
1314 ok = _SC_CFEqual(prefsOld, prefsNew);
1315
1316 // clean up
1317 if (prefsOld != NULL) CFRelease(prefsOld);
1318 if (prefsNew != NULL) CFRelease(prefsNew);
1319 prefsPrivate->prefs = prefsSave;
1320 }
1321
1322 if (!ok) {
1323 *status = kSCStatusAccessError;
1324 goto done;
1325 }
1326 }
1327
1328 /* Take a backup of prefs, accessed bit, changed bit to
1329 restore them IFF the commit fails. Pretend as if the
1330 commit never happened!
1331 */
1332 savePrefs = prefsPrivate->prefs;
1333 saveAccessed = prefsPrivate->accessed;
1334 saveChanged = prefsPrivate->changed;
1335
1336 prefsPrivate->prefs = CFDictionaryCreateMutableCopy(NULL, 0, prefsData);
1337 prefsPrivate->accessed = TRUE;
1338 prefsPrivate->changed = TRUE;
1339 saveValid = TRUE;
1340
1341 commit :
1342
1343 ok = SCPreferencesCommitChanges(prefs);
1344 if (ok) {
1345 if (savePrefs != NULL) {
1346 CFRelease(savePrefs);
1347 }
1348 *reply = SCPreferencesGetSignature(prefs);
1349 CFRetain(*reply);
1350 } else {
1351 /* Restore the backup we took earlier */
1352 if (saveValid) {
1353 if (prefsPrivate->prefs != NULL) {
1354 CFRelease(prefsPrivate->prefs);
1355 }
1356
1357 prefsPrivate->prefs = savePrefs;
1358 prefsPrivate->accessed = saveAccessed;
1359 prefsPrivate->changed = saveChanged;
1360 }
1361 *status = SCError();
1362 }
1363
1364 done :
1365
1366 if (prefsData != NULL) CFRelease(prefsData);
1367 return ok;
1368 }
1369
1370
1371 /*
1372 * APPLY
1373 * (in) data = N/A
1374 * (out) status = SCError()
1375 * (out) reply = N/A
1376 */
1377 static Boolean
1378 do_prefs_Apply(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
1379 {
1380 #pragma unused(info)
1381 #pragma unused(data)
1382 #pragma unused(reply)
1383 Boolean ok;
1384 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
1385
1386 if (prefs == NULL) {
1387 return FALSE;
1388 }
1389
1390 ok = SCPreferencesApplyChanges(prefs);
1391 if (!ok) {
1392 *status = SCError();
1393 }
1394
1395 return TRUE;
1396 }
1397
1398
1399 /*
1400 * UNLOCK
1401 * (in) data = N/A
1402 * (out) status = SCError()
1403 * (out) reply = N/A
1404 */
1405 static Boolean
1406 do_prefs_Unlock(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
1407 {
1408 #pragma unused(info)
1409 #pragma unused(data)
1410 #pragma unused(reply)
1411 Boolean ok;
1412 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
1413
1414 if (prefs == NULL) {
1415 return FALSE;
1416 }
1417
1418 ok = SCPreferencesUnlock(prefs);
1419 if (!ok) {
1420 *status = SCError();
1421 }
1422
1423 return TRUE;
1424 }
1425
1426
1427 /*
1428 * CLOSE
1429 * (in) data = N/A
1430 * (out) status = SCError()
1431 * (out) reply = N/A
1432 */
1433 static Boolean
1434 do_prefs_Close(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
1435 {
1436 #pragma unused(info)
1437 #pragma unused(data)
1438 #pragma unused(reply)
1439 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
1440
1441 if (prefs == NULL) {
1442 return FALSE;
1443 }
1444
1445 __SCHelperSessionSetPreferences(session, NULL);
1446 *status = HELPER_STATUS_NO_REPLY;
1447 return TRUE;
1448 }
1449
1450
1451 /*
1452 * SYNCHRONIZE
1453 * (in) data = N/A
1454 * (out) status = kSCStatusOK
1455 * (out) reply = N/A
1456 */
1457 static Boolean
1458 do_prefs_Synchronize(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
1459 {
1460 #pragma unused(info)
1461 #pragma unused(data)
1462 #pragma unused(reply)
1463 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
1464
1465 if (prefs == NULL) {
1466 return FALSE;
1467 }
1468
1469 SCPreferencesSynchronize(prefs);
1470 *status = kSCStatusOK;
1471 return TRUE;
1472 }
1473
1474
1475 #pragma mark -
1476 #pragma mark Process commands
1477
1478
1479 static CFStringRef
1480 sessionName(SCHelperSessionRef session)
1481 {
1482 CFStringRef name = NULL;
1483 SCPreferencesRef prefs;
1484
1485 prefs = __SCHelperSessionGetPreferences(session);
1486 if (prefs != NULL) {
1487 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
1488
1489 name = prefsPrivate->name;
1490 }
1491
1492 return (name != NULL) ? name : CFSTR("???");
1493 }
1494
1495
1496 static CFStringRef
1497 sessionPrefsID(SCHelperSessionRef session)
1498 {
1499 CFStringRef prefsID;
1500 SCPreferencesPrivateRef prefsPrivate;
1501 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
1502
1503 prefsPrivate = (SCPreferencesPrivateRef)sessionPrivate->prefs;
1504 if ((prefsPrivate != NULL) && (prefsPrivate->prefsID != NULL)) {
1505 prefsID = prefsPrivate->prefsID;
1506 } else {
1507 prefsID = PREFS_DEFAULT_CONFIG;
1508 }
1509
1510 return prefsID;
1511 }
1512
1513
1514 static CFTypeRef
1515 copyEntitlement(SCHelperSessionRef session, CFStringRef entitlement)
1516 {
1517 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
1518 SecTaskRef task;
1519 CFTypeRef value = NULL;
1520
1521 // Create the security task from the audit token
1522 task = SecTaskCreateWithAuditToken(NULL, sessionPrivate->auditToken);
1523 if (task != NULL) {
1524 CFErrorRef error = NULL;
1525
1526 // Get the value for the entitlement
1527 value = SecTaskCopyValueForEntitlement(task, entitlement, &error);
1528 if ((value == NULL) && (error != NULL)) {
1529 CFIndex code = CFErrorGetCode(error);
1530 CFStringRef domain = CFErrorGetDomain(error);
1531
1532 if (!CFEqual(domain, kCFErrorDomainMach) ||
1533 ((code != kIOReturnInvalid) && (code != kIOReturnNotFound))) {
1534 // if unexpected error
1535 SC_log(LOG_NOTICE, "SecTaskCopyValueForEntitlement(,\"%@\",) failed, error = %@ : %@",
1536 entitlement,
1537 error,
1538 sessionName(session));
1539 }
1540 CFRelease(error);
1541 }
1542
1543 CFRelease(task);
1544 } else {
1545 SC_log(LOG_NOTICE, "SecTaskCreateWithAuditToken() failed: %@",
1546 sessionName(session));
1547 }
1548
1549 return value;
1550 }
1551
1552
1553 #if !TARGET_OS_IPHONE
1554 static Boolean
1555 isSetChange(SCHelperSessionRef session)
1556 {
1557 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
1558
1559 if (sessionPrivate->isSetChange == UNKNOWN) {
1560 CFBooleanRef bVal = NULL;
1561 CFStringRef prefsID;
1562 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)sessionPrivate->prefs;
1563 Boolean setFilter = FALSE;
1564
1565 prefsID = sessionPrefsID(session);
1566 if (CFEqual(prefsID, PREFS_DEFAULT_CONFIG) &&
1567 isA_CFDictionary(prefsPrivate->options) &&
1568 CFDictionaryGetValueIfPresent(prefsPrivate->options,
1569 kSCPreferencesOptionChangeNetworkSet,
1570 (const void **)&bVal) &&
1571 isA_CFBoolean(bVal) &&
1572 CFBooleanGetValue(bVal)) {
1573 setFilter = TRUE;
1574 }
1575
1576 // establish network set (location) filter
1577 __SCHelperSessionSetNetworkSetFilter(session, setFilter);
1578 }
1579
1580 return (sessionPrivate->isSetChange == YES) ? TRUE : FALSE;
1581 }
1582 #endif // !TARGET_OS_IPHONE
1583
1584
1585 static Boolean
1586 isVPNChange(SCHelperSessionRef session)
1587 {
1588 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
1589
1590 if (sessionPrivate->isVPNChange == UNKNOWN) {
1591 CFArrayRef entitlement;
1592 Boolean vpnChange = FALSE;
1593 CFArrayRef vpnTypes = NULL;
1594
1595 entitlement = copyEntitlement(session, kSCVPNFilterEntitlementName);
1596 if (entitlement != NULL) {
1597 if (isA_CFArray(entitlement)) {
1598 CFStringRef prefsID;
1599
1600 prefsID = sessionPrefsID(session);
1601 if (CFEqual(prefsID, PREFS_DEFAULT_CONFIG)) {
1602 // save the VPN type identifiers
1603 vpnTypes = CFRetain(entitlement);
1604
1605 // grant an exception
1606 vpnChange = TRUE;
1607 } else if (CFStringHasPrefix(prefsID, CFSTR("VPN-")) &&
1608 CFStringHasSuffix(prefsID, CFSTR(".plist"))) {
1609 CFRange range;
1610 CFStringRef vpnID;
1611
1612 range.location = sizeof("VPN-") - 1;
1613 range.length = CFStringGetLength(prefsID)
1614 - (sizeof("VPN-") - 1) // trim VPN-
1615 - (sizeof(".plist") - 1); // trim .plist
1616 vpnID = CFStringCreateWithSubstring(NULL, prefsID, range);
1617 if (CFArrayContainsValue(entitlement,
1618 CFRangeMake(0, CFArrayGetCount(entitlement)),
1619 vpnID)) {
1620 // grant an exception
1621 vpnChange = TRUE;
1622 }
1623 CFRelease(vpnID);
1624 }
1625 }
1626
1627 CFRelease(entitlement);
1628 }
1629
1630 __SCHelperSessionSetVPNFilter(session, vpnChange, vpnTypes);
1631 if (vpnTypes != NULL) {
1632 CFRelease(vpnTypes);
1633 }
1634 }
1635
1636 return (sessionPrivate->isVPNChange == YES) ? TRUE : FALSE;
1637 }
1638
1639
1640 static Boolean
1641 checkEntitlement(SCHelperSessionRef session, CFStringRef prefsID, CFStringRef entitlement_name)
1642 {
1643 CFArrayRef entitlement;
1644 Boolean hasEntitlement = FALSE;
1645
1646 entitlement = copyEntitlement(session, entitlement_name);
1647 if (entitlement != NULL) {
1648 if (isA_CFArray(entitlement)) {
1649 if (CFArrayContainsValue(entitlement,
1650 CFRangeMake(0, CFArrayGetCount(entitlement)),
1651 prefsID)) {
1652 // if client DOES have entitlement
1653 hasEntitlement = TRUE;
1654 }
1655 } else {
1656 SC_log(LOG_NOTICE, "hasAuthorization() session=%@: entitlement=%@: not valid",
1657 sessionName(session),
1658 entitlement_name);
1659 }
1660
1661 CFRelease(entitlement);
1662 }
1663
1664 #if TARGET_OS_IPHONE
1665 // make an exception for VPN configuration management
1666 if (!hasEntitlement) {
1667 if (isVPNChange(session)) {
1668 // grant a "filtered" exception
1669 hasEntitlement = TRUE;
1670 }
1671 }
1672 #endif // TARGET_OS_IPHONE
1673
1674 return hasEntitlement;
1675 }
1676
1677
1678 static Boolean
1679 hasAuthorization(SCHelperSessionRef session, Boolean needWrite)
1680 {
1681 AuthorizationRef authorization = __SCHelperSessionGetAuthorization(session);
1682 CFStringRef prefsID;
1683 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
1684
1685 if (authorization == NULL) {
1686 return FALSE;
1687 }
1688
1689 #if !TARGET_OS_IPHONE
1690 if (!__SCHelperSessionUseEntitlement(session)) {
1691 AuthorizationFlags flags;
1692 AuthorizationItem items[1];
1693 AuthorizationRights rights;
1694 OSStatus status;
1695
1696 if (isSetChange(session)) {
1697 items[0].name = kSCPreferencesAuthorizationRight_network_set;
1698 items[0].value = NULL;
1699 items[0].valueLength = 0;
1700 items[0].flags = 0;
1701 } else if (isVPNChange(session)) {
1702 items[0].name = kSCPreferencesAuthorizationRight_write;
1703 items[0].value = NULL;
1704 items[0].valueLength = 0;
1705 items[0].flags = 0;
1706 } else {
1707 items[0].name = kSCPreferencesAuthorizationRight_write;
1708 items[0].value = NULL;
1709 items[0].valueLength = 0;
1710 items[0].flags = 0;
1711 }
1712
1713 rights.count = sizeof(items) / sizeof(items[0]);
1714 rights.items = items;
1715
1716 flags = kAuthorizationFlagDefaults;
1717 flags |= kAuthorizationFlagInteractionAllowed;
1718 flags |= kAuthorizationFlagExtendRights;
1719 // flags |= kAuthorizationFlagPartialRights;
1720 // flags |= kAuthorizationFlagPreAuthorize;
1721
1722 status = AuthorizationCopyRights(authorization,
1723 &rights,
1724 kAuthorizationEmptyEnvironment,
1725 flags,
1726 NULL);
1727 if (status != errAuthorizationSuccess) {
1728 SC_log(LOG_INFO, "AuthorizationCopyRights() failed: status = %d",
1729 (int)status);
1730 return FALSE;
1731 }
1732
1733 return TRUE;
1734 }
1735 #endif // !TARGET_OS_IPHONE
1736
1737 prefsID = sessionPrefsID(session);
1738
1739 if (sessionPrivate->callerWriteAccess == UNKNOWN) {
1740 if (checkEntitlement(session, prefsID, kSCWriteEntitlementName)) {
1741 sessionPrivate->callerWriteAccess = YES;
1742 sessionPrivate->callerReadAccess = YES; // implied
1743 } else {
1744 sessionPrivate->callerWriteAccess = NO;
1745 }
1746 }
1747
1748 if (needWrite) {
1749 if (sessionPrivate->callerWriteAccess == YES) {
1750 return TRUE;
1751 } else {
1752 SC_log(LOG_NOTICE, "SCPreferences write access to \"%@\" denied, no entitlement for \"%@\"",
1753 prefsID,
1754 sessionName(session));
1755 return FALSE;
1756 }
1757 }
1758
1759 if (sessionPrivate->callerReadAccess == UNKNOWN) {
1760 if (checkEntitlement(session, prefsID, kSCReadEntitlementName)) {
1761 sessionPrivate->callerReadAccess = YES;
1762 } else {
1763 sessionPrivate->callerWriteAccess = NO;
1764 }
1765 }
1766
1767 if (sessionPrivate->callerReadAccess == YES) {
1768 return TRUE;
1769 }
1770
1771 SC_log(LOG_NOTICE, "SCPreferences access to \"%@\" denied, no entitlement for \"%@\"",
1772 prefsID,
1773 sessionName(session));
1774 return FALSE;
1775 }
1776
1777
1778 typedef Boolean (*helperFunction) (SCHelperSessionRef session,
1779 void *info,
1780 CFDataRef data,
1781 uint32_t *status,
1782 CFDataRef *reply);
1783
1784
1785 static const struct helper {
1786 int command;
1787 const char *commandName;
1788 Boolean needsAuthorization;
1789 Boolean needsWrite;
1790 helperFunction func;
1791 void *info;
1792 } helpers[] = {
1793 { SCHELPER_MSG_AUTH, "AUTH", FALSE, FALSE, do_Auth , NULL },
1794
1795 { SCHELPER_MSG_PREFS_OPEN, "PREFS open", FALSE, FALSE, do_prefs_Open , NULL },
1796 { SCHELPER_MSG_PREFS_ACCESS, "PREFS access", TRUE, FALSE, do_prefs_Access , NULL },
1797 { SCHELPER_MSG_PREFS_LOCK, "PREFS lock", TRUE, TRUE, do_prefs_Lock , (void *)FALSE },
1798 { SCHELPER_MSG_PREFS_LOCKWAIT, "PREFS lock/wait", TRUE, TRUE, do_prefs_Lock , (void *)TRUE },
1799 { SCHELPER_MSG_PREFS_COMMIT, "PREFS commit", TRUE, TRUE, do_prefs_Commit , NULL },
1800 { SCHELPER_MSG_PREFS_APPLY, "PREFS apply", TRUE, TRUE, do_prefs_Apply , NULL },
1801 { SCHELPER_MSG_PREFS_UNLOCK, "PREFS unlock", FALSE, TRUE, do_prefs_Unlock , NULL },
1802 { SCHELPER_MSG_PREFS_CLOSE, "PREFS close", FALSE, FALSE, do_prefs_Close , NULL },
1803 { SCHELPER_MSG_PREFS_SYNCHRONIZE, "PREFS synchronize", FALSE, FALSE, do_prefs_Synchronize , NULL },
1804
1805 { SCHELPER_MSG_INTERFACE_REFRESH, "INTERFACE refresh", TRUE, TRUE, do_interface_refresh , NULL },
1806
1807 #if !TARGET_OS_IPHONE
1808 { SCHELPER_MSG_KEYCHAIN_COPY, "KEYCHAIN copy", TRUE, FALSE, do_keychain_copy , NULL },
1809 { SCHELPER_MSG_KEYCHAIN_EXISTS, "KEYCHAIN exists", TRUE, FALSE, do_keychain_exists , NULL },
1810 { SCHELPER_MSG_KEYCHAIN_REMOVE, "KEYCHAIN remove", TRUE, TRUE, do_keychain_remove , NULL },
1811 { SCHELPER_MSG_KEYCHAIN_SET, "KEYCHAIN set", TRUE, TRUE, do_keychain_set , NULL },
1812 #endif // !TARGET_OS_IPHONE
1813
1814 { SCHELPER_MSG_EXIT, "EXIT", FALSE, FALSE, do_Exit , NULL }
1815 };
1816 #define nHELPERS (sizeof(helpers)/sizeof(struct helper))
1817
1818
1819 static int
1820 findCommand(int command)
1821 {
1822 for (int i = 0; i < (int)nHELPERS; i++) {
1823 if (helpers[i].command == command) {
1824 return i;
1825 }
1826 }
1827
1828 return -1;
1829 }
1830
1831
1832 static void *
1833 newHelper(void *arg)
1834 {
1835 int ret;
1836 CFRunLoopSourceRef rls = NULL;
1837 SCHelperSessionRef session = (SCHelperSessionRef)arg;
1838 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
1839
1840 assert(session != NULL);
1841 assert(sessionPrivate->mp != NULL);
1842
1843 __SCHelperSessionSetThreadName(session);
1844
1845 ret = pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0);
1846 if (ret != 0) {
1847 SC_log(LOG_ERR, "pthread_set_qos_class_self_np() failed: %s", strerror(errno));
1848 }
1849
1850 rls = CFMachPortCreateRunLoopSource(NULL, sessionPrivate->mp, 0);
1851 CFRelease(sessionPrivate->mp);
1852
1853 if (rls != NULL) {
1854 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
1855 CFRelease(rls);
1856
1857 SC_log(LOG_INFO, "%p : start", session);
1858 CFRunLoopRun();
1859 SC_log(LOG_INFO, "%p : stop", session);
1860 }
1861
1862 return NULL;
1863 }
1864
1865
1866 #pragma mark -
1867 #pragma mark Main loop
1868
1869
1870 // MiG generated externals and functions
1871 extern struct mig_subsystem _helper_subsystem;
1872 extern boolean_t helper_server(mach_msg_header_t *, mach_msg_header_t *);
1873
1874
1875 static
1876 boolean_t
1877 notify_server(mach_msg_header_t *request, mach_msg_header_t *reply)
1878 {
1879 mach_no_senders_notification_t *Request = (mach_no_senders_notification_t *)request;
1880 mig_reply_error_t *Reply = (mig_reply_error_t *)reply;
1881
1882 reply->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request->msgh_bits), 0);
1883 reply->msgh_remote_port = request->msgh_remote_port;
1884 reply->msgh_size = sizeof(mig_reply_error_t); /* Minimal size: update as needed */
1885 reply->msgh_local_port = MACH_PORT_NULL;
1886 reply->msgh_id = request->msgh_id + 100;
1887
1888 if ((Request->not_header.msgh_id > MACH_NOTIFY_LAST) ||
1889 (Request->not_header.msgh_id < MACH_NOTIFY_FIRST)) {
1890 Reply->NDR = NDR_record;
1891 Reply->RetCode = MIG_BAD_ID;
1892 return FALSE; /* if this is not a notification message */
1893 }
1894
1895 switch (Request->not_header.msgh_id) {
1896 case MACH_NOTIFY_NO_SENDERS : {
1897 SCHelperSessionRef session;
1898
1899 __MACH_PORT_DEBUG(TRUE, "*** notify_server MACH_NOTIFY_NO_SENDERS", Request->not_header.msgh_local_port);
1900
1901 // clean up session
1902 session = __SCHelperSessionFindWithPort(Request->not_header.msgh_local_port);
1903 if (session != NULL) {
1904 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
1905
1906 // release CFMachPort *and* SCHelperSession
1907 CFMachPortInvalidate(sessionPrivate->mp);
1908 }
1909
1910 __MACH_PORT_DEBUG(TRUE, "*** notify_server after invalidate", Request->not_header.msgh_local_port);
1911
1912 // and, lastly, remove our receive right.
1913 (void) mach_port_mod_refs(mach_task_self(),
1914 Request->not_header.msgh_local_port,
1915 MACH_PORT_RIGHT_RECEIVE, -1);
1916
1917 Reply->Head.msgh_bits = 0;
1918 Reply->Head.msgh_remote_port = MACH_PORT_NULL;
1919 Reply->RetCode = KERN_SUCCESS;
1920 return TRUE;
1921 }
1922
1923 default :
1924 break;
1925 }
1926
1927 SC_log(LOG_NOTICE, "HELP!, Received notification: port=%d, msgh_id=%d",
1928 Request->not_header.msgh_local_port,
1929 Request->not_header.msgh_id);
1930
1931 Reply->NDR = NDR_record;
1932 Reply->RetCode = MIG_BAD_ID;
1933 return FALSE; /* if this is not a notification we are handling */
1934 }
1935
1936
1937 __private_extern__
1938 boolean_t
1939 helper_demux(mach_msg_header_t *request, mach_msg_header_t *reply)
1940 {
1941 Boolean processed = FALSE;
1942
1943 /*
1944 * (attempt to) process SCHelper requests.
1945 */
1946 processed = helper_server(request, reply);
1947 if (processed) {
1948 return TRUE;
1949 }
1950
1951 /*
1952 * (attempt to) process (NO MORE SENDERS) notification messages.
1953 */
1954 processed = notify_server(request, reply);
1955 if (processed) {
1956 return TRUE;
1957 }
1958
1959 /*
1960 * unknown message ID, log and return an error.
1961 */
1962 SC_log(LOG_NOTICE, "helper_demux(): unknown message ID (%d) received", request->msgh_id);
1963 reply->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request->msgh_bits), 0);
1964 reply->msgh_remote_port = request->msgh_remote_port;
1965 reply->msgh_size = sizeof(mig_reply_error_t); /* Minimal size */
1966 reply->msgh_local_port = MACH_PORT_NULL;
1967 reply->msgh_id = request->msgh_id + 100;
1968 ((mig_reply_error_t *)reply)->NDR = NDR_record;
1969 ((mig_reply_error_t *)reply)->RetCode = MIG_BAD_ID;
1970
1971 return FALSE;
1972 }
1973
1974
1975 #define MACH_MSG_BUFFER_SIZE 128
1976
1977
1978 static void
1979 helperCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
1980 {
1981 #pragma unused(port)
1982 #pragma unused(size)
1983 #pragma unused(info)
1984 mig_reply_error_t * bufRequest = msg;
1985 uint32_t bufReply_q[MACH_MSG_BUFFER_SIZE/sizeof(uint32_t)];
1986 mig_reply_error_t * bufReply = (mig_reply_error_t *)bufReply_q;
1987 static size_t bufSize = 0;
1988 mach_msg_return_t mr;
1989 int options;
1990
1991 if (bufSize == 0) {
1992 // get max size for MiG reply buffers
1993 bufSize = _helper_subsystem.maxsize;
1994
1995 // check if our on-the-stack reply buffer will be big enough
1996 if (bufSize > sizeof(bufReply_q)) {
1997 SC_log(LOG_NOTICE, "buffer size should be increased > %d",
1998 _helper_subsystem.maxsize);
1999 }
2000 }
2001
2002 if (bufSize > sizeof(bufReply_q)) {
2003 bufReply = CFAllocatorAllocate(NULL, _helper_subsystem.maxsize, 0);
2004 }
2005 bufReply->RetCode = 0;
2006
2007 /* we have a request message */
2008 (void) helper_demux(&bufRequest->Head, &bufReply->Head);
2009
2010 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
2011 if (bufReply->RetCode == MIG_NO_REPLY) {
2012 bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
2013 } else if ((bufReply->RetCode != KERN_SUCCESS) &&
2014 (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
2015 /*
2016 * destroy the request - but not the reply port
2017 */
2018 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
2019 mach_msg_destroy(&bufRequest->Head);
2020 }
2021 }
2022
2023 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
2024 /*
2025 * send reply.
2026 *
2027 * We don't want to block indefinitely because the client
2028 * isn't receiving messages from the reply port.
2029 * If we have a send-once right for the reply port, then
2030 * this isn't a concern because the send won't block.
2031 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
2032 * To avoid falling off the kernel's fast RPC path unnecessarily,
2033 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
2034 */
2035
2036 options = MACH_SEND_MSG;
2037 if (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) != MACH_MSG_TYPE_MOVE_SEND_ONCE) {
2038 options |= MACH_SEND_TIMEOUT;
2039 }
2040 mr = mach_msg(&bufReply->Head, /* msg */
2041 options, /* option */
2042 bufReply->Head.msgh_size, /* send_size */
2043 0, /* rcv_size */
2044 MACH_PORT_NULL, /* rcv_name */
2045 MACH_MSG_TIMEOUT_NONE, /* timeout */
2046 MACH_PORT_NULL); /* notify */
2047
2048 /* Has a message error occurred? */
2049 switch (mr) {
2050 case MACH_SEND_INVALID_DEST:
2051 case MACH_SEND_TIMED_OUT:
2052 break;
2053 default :
2054 /* Includes success case. */
2055 goto done;
2056 }
2057 }
2058
2059 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) {
2060 mach_msg_destroy(&bufReply->Head);
2061 }
2062
2063 done :
2064
2065 if (bufReply != (mig_reply_error_t *)bufReply_q)
2066 CFAllocatorDeallocate(NULL, bufReply);
2067
2068 return;
2069 }
2070
2071
2072 static CFStringRef
2073 initMPCopyDescription(const void *info)
2074 {
2075 #pragma unused(info)
2076 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SCHelper MP>"));
2077 }
2078
2079
2080 __private_extern__
2081 kern_return_t
2082 _helperinit(mach_port_t server,
2083 mach_port_t *newSession,
2084 uint32_t *status,
2085 audit_token_t audit_token)
2086 {
2087 CFMachPortContext context = { 0
2088 , NULL
2089 , CFRetain
2090 , CFRelease
2091 , initMPCopyDescription
2092 };
2093 kern_return_t kr;
2094 mach_port_t oldNotify;
2095 SCHelperSessionRef session;
2096 SCHelperSessionPrivateRef sessionPrivate;
2097 pthread_attr_t tattr;
2098 pthread_t tid;
2099
2100 *newSession = MACH_PORT_NULL;
2101
2102 session = __SCHelperSessionFindWithPort(server);
2103 if (session != NULL) {
2104 #ifdef DEBUG
2105 SC_log(LOG_DEBUG, "session is already open");
2106 #endif /* DEBUG */
2107 *status = kSCStatusFailed; /* you can't re-open an "open" session */
2108 return KERN_SUCCESS;
2109 }
2110
2111 session = __SCHelperSessionCreate(NULL);
2112 assert(session != NULL);
2113 sessionPrivate = (SCHelperSessionPrivateRef)session;
2114
2115 // create per-session port
2116 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sessionPrivate->port);
2117 if (kr != KERN_SUCCESS) {
2118 SC_log(LOG_ERR, "mach_port_allocate() failed: %s", mach_error_string(kr));
2119 *status = kr;
2120 goto done;
2121 }
2122
2123 *newSession = sessionPrivate->port;
2124
2125 (void) mach_port_set_attributes(mach_task_self(),
2126 *newSession,
2127 MACH_PORT_IMPORTANCE_RECEIVER,
2128 NULL,
2129 0);
2130
2131 //
2132 // Note: we create the CFMachPort *before* we insert a send
2133 // right present to ensure that CF does not establish
2134 // its dead name notification.
2135 //
2136 context.info = (void *)session;
2137 sessionPrivate->mp = _SC_CFMachPortCreateWithPort("SCHelper/session",
2138 *newSession,
2139 helperCallback,
2140 &context);
2141
2142 /* Request a notification when/if the client dies */
2143 kr = mach_port_request_notification(mach_task_self(),
2144 *newSession,
2145 MACH_NOTIFY_NO_SENDERS,
2146 1,
2147 *newSession,
2148 MACH_MSG_TYPE_MAKE_SEND_ONCE,
2149 &oldNotify);
2150 if (kr != KERN_SUCCESS) {
2151 SC_log(LOG_NOTICE, "mach_port_request_notification() failed: %s", mach_error_string(kr));
2152
2153 // clean up CFMachPort, mach port rights
2154 CFMachPortInvalidate(sessionPrivate->mp);
2155 CFRelease(sessionPrivate->mp);
2156 sessionPrivate->mp = NULL;
2157 (void) mach_port_mod_refs(mach_task_self(), *newSession, MACH_PORT_RIGHT_RECEIVE, -1);
2158 *newSession = MACH_PORT_NULL;
2159 *status = kSCStatusFailed;
2160 goto done;
2161 }
2162
2163 if (oldNotify != MACH_PORT_NULL) {
2164 SC_log(LOG_NOTICE, "oldNotify != MACH_PORT_NULL");
2165 }
2166
2167 // add send right (that will be passed back to the client)
2168 (void) mach_port_insert_right(mach_task_self(),
2169 *newSession,
2170 *newSession,
2171 MACH_MSG_TYPE_MAKE_SEND);
2172
2173 // save audit token
2174 sessionPrivate->auditToken = audit_token;
2175
2176 //
2177 // Note: at this time we should be holding ONE send right and
2178 // ONE receive right to the server. The send right is
2179 // moved to the caller.
2180 //
2181
2182 // start per-session thread
2183 pthread_attr_init(&tattr);
2184 pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
2185 pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
2186 // pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
2187 pthread_create(&tid, &tattr, newHelper, (void *)session);
2188 pthread_attr_destroy(&tattr);
2189
2190 *status = kSCStatusOK;
2191
2192 done :
2193
2194 CFRelease(session);
2195 return KERN_SUCCESS;
2196 }
2197
2198
2199 __private_extern__
2200 kern_return_t
2201 _helperexec(mach_port_t server,
2202 uint32_t msgID,
2203 xmlData_t dataRef, /* raw XML bytes */
2204 mach_msg_type_number_t dataLen,
2205 xmlData_t traceRef, /* raw XML bytes */
2206 mach_msg_type_number_t traceLen,
2207 uint32_t *status,
2208 xmlDataOut_t *replyRef, /* raw XML bytes */
2209 mach_msg_type_number_t *replyLen)
2210 {
2211 CFStringRef backtrace = NULL;
2212 CFDataRef data = NULL;
2213 int i;
2214 CFDataRef reply = NULL;
2215 SCHelperSessionRef session;
2216
2217 *status = kSCStatusOK;
2218 *replyRef = NULL;
2219 *replyLen = 0;
2220
2221 if ((dataRef != NULL) && (dataLen > 0)) {
2222 if (!_SCUnserializeData(&data, (void *)dataRef, dataLen)) {
2223 *status = SCError();
2224 }
2225 }
2226
2227 if ((traceRef != NULL) && (traceLen > 0)) {
2228 if (!_SCUnserializeString(&backtrace, NULL, (void *)traceRef, traceLen)) {
2229 *status = SCError();
2230 }
2231 }
2232
2233 if (*status != kSCStatusOK) {
2234 goto done;
2235 }
2236
2237 session = __SCHelperSessionFindWithPort(server);
2238 if (session == NULL) {
2239 *status = kSCStatusFailed; /* you must have an open session to play */
2240 goto done;
2241 }
2242
2243 i = findCommand(msgID);
2244 if (i == -1) {
2245 SC_log(LOG_NOTICE, "received unknown command : %u", msgID);
2246 *status = kSCStatusInvalidArgument;
2247 goto done;
2248 }
2249
2250 SC_log(LOG_INFO, "%p : processing command \"%s\"%s",
2251 session,
2252 helpers[i].commandName,
2253 (data != NULL) ? " w/data" : "");
2254
2255 if (helpers[i].needsAuthorization &&
2256 !hasAuthorization(session, helpers[i].needsWrite)) {
2257 SC_log(LOG_INFO, "%p : command \"%s\" : not authorized",
2258 session,
2259 helpers[i].commandName);
2260 *status = kSCStatusAccessError;
2261 }
2262
2263 if (*status == kSCStatusOK) {
2264 if (backtrace != NULL) {
2265 __SCHelperSessionAddBacktrace(session, backtrace, helpers[i].commandName);
2266 }
2267 (*helpers[i].func)(session, helpers[i].info, data, status, &reply);
2268 }
2269
2270 if ((*status != HELPER_STATUS_NO_REPLY) || (reply != NULL)) {
2271 Boolean ok;
2272
2273 SC_log(LOG_INFO, "%p : sending status %u%s",
2274 session,
2275 *status,
2276 (reply != NULL) ? " w/reply" : "");
2277
2278 /* serialize the data */
2279 if (reply != NULL) {
2280 CFIndex len;
2281
2282 ok = _SCSerializeData(reply, (void **)replyRef, &len);
2283 *replyLen = (mach_msg_type_number_t)len;
2284 if (!ok) {
2285 *status = SCError();
2286 goto done;
2287 }
2288 }
2289 }
2290
2291 done :
2292
2293 if (data != NULL) CFRelease(data);
2294 if (backtrace != NULL) CFRelease(backtrace);
2295 if (reply != NULL) CFRelease(reply);
2296 return KERN_SUCCESS;
2297 }
2298
2299
2300 static CFStringRef
2301 helperMPCopyDescription(const void *info)
2302 {
2303 #pragma unused(info)
2304 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<main SCHelper MP>"));
2305 }
2306
2307
2308 static int
2309 init_MiG(const char *service_name, int *n_listeners)
2310 {
2311 CFMachPortContext context = { 0
2312 , (void *)1
2313 , NULL
2314 , NULL
2315 , helperMPCopyDescription
2316 };
2317 kern_return_t kr;
2318 CFMachPortRef mp;
2319 CFRunLoopSourceRef rls;
2320 mach_port_t service_port = MACH_PORT_NULL;
2321
2322 kr = bootstrap_check_in(bootstrap_port, service_name, &service_port);
2323 if (kr != BOOTSTRAP_SUCCESS) {
2324 SC_log(LOG_NOTICE, "bootstrap_check_in() failed: %s",
2325 bootstrap_strerror(kr));
2326 return 1;
2327 }
2328
2329 // add a run loop source to listen for new requests
2330 mp = _SC_CFMachPortCreateWithPort("SCHelper/server",
2331 service_port,
2332 helperCallback,
2333 &context);
2334 rls = CFMachPortCreateRunLoopSource(NULL, mp, 0);
2335 CFRelease(mp);
2336 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
2337 CFRelease(rls);
2338
2339 *n_listeners = *n_listeners + 1;
2340
2341 return 0;
2342 }
2343
2344
2345 #pragma mark -
2346 #pragma mark Main
2347
2348
2349 static const struct option longopts[] = {
2350 { "debug", no_argument, 0, 'd' },
2351 { 0, 0, 0, 0 }
2352 };
2353
2354
2355 int
2356 main(int argc, char **argv)
2357 {
2358 Boolean done = FALSE;
2359 int err = 0;
2360 int gen_reported = 0;
2361 int idle = 0;
2362 int n_listeners = 0;
2363 // extern int optind;
2364 int opt;
2365 int opti;
2366 int ret;
2367
2368 openlog("SCHelper", LOG_CONS|LOG_PID, LOG_DAEMON);
2369
2370 // process any arguments
2371 while ((opt = getopt_long(argc, argv, "d", longopts, &opti)) != -1) {
2372 switch(opt) {
2373 case 'd':
2374 debug = TRUE;
2375 break;
2376 case 0 :
2377 // if (strcmp(longopts[opti].name, "debug") == 1) {
2378 // }
2379 break;
2380 case '?':
2381 default :
2382 SC_log(LOG_NOTICE, "ignoring unknown or ambiguous command line option");
2383 break;
2384 }
2385 }
2386 // argc -= optind;
2387 // argv += optind;
2388
2389 if (geteuid() != 0) {
2390 SC_log(LOG_NOTICE, "%s", strerror(EACCES));
2391 exit(EACCES);
2392 }
2393
2394 main_runLoop = CFRunLoopGetCurrent();
2395
2396 err = init_MiG("com.apple.SystemConfiguration.helper", &n_listeners);
2397 if ((err != 0) || (n_listeners == 0)) {
2398 exit(err);
2399 }
2400
2401 pthread_setname_np("SCHelper main thread");
2402
2403 ret = pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0);
2404 if (ret != 0) {
2405 SC_log(LOG_ERR, "pthread_set_qos_class_self_np() failed: %s", strerror(errno));
2406 }
2407
2408 while (!done) {
2409 SInt32 rlStatus;
2410 int gen_current;
2411
2412 rlStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 15.0, TRUE);
2413
2414 pthread_mutex_lock(&sessions_lock);
2415
2416 if (sessions != NULL) {
2417 if (rlStatus == kCFRunLoopRunTimedOut) {
2418 idle++;
2419
2420 if ((CFSetGetCount(sessions) == 0) && (sessions_closed == 0)) {
2421 // if we don't have any open sessions and no
2422 // sessions have recently been closed
2423 done = TRUE;
2424 }
2425 } else {
2426 idle = 0;
2427 }
2428 }
2429 gen_current = sessions_generation;
2430 sessions_closed = 0;
2431
2432 if (!done && (idle >= (2 * 60 / 15))) {
2433 if (gen_reported != gen_current) {
2434 FILE *logFile = NULL;
2435
2436 SC_log(LOG_INFO, "active (but IDLE) sessions");
2437 CFSetApplyFunction(sessions, __SCHelperSessionLog, (void *)&logFile);
2438 gen_reported = gen_current;
2439
2440 if (logFile != NULL) {
2441 (void) fclose(logFile);
2442 }
2443 }
2444 idle = 0;
2445 }
2446
2447 pthread_mutex_unlock(&sessions_lock);
2448 }
2449
2450 exit(EX_OK);
2451 }