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