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