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