2 * Copyright (c) 2005-2011 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
30 #include <bsm/libbsm.h>
31 #include <sys/types.h>
34 //#define DEBUG_MACH_PORT_ALLOCATIONS
36 #include <CoreFoundation/CoreFoundation.h>
37 #include <CoreFoundation/CFRuntime.h>
38 #include <SystemConfiguration/SystemConfiguration.h>
39 #include <SystemConfiguration/SCPrivate.h>
40 #include <SystemConfiguration/SCValidation.h>
42 #include "SCPreferencesInternal.h"
43 #include "SCHelper_client.h"
44 #include "helper_types.h"
48 #pragma mark SCHelper session managment
54 // entitlement used to control write access to a given "prefsID"
56 #define kSCWriteEntitlementName CFSTR("com.apple.SystemConfiguration.SCPreferences-write-access")
59 // entitlement used to allow limited [VPN configuration] write access to the "preferences.plist"
61 #define kSCVPNFilterEntitlementName CFSTR("com.apple.networking.vpn.configuration")
63 #endif // TARGET_OS_IPHONE
66 typedef enum { NO
= 0, YES
, UNKNOWN
} lazyBoolean
;
68 typedef const struct __SCHelperSession
* SCHelperSessionRef
;
72 // base CFType information
79 AuthorizationRef authorization
;
85 // Mach security audit trailer for evaluating credentials
86 audit_token_t auditToken
;
88 // write access entitlement associated with this session
89 lazyBoolean callerWriteAccess
;
91 // VPN configuration filtering
95 SCPreferencesRef prefs
;
97 } SCHelperSessionPrivate
, *SCHelperSessionPrivateRef
;
100 static CFStringRef
__SCHelperSessionCopyDescription (CFTypeRef cf
);
101 static void __SCHelperSessionDeallocate (CFTypeRef cf
);
104 static CFTypeID __kSCHelperSessionTypeID
= _kCFRuntimeNotATypeID
;
105 static Boolean debug
= FALSE
;
106 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
107 static CFRunLoopRef main_runLoop
= NULL
;
108 static CFMutableSetRef sessions
= NULL
;
109 static int sessions_closed
= 0; // count of sessions recently closed
110 static int sessions_generation
= 0;
111 static pthread_mutex_t sessions_lock
= PTHREAD_MUTEX_INITIALIZER
;
117 static AuthorizationRef
118 __SCHelperSessionGetAuthorization(SCHelperSessionRef session
)
120 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
122 return sessionPrivate
->authorization
;
127 __SCHelperSessionSetAuthorization(SCHelperSessionRef session
, CFTypeRef authorizationData
)
130 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
132 pthread_mutex_lock(&sessionPrivate
->lock
);
134 #if !TARGET_OS_IPHONE
135 if (sessionPrivate
->authorization
!= NULL
) {
136 AuthorizationFree(sessionPrivate
->authorization
, kAuthorizationFlagDefaults
);
137 // AuthorizationFree(sessionPrivate->authorization, kAuthorizationFlagDestroyRights);
138 sessionPrivate
->authorization
= NULL
;
141 if (isA_CFData(authorizationData
)) {
142 AuthorizationExternalForm extForm
;
144 if (CFDataGetLength(authorizationData
) == sizeof(extForm
.bytes
)) {
147 bcopy(CFDataGetBytePtr(authorizationData
), extForm
.bytes
, sizeof(extForm
.bytes
));
148 err
= AuthorizationCreateFromExternalForm(&extForm
,
149 &sessionPrivate
->authorization
);
150 if (err
!= errAuthorizationSuccess
) {
152 CFSTR("AuthorizationCreateFromExternalForm() failed: status = %d"),
154 sessionPrivate
->authorization
= NULL
;
159 #else // !TARGET_OS_IPHONE
160 if (sessionPrivate
->authorization
!= NULL
) {
161 CFRelease(sessionPrivate
->authorization
);
162 sessionPrivate
->authorization
= NULL
;
165 if (isA_CFString(authorizationData
)) {
166 sessionPrivate
->authorization
= (void *)CFRetain(authorizationData
);
168 #endif // !TARGET_OS_IPHONE
170 pthread_mutex_unlock(&sessionPrivate
->lock
);
176 static SCPreferencesRef
177 __SCHelperSessionGetPreferences(SCHelperSessionRef session
)
179 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
181 return sessionPrivate
->prefs
;
186 __SCHelperSessionSetThreadName(SCHelperSessionRef session
)
192 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
195 if (sessionPrivate
->mp
== NULL
) {
199 if (sessionPrivate
->prefs
!= NULL
) {
200 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
202 if (prefsPrivate
->name
!= NULL
) {
203 caller
= _SC_cfstring_to_cstring(prefsPrivate
->name
,
206 kCFStringEncodingUTF8
);
209 path
= (prefsPrivate
->newPath
!= NULL
) ? prefsPrivate
->newPath
: prefsPrivate
->path
;
211 path_s
= strrchr(path
, '/');
212 if (path_s
!= NULL
) {
218 if (caller
!= NULL
) {
219 snprintf(name
, sizeof(name
), "SESSION|%p|%s|%s%s",
220 (void *)(uintptr_t)CFMachPortGetPort(sessionPrivate
->mp
),
221 (caller
!= NULL
) ? caller
: "?",
222 (path_s
!= NULL
) ? "*/" : "",
223 (path
!= NULL
) ? path
: "?");
224 CFAllocatorDeallocate(NULL
, caller
);
226 snprintf(name
, sizeof(name
), "SESSION|%p",
227 (void *)(uintptr_t)CFMachPortGetPort(sessionPrivate
->mp
));
230 pthread_setname_np(name
);
237 __SCHelperSessionSetPreferences(SCHelperSessionRef session
, SCPreferencesRef prefs
)
239 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
241 pthread_mutex_lock(&sessionPrivate
->lock
);
246 if (sessionPrivate
->prefs
!= NULL
) {
247 SCLog(debug
, LOG_DEBUG
,
250 CFRelease(sessionPrivate
->prefs
);
253 SCLog(debug
, LOG_DEBUG
,
254 CFSTR("%p : open, prefs = %@"),
258 sessionPrivate
->prefs
= prefs
;
260 __SCHelperSessionSetThreadName(session
);
262 pthread_mutex_unlock(&sessionPrivate
->lock
);
269 __SCHelperSessionGetVPNFilter(SCHelperSessionRef session
)
271 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
273 return sessionPrivate
->vpnFilter
;
278 __SCHelperSessionSetVPNFilter(SCHelperSessionRef session
, CFArrayRef vpnFilter
)
280 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
282 pthread_mutex_lock(&sessionPrivate
->lock
);
284 if (vpnFilter
!= NULL
) {
287 if (sessionPrivate
->vpnFilter
!= NULL
) {
288 CFRelease(sessionPrivate
->vpnFilter
);
290 sessionPrivate
->vpnFilter
= vpnFilter
;
292 pthread_mutex_unlock(&sessionPrivate
->lock
);
299 __SCHelperSessionLog(const void *value
, void *context
)
301 SCHelperSessionRef session
= (SCHelperSessionRef
)value
;
302 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
304 pthread_mutex_lock(&sessionPrivate
->lock
);
306 if ((sessionPrivate
->mp
!= NULL
) && (sessionPrivate
->prefs
!= NULL
)) {
307 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
309 SCLog(TRUE
, LOG_NOTICE
,
310 CFSTR(" %p {port = %p, caller = %@, path = %s%s}"),
312 CFMachPortGetPort(sessionPrivate
->mp
),
314 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
,
315 prefsPrivate
->locked
? ", locked" : "");
318 pthread_mutex_unlock(&sessionPrivate
->lock
);
327 static const CFRuntimeClass __SCHelperSessionClass
= {
329 "SCHelperSession", // className
332 __SCHelperSessionDeallocate
, // dealloc
335 NULL
, // copyFormattingDesc
336 __SCHelperSessionCopyDescription
// copyDebugDesc
341 __SCHelperSessionCopyDescription(CFTypeRef cf
)
343 CFAllocatorRef allocator
= CFGetAllocator(cf
);
344 CFMutableStringRef result
;
345 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)cf
;
347 pthread_mutex_lock(&sessionPrivate
->lock
);
349 result
= CFStringCreateMutable(allocator
, 0);
350 CFStringAppendFormat(result
, NULL
, CFSTR("<SCHelperSession %p [%p]> {"), cf
, allocator
);
351 CFStringAppendFormat(result
, NULL
, CFSTR("authorization = %p"), sessionPrivate
->authorization
);
352 if (sessionPrivate
->mp
!= NULL
) {
353 CFStringAppendFormat(result
, NULL
,
354 CFSTR(", mp = %p (port = %p)"),
356 CFMachPortGetPort(sessionPrivate
->mp
));
358 if (sessionPrivate
->prefs
!= NULL
) {
359 CFStringAppendFormat(result
, NULL
, CFSTR(", prefs = %@"), sessionPrivate
->prefs
);
361 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
363 pthread_mutex_unlock(&sessionPrivate
->lock
);
370 __SCHelperSessionDeallocate(CFTypeRef cf
)
372 SCHelperSessionRef session
= (SCHelperSessionRef
)cf
;
373 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
375 // we're releasing "a" session so take the global lock...
376 pthread_mutex_lock(&sessions_lock
);
379 __SCHelperSessionSetAuthorization(session
, NULL
);
380 __SCHelperSessionSetPreferences (session
, NULL
);
381 __SCHelperSessionSetVPNFilter (session
, NULL
);
382 pthread_mutex_destroy(&sessionPrivate
->lock
);
384 // we no longer need/want to track this session
385 CFSetRemoveValue(sessions
, sessionPrivate
);
386 sessions_generation
++;
389 // release the global lock, wake up the main runloop, all done
390 pthread_mutex_unlock(&sessions_lock
);
391 CFRunLoopWakeUp(main_runLoop
);
398 __SCHelperSessionInitialize(void)
400 __kSCHelperSessionTypeID
= _CFRuntimeRegisterClass(&__SCHelperSessionClass
);
405 static SCHelperSessionRef
406 __SCHelperSessionCreate(CFAllocatorRef allocator
)
408 SCHelperSessionPrivateRef sessionPrivate
;
411 // initialize runtime
412 pthread_once(&initialized
, __SCHelperSessionInitialize
);
415 size
= sizeof(SCHelperSessionPrivate
) - sizeof(CFRuntimeBase
);
416 sessionPrivate
= (SCHelperSessionPrivateRef
)_CFRuntimeCreateInstance(allocator
,
417 __kSCHelperSessionTypeID
,
420 if (sessionPrivate
== NULL
) {
424 if (pthread_mutex_init(&sessionPrivate
->lock
, NULL
) != 0) {
425 SCLog(TRUE
, LOG_ERR
, CFSTR("pthread_mutex_init(): failure to initialize per session lock"));
426 CFRelease(sessionPrivate
);
429 sessionPrivate
->authorization
= NULL
;
430 sessionPrivate
->port
= MACH_PORT_NULL
;
431 sessionPrivate
->mp
= NULL
;
432 sessionPrivate
->callerWriteAccess
= UNKNOWN
;
433 sessionPrivate
->vpnFilter
= NULL
;
434 sessionPrivate
->prefs
= NULL
;
436 // keep track this session
437 pthread_mutex_lock(&sessions_lock
);
438 if (sessions
== NULL
) {
439 const CFSetCallBacks mySetCallBacks
= { 0, NULL
, NULL
, CFCopyDescription
, CFEqual
, CFHash
};
441 // create a non-retaining set
442 sessions
= CFSetCreateMutable(NULL
, 0, &mySetCallBacks
);
444 CFSetAddValue(sessions
, sessionPrivate
);
445 sessions_generation
++;
446 pthread_mutex_unlock(&sessions_lock
);
448 return (SCHelperSessionRef
)sessionPrivate
;
455 static SCHelperSessionRef
456 __SCHelperSessionFindWithPort(mach_port_t port
)
458 SCHelperSessionRef session
= NULL
;
460 // keep track this session
461 pthread_mutex_lock(&sessions_lock
);
462 if (sessions
!= NULL
) {
464 CFIndex n
= CFSetGetCount(sessions
);
465 const void * vals_q
[16];
466 const void ** vals
= vals_q
;
468 if (n
> (CFIndex
)(sizeof(vals_q
) / sizeof(SCHelperSessionRef
)))
469 vals
= CFAllocatorAllocate(NULL
, n
* sizeof(CFStringRef
), 0);
470 CFSetGetValues(sessions
, vals
);
471 for (i
= 0; i
< n
; i
++) {
472 SCHelperSessionPrivateRef sessionPrivate
;
474 sessionPrivate
= (SCHelperSessionPrivateRef
)vals
[i
];
475 if (sessionPrivate
->port
== port
) {
476 session
= (SCHelperSessionRef
)sessionPrivate
;
481 CFAllocatorDeallocate(NULL
, vals
);
483 pthread_mutex_unlock(&sessions_lock
);
496 * (out) status = SCError()
500 do_Exit(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
509 * (in) data = AuthorizationExternalForm
510 * (out) status = OSStatus
514 do_Auth(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
518 #if !TARGET_OS_IPHONE
520 ok
= __SCHelperSessionSetAuthorization(session
, data
);
522 #else //!TARGET_OS_IPHONE
524 CFStringRef authorizationInfo
= NULL
;
526 if ((data
!= NULL
) && !_SCUnserializeString(&authorizationInfo
, data
, NULL
, 0)) {
530 if (!isA_CFString(authorizationInfo
)) {
531 if (authorizationInfo
!= NULL
) CFRelease(authorizationInfo
);
535 ok
= __SCHelperSessionSetAuthorization(session
, authorizationInfo
);
536 if (authorizationInfo
!= NULL
) CFRelease(authorizationInfo
);
538 #endif // !TARGET_OS_IPHONE
540 *status
= ok
? 0 : 1;
545 #if !TARGET_OS_IPHONE
549 * SCHELPER_MSG_KEYCHAIN_COPY
550 * (in) data = unique_id
551 * (out) status = SCError()
552 * (out) reply = password
555 do_keychain_copy(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
558 SCPreferencesRef prefs
;
559 CFStringRef unique_id
= NULL
;
561 if ((data
!= NULL
) && !_SCUnserializeString(&unique_id
, data
, NULL
, 0)) {
565 if (unique_id
!= NULL
) {
566 if (isA_CFString(unique_id
)) {
567 prefs
= __SCHelperSessionGetPreferences(session
);
568 *reply
= _SCPreferencesSystemKeychainPasswordItemCopy(prefs
, unique_id
);
569 if (*reply
== NULL
) {
575 CFRelease(unique_id
);
583 * SCHELPER_MSG_KEYCHAIN_EXISTS
584 * (in) data = unique_id
585 * (out) status = SCError()
589 do_keychain_exists(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
592 SCPreferencesRef prefs
;
593 CFStringRef unique_id
= NULL
;
595 if ((data
!= NULL
) && !_SCUnserializeString(&unique_id
, data
, NULL
, 0)) {
599 if (unique_id
!= NULL
) {
600 if (isA_CFString(unique_id
)) {
601 prefs
= __SCHelperSessionGetPreferences(session
);
602 ok
= _SCPreferencesSystemKeychainPasswordItemExists(prefs
, unique_id
);
608 CFRelease(unique_id
);
616 * SCHELPER_MSG_KEYCHAIN_REMOVE
617 * (in) data = unique_id
618 * (out) status = SCError()
622 do_keychain_remove(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
625 SCPreferencesRef prefs
;
626 CFStringRef unique_id
= NULL
;
628 if ((data
!= NULL
) && !_SCUnserializeString(&unique_id
, data
, NULL
, 0)) {
632 if (unique_id
!= NULL
) {
633 if (isA_CFString(unique_id
)) {
634 prefs
= __SCHelperSessionGetPreferences(session
);
635 ok
= _SCPreferencesSystemKeychainPasswordItemRemove(prefs
, unique_id
);
641 CFRelease(unique_id
);
649 * SCHELPER_MSG_KEYCHAIN_SET
650 * (in) data = options dictionary
651 * (out) status = SCError()
655 do_keychain_set(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
658 CFStringRef description
;
659 CFArrayRef executablePaths
= NULL
;
662 CFDictionaryRef options
= NULL
;
664 SCPreferencesRef prefs
;
665 CFStringRef unique_id
;
667 if ((data
!= NULL
) && !_SCUnserialize((CFPropertyListRef
*)&options
, data
, NULL
, 0)) {
671 if (options
!= NULL
) {
672 if (!isA_CFDictionary(options
)) {
680 if (CFDictionaryGetValueIfPresent(options
,
681 kSCKeychainOptionsAllowedExecutables
,
682 (const void **)&executablePaths
)) {
683 CFMutableArrayRef executableURLs
;
686 CFMutableDictionaryRef newOptions
;
688 executableURLs
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
689 n
= CFArrayGetCount(executablePaths
);
690 for (i
= 0; i
< n
; i
++) {
694 path
= CFArrayGetValueAtIndex(executablePaths
, i
);
695 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
696 CFDataGetBytePtr(path
),
697 CFDataGetLength(path
),
700 CFArrayAppendValue(executableURLs
, url
);
705 newOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, options
);
706 CFDictionarySetValue(newOptions
, kSCKeychainOptionsAllowedExecutables
, executableURLs
);
707 CFRelease(executableURLs
);
710 options
= newOptions
;
713 unique_id
= CFDictionaryGetValue(options
, kSCKeychainOptionsUniqueID
);
714 label
= CFDictionaryGetValue(options
, kSCKeychainOptionsLabel
);
715 description
= CFDictionaryGetValue(options
, kSCKeychainOptionsDescription
);
716 account
= CFDictionaryGetValue(options
, kSCKeychainOptionsAccount
);
717 password
= CFDictionaryGetValue(options
, kSCKeychainOptionsPassword
);
719 prefs
= __SCHelperSessionGetPreferences(session
);
720 ok
= _SCPreferencesSystemKeychainPasswordItemSet(prefs
,
736 #endif // !TARGET_OS_IPHONE
740 * SCHELPER_MSG_INTERFACE_REFRESH
742 * (out) status = SCError()
746 do_interface_refresh(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
748 CFStringRef ifName
= NULL
;
751 if ((data
!= NULL
) && !_SCUnserializeString(&ifName
, data
, NULL
, 0)) {
752 SCLog(TRUE
, LOG_ERR
, CFSTR("interface name not valid"));
756 if (ifName
!= NULL
) {
757 if (isA_CFString(ifName
)) {
758 ok
= _SCNetworkInterfaceForceConfigurationRefresh(ifName
);
768 SCLog(TRUE
, LOG_ERR
, CFSTR("interface name not valid"));
777 * (in) data = prefsID
778 * (out) status = SCError()
782 do_prefs_Open(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
785 CFDictionaryRef options
;
787 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
788 CFDictionaryRef prefsInfo
= NULL
;
790 CFStringRef prefsName
;
791 CFStringRef proc_name
;
797 if ((data
!= NULL
) && !_SCUnserialize((CFPropertyListRef
*)&prefsInfo
, data
, NULL
, 0)) {
798 SCLog(TRUE
, LOG_ERR
, CFSTR("data not valid, %@"), data
);
802 if ((prefsInfo
== NULL
) || !isA_CFDictionary(prefsInfo
)) {
803 SCLog(TRUE
, LOG_ERR
, CFSTR("info not valid"));
804 if (prefsInfo
!= NULL
) CFRelease(prefsInfo
);
808 // get [optional] prefsID
809 prefsID
= CFDictionaryGetValue(prefsInfo
, CFSTR("prefsID"));
810 prefsID
= isA_CFString(prefsID
);
811 if (prefsID
!= NULL
) {
812 if (CFStringHasPrefix(prefsID
, CFSTR("/")) ||
813 CFStringHasPrefix(prefsID
, CFSTR("../")) ||
814 CFStringHasSuffix(prefsID
, CFSTR("/..")) ||
815 (CFStringFind(prefsID
, CFSTR("/../"), 0).location
!= kCFNotFound
)) {
816 // if we're trying to escape from the preferences directory
817 SCLog(TRUE
, LOG_ERR
, CFSTR("prefsID (%@) not valid"), prefsID
);
818 CFRelease(prefsInfo
);
819 *status
= kSCStatusInvalidArgument
;
824 // get [optional] options
825 options
= CFDictionaryGetValue(prefsInfo
, CFSTR("options"));
826 options
= isA_CFDictionary(options
);
828 // get preferences session "name"
829 name
= CFDictionaryGetValue(prefsInfo
, CFSTR("name"));
830 if (!isA_CFString(name
)) {
831 SCLog(TRUE
, LOG_ERR
, CFSTR("session \"name\" not valid"));
832 CFRelease(prefsInfo
);
837 pid
= CFDictionaryGetValue(prefsInfo
, CFSTR("PID"));
838 if (!isA_CFNumber(pid
)) {
839 SCLog(TRUE
, LOG_ERR
, CFSTR("PID not valid"));
840 CFRelease(prefsInfo
);
844 // get process name of caller
845 proc_name
= CFDictionaryGetValue(prefsInfo
, CFSTR("PROC_NAME"));
846 if (!isA_CFString(proc_name
)) {
847 SCLog(TRUE
, LOG_ERR
, CFSTR("process name not valid"));
848 CFRelease(prefsInfo
);
852 // build [helper] preferences "name" (used for debugging) and estabish
853 // a preferences session.
854 prefsName
= CFStringCreateWithFormat(NULL
, NULL
,
860 prefs
= SCPreferencesCreateWithOptions(NULL
, prefsName
, prefsID
, NULL
, options
);
861 CFRelease(prefsName
);
862 CFRelease(prefsInfo
);
864 __SCHelperSessionSetPreferences(session
, prefs
);
869 CFRunLoopRef rl
= CFRunLoopGetCurrent();
871 // [temporarily] schedule notifications to ensure that we can use
872 // the SCDynamicStore to track helper sessions
873 ok
= SCPreferencesScheduleWithRunLoop(prefs
, rl
, kCFRunLoopDefaultMode
);
875 (void)SCPreferencesUnscheduleFromRunLoop(prefs
, rl
, kCFRunLoopDefaultMode
);
879 // the session now holds a references to the SCPreferencesRef
892 * (out) status = SCError()
893 * (out) reply = current signature + current preferences
896 do_prefs_Access(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
899 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
906 signature
= SCPreferencesGetSignature(prefs
);
907 if (signature
!= NULL
) {
908 const void * dictKeys
[2];
909 const void * dictVals
[2];
910 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
911 CFDictionaryRef replyDict
;
913 dictKeys
[0] = CFSTR("signature");
914 dictVals
[0] = signature
;
916 dictKeys
[1] = CFSTR("preferences");
917 dictVals
[1] = prefsPrivate
->prefs
;
919 replyDict
= CFDictionaryCreate(NULL
,
920 (const void **)&dictKeys
,
921 (const void **)&dictVals
,
922 sizeof(dictKeys
)/sizeof(dictKeys
[0]),
923 &kCFTypeDictionaryKeyCallBacks
,
924 &kCFTypeDictionaryValueCallBacks
);
926 ok
= _SCSerialize(replyDict
, reply
, NULL
, NULL
);
927 CFRelease(replyDict
);
941 * (in) data = client prefs signature (NULL if check not needed)
942 * (out) status = SCError()
946 do_prefs_Lock(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
948 CFDataRef clientSignature
= (CFDataRef
)data
;
950 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
951 Boolean wait
= (info
== (void *)FALSE
) ? FALSE
: TRUE
;
957 ok
= SCPreferencesLock(prefs
, wait
);
963 if (clientSignature
!= NULL
) {
964 CFDataRef serverSignature
;
966 serverSignature
= SCPreferencesGetSignature(prefs
);
967 if (!CFEqual(clientSignature
, serverSignature
)) {
968 (void)SCPreferencesUnlock(prefs
);
969 *status
= kSCStatusStale
;
979 * (in) data = new preferences (NULL if commit w/no changes)
980 * (out) status = SCError()
981 * (out) reply = new signature
984 do_prefs_Commit(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
987 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
988 CFPropertyListRef prefsData
= NULL
;
989 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
990 CFArrayRef vpnFilter
;
997 ok
= _SCUnserialize(&prefsData
, data
, NULL
, 0);
1002 if (!isA_CFDictionary(prefsData
)) {
1003 *status
= kSCStatusFailed
;
1009 vpnFilter
= __SCHelperSessionGetVPNFilter(session
);
1010 if (vpnFilter
!= NULL
) {
1013 if ((prefsPrivate
->prefs
!= NULL
) && (prefsData
!= NULL
)) {
1015 CFMutableDictionaryRef prefsNew
= NULL
;
1016 CFMutableDictionaryRef prefsOld
= NULL
;
1017 CFMutableDictionaryRef prefsSave
= prefsPrivate
->prefs
;
1018 CFRange range
= CFRangeMake(0, CFArrayGetCount(vpnFilter
));
1020 for (c
= 0; c
< 2; c
++) {
1021 CFArrayRef services
;
1025 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, prefsSave
);
1028 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, prefsData
);
1032 // filter out VPN services of the specified type
1033 services
= SCNetworkServiceCopyAll(prefs
);
1034 if (services
!= NULL
) {
1036 CFIndex n
= CFArrayGetCount(services
);
1038 for (i
= 0; i
< n
; i
++) {
1039 SCNetworkServiceRef service
;
1041 service
= CFArrayGetValueAtIndex(services
, i
);
1042 if (_SCNetworkServiceIsVPN(service
)) {
1043 SCNetworkInterfaceRef child
;
1044 CFStringRef childType
= NULL
;
1045 SCNetworkInterfaceRef interface
;
1046 CFStringRef interfaceType
;
1048 interface
= SCNetworkServiceGetInterface(service
);
1049 interfaceType
= SCNetworkInterfaceGetInterfaceType(interface
);
1050 child
= SCNetworkInterfaceGetInterface(interface
);
1051 if (child
!= NULL
) {
1052 childType
= SCNetworkInterfaceGetInterfaceType(child
);
1054 if (CFEqual(interfaceType
, kSCNetworkInterfaceTypeVPN
) &&
1055 (childType
!= NULL
) &&
1056 CFArrayContainsValue(vpnFilter
, range
, childType
)) {
1057 // filter out VPN service
1058 (void) SCNetworkServiceRemove(service
);
1060 // mark all other VPN services "enabled"
1061 (void) SCNetworkServiceSetEnabled(service
, TRUE
);
1066 CFRelease(services
);
1071 prefsOld
= prefsPrivate
->prefs
;
1074 prefsNew
= prefsPrivate
->prefs
;
1079 // compare the filtered configurations
1080 ok
= _SC_CFEqual(prefsOld
, prefsNew
);
1083 if (prefsOld
!= NULL
) CFRelease(prefsOld
);
1084 if (prefsNew
!= NULL
) CFRelease(prefsNew
);
1085 prefsPrivate
->prefs
= prefsSave
;
1089 *status
= kSCStatusAccessError
;
1094 if (prefsData
!= NULL
) {
1095 if (prefsPrivate
->prefs
!= NULL
) {
1096 CFRelease(prefsPrivate
->prefs
);
1098 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, prefsData
);
1099 prefsPrivate
->accessed
= TRUE
;
1100 prefsPrivate
->changed
= TRUE
;
1103 ok
= SCPreferencesCommitChanges(prefs
);
1105 *reply
= SCPreferencesGetSignature(prefs
);
1108 *status
= SCError();
1113 if (prefsData
!= NULL
) CFRelease(prefsData
);
1121 * (out) status = SCError()
1125 do_prefs_Apply(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1128 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1130 if (prefs
== NULL
) {
1134 ok
= SCPreferencesApplyChanges(prefs
);
1136 *status
= SCError();
1146 * (out) status = SCError()
1150 do_prefs_Unlock(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1153 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1155 if (prefs
== NULL
) {
1159 ok
= SCPreferencesUnlock(prefs
);
1161 *status
= SCError();
1171 * (out) status = SCError()
1175 do_prefs_Close(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1177 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1179 if (prefs
== NULL
) {
1183 __SCHelperSessionSetPreferences(session
, NULL
);
1192 * (out) status = kSCStatusOK
1196 do_prefs_Synchronize(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1198 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1200 if (prefs
== NULL
) {
1204 SCPreferencesSynchronize(prefs
);
1205 *status
= kSCStatusOK
;
1211 #pragma mark Process commands
1214 #if TARGET_OS_IPHONE
1216 #include <Security/Security.h>
1217 #include <Security/SecTask.h>
1220 sessionName(SCHelperSessionRef session
)
1222 CFStringRef name
= NULL
;
1223 SCPreferencesRef prefs
;
1225 prefs
= __SCHelperSessionGetPreferences(session
);
1226 if (prefs
!= NULL
) {
1227 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1229 name
= prefsPrivate
->name
;
1232 return (name
!= NULL
) ? name
: CFSTR("???");
1237 copyEntitlement(SCHelperSessionRef session
, CFStringRef entitlement
)
1239 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1241 CFTypeRef value
= NULL
;
1243 // Create the security task from the audit token
1244 task
= SecTaskCreateWithAuditToken(NULL
, sessionPrivate
->auditToken
);
1246 CFErrorRef error
= NULL
;
1248 // Get the value for the entitlement
1249 value
= SecTaskCopyValueForEntitlement(task
, entitlement
, &error
);
1250 if ((value
== NULL
) && (error
!= NULL
)) {
1251 CFIndex code
= CFErrorGetCode(error
);
1252 CFStringRef domain
= CFErrorGetDomain(error
);
1254 if (!CFEqual(domain
, kCFErrorDomainMach
) ||
1255 ((code
!= kIOReturnInvalid
) && (code
!= kIOReturnNotFound
))) {
1256 // if unexpected error
1257 SCLog(TRUE
, LOG_ERR
,
1258 CFSTR("SecTaskCopyValueForEntitlement(,\"%@\",) failed, error = %@ : %@"),
1261 sessionName(session
));
1268 SCLog(TRUE
, LOG_ERR
,
1269 CFSTR("SecTaskCreateWithAuditToken() failed: %@"),
1270 sessionName(session
));
1276 #endif // TARGET_OS_IPHONE
1282 hasAuthorization(SCHelperSessionRef session
)
1284 AuthorizationRef authorization
= __SCHelperSessionGetAuthorization(session
);
1286 if (authorization
== NULL
) {
1290 #if !TARGET_OS_IPHONE
1291 AuthorizationFlags flags
;
1292 AuthorizationItem items
[1];
1293 AuthorizationRights rights
;
1296 items
[0].name
= "system.preferences";
1297 items
[0].value
= NULL
;
1298 items
[0].valueLength
= 0;
1301 rights
.count
= sizeof(items
) / sizeof(items
[0]);
1302 rights
.items
= items
;
1304 flags
= kAuthorizationFlagDefaults
;
1305 flags
|= kAuthorizationFlagExtendRights
;
1306 flags
|= kAuthorizationFlagInteractionAllowed
;
1307 // flags |= kAuthorizationFlagPartialRights;
1308 // flags |= kAuthorizationFlagPreAuthorize;
1310 status
= AuthorizationCopyRights(authorization
,
1312 kAuthorizationEmptyEnvironment
,
1315 if (status
!= errAuthorizationSuccess
) {
1320 #else // !TARGET_OS_IPHONE
1321 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1323 if (sessionPrivate
->callerWriteAccess
== UNKNOWN
) {
1324 CFArrayRef entitlement
;
1325 CFStringRef prefsID
;
1326 SCPreferencesPrivateRef prefsPrivate
;
1328 // assume that the client DOES NOT have the entitlement
1329 sessionPrivate
->callerWriteAccess
= NO
;
1331 prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
1332 prefsID
= (prefsPrivate
->prefsID
!= NULL
) ? prefsPrivate
->prefsID
: PREFS_DEFAULT_CONFIG
;
1334 entitlement
= copyEntitlement(session
, kSCWriteEntitlementName
);
1335 if (entitlement
!= NULL
) {
1336 if (isA_CFArray(entitlement
)) {
1337 if (CFArrayContainsValue(entitlement
,
1338 CFRangeMake(0, CFArrayGetCount(entitlement
)),
1340 // if client DOES have entitlement
1341 sessionPrivate
->callerWriteAccess
= YES
;
1344 SCLog(TRUE
, LOG_ERR
,
1345 CFSTR("hasAuthorization: entitlement not valid: %@"),
1346 sessionName(session
));
1349 CFRelease(entitlement
);
1352 // make an exception for VPN configuration management
1353 if (sessionPrivate
->callerWriteAccess
!= YES
) {
1354 entitlement
= copyEntitlement(session
, kSCVPNFilterEntitlementName
);
1355 if (entitlement
!= NULL
) {
1356 if (isA_CFArray(entitlement
)) {
1357 if (CFEqual(prefsID
, PREFS_DEFAULT_CONFIG
)) {
1358 // save the VPN bundle identifiers
1359 __SCHelperSessionSetVPNFilter(session
, entitlement
);
1361 // and grant a "filtered" exception
1362 sessionPrivate
->callerWriteAccess
= YES
;
1363 } else if (CFStringHasPrefix(prefsID
, CFSTR("VPN-")) &&
1364 CFStringHasSuffix(prefsID
, CFSTR(".plist"))) {
1368 range
.location
= sizeof("VPN-") - 1;
1369 range
.length
= CFStringGetLength(prefsID
)
1370 - (sizeof("VPN-") - 1) // trim VPN-
1371 - (sizeof(".plist") - 1); // trim .plist
1372 vpnID
= CFStringCreateWithSubstring(NULL
, prefsID
, range
);
1373 if (CFArrayContainsValue(entitlement
,
1374 CFRangeMake(0, CFArrayGetCount(entitlement
)),
1376 // grant an exception
1377 sessionPrivate
->callerWriteAccess
= YES
;
1383 CFRelease(entitlement
);
1387 if (sessionPrivate
->callerWriteAccess
!= YES
) {
1388 SCLog(TRUE
, LOG_ERR
,
1389 CFSTR("SCPreferences write access to \"%@\" denied, no entitlement for \"%@\""),
1391 sessionName(session
));
1395 return (sessionPrivate
->callerWriteAccess
== YES
) ? TRUE
: FALSE
;
1396 #endif // TARGET_OS_IPHONE
1400 typedef Boolean (*helperFunction
) (SCHelperSessionRef session
,
1407 static const struct helper
{
1409 const char *commandName
;
1410 Boolean needsAuthorization
;
1411 helperFunction func
;
1414 { SCHELPER_MSG_AUTH
, "AUTH", FALSE
, do_Auth
, NULL
},
1416 { SCHELPER_MSG_PREFS_OPEN
, "PREFS open", FALSE
, do_prefs_Open
, NULL
},
1417 { SCHELPER_MSG_PREFS_ACCESS
, "PREFS access", TRUE
, do_prefs_Access
, NULL
},
1418 { SCHELPER_MSG_PREFS_LOCK
, "PREFS lock", TRUE
, do_prefs_Lock
, (void *)FALSE
},
1419 { SCHELPER_MSG_PREFS_LOCKWAIT
, "PREFS lock/wait", TRUE
, do_prefs_Lock
, (void *)TRUE
},
1420 { SCHELPER_MSG_PREFS_COMMIT
, "PREFS commit", TRUE
, do_prefs_Commit
, NULL
},
1421 { SCHELPER_MSG_PREFS_APPLY
, "PREFS apply", TRUE
, do_prefs_Apply
, NULL
},
1422 { SCHELPER_MSG_PREFS_UNLOCK
, "PREFS unlock", FALSE
, do_prefs_Unlock
, NULL
},
1423 { SCHELPER_MSG_PREFS_CLOSE
, "PREFS close", FALSE
, do_prefs_Close
, NULL
},
1424 { SCHELPER_MSG_PREFS_SYNCHRONIZE
, "PREFS synchronize", FALSE
, do_prefs_Synchronize
, NULL
},
1426 { SCHELPER_MSG_INTERFACE_REFRESH
, "INTERFACE refresh", TRUE
, do_interface_refresh
, NULL
},
1428 #if !TARGET_OS_IPHONE
1429 { SCHELPER_MSG_KEYCHAIN_COPY
, "KEYCHAIN copy", TRUE
, do_keychain_copy
, NULL
},
1430 { SCHELPER_MSG_KEYCHAIN_EXISTS
, "KEYCHAIN exists", TRUE
, do_keychain_exists
, NULL
},
1431 { SCHELPER_MSG_KEYCHAIN_REMOVE
, "KEYCHAIN remove", TRUE
, do_keychain_remove
, NULL
},
1432 { SCHELPER_MSG_KEYCHAIN_SET
, "KEYCHAIN set", TRUE
, do_keychain_set
, NULL
},
1433 #endif // !TARGET_OS_IPHONE
1435 { SCHELPER_MSG_EXIT
, "EXIT", FALSE
, do_Exit
, NULL
}
1437 #define nHELPERS (sizeof(helpers)/sizeof(struct helper))
1441 findCommand(uint32_t command
)
1445 for (i
= 0; i
< (int)nHELPERS
; i
++) {
1446 if (helpers
[i
].command
== command
) {
1456 newHelper(void *arg
)
1458 CFRunLoopSourceRef rls
= NULL
;
1459 SCHelperSessionRef session
= (SCHelperSessionRef
)arg
;
1460 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1462 __SCHelperSessionSetThreadName(session
);
1464 rls
= CFMachPortCreateRunLoopSource(NULL
, sessionPrivate
->mp
, 0);
1465 CFRelease(sessionPrivate
->mp
);
1468 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
1471 SCLog(debug
, LOG_DEBUG
, CFSTR("%p : start"), session
);
1473 SCLog(debug
, LOG_DEBUG
, CFSTR("%p : stop"), session
);
1481 #pragma mark Main loop
1484 // MiG generated externals and functions
1485 extern struct mig_subsystem _helper_subsystem
;
1486 extern boolean_t
helper_server(mach_msg_header_t
*, mach_msg_header_t
*);
1491 notify_server(mach_msg_header_t
*request
, mach_msg_header_t
*reply
)
1493 mach_no_senders_notification_t
*Request
= (mach_no_senders_notification_t
*)request
;
1494 mig_reply_error_t
*Reply
= (mig_reply_error_t
*)reply
;
1496 reply
->msgh_bits
= MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request
->msgh_bits
), 0);
1497 reply
->msgh_remote_port
= request
->msgh_remote_port
;
1498 reply
->msgh_size
= sizeof(mig_reply_error_t
); /* Minimal size: update as needed */
1499 reply
->msgh_local_port
= MACH_PORT_NULL
;
1500 reply
->msgh_id
= request
->msgh_id
+ 100;
1502 if ((Request
->not_header
.msgh_id
> MACH_NOTIFY_LAST
) ||
1503 (Request
->not_header
.msgh_id
< MACH_NOTIFY_FIRST
)) {
1504 Reply
->NDR
= NDR_record
;
1505 Reply
->RetCode
= MIG_BAD_ID
;
1506 return FALSE
; /* if this is not a notification message */
1509 switch (Request
->not_header
.msgh_id
) {
1510 case MACH_NOTIFY_NO_SENDERS
: {
1511 SCHelperSessionRef session
;
1513 __MACH_PORT_DEBUG(TRUE
, "*** notify_server MACH_NOTIFY_NO_SENDERS", Request
->not_header
.msgh_local_port
);
1516 session
= __SCHelperSessionFindWithPort(Request
->not_header
.msgh_local_port
);
1517 if (session
!= NULL
) {
1518 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1520 // release CFMachPort *and* SCHelperSession
1521 CFMachPortInvalidate(sessionPrivate
->mp
);
1524 __MACH_PORT_DEBUG(TRUE
, "*** notify_server after invalidate", Request
->not_header
.msgh_local_port
);
1526 // and, lastly, remove our receive right.
1527 (void) mach_port_mod_refs(mach_task_self(),
1528 Request
->not_header
.msgh_local_port
,
1529 MACH_PORT_RIGHT_RECEIVE
, -1);
1531 Reply
->Head
.msgh_bits
= 0;
1532 Reply
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1533 Reply
->RetCode
= KERN_SUCCESS
;
1541 SCLog(TRUE
, LOG_ERR
, CFSTR("HELP!, Received notification: port=%d, msgh_id=%d"),
1542 Request
->not_header
.msgh_local_port
,
1543 Request
->not_header
.msgh_id
);
1545 Reply
->NDR
= NDR_record
;
1546 Reply
->RetCode
= MIG_BAD_ID
;
1547 return FALSE
; /* if this is not a notification we are handling */
1553 helper_demux(mach_msg_header_t
*request
, mach_msg_header_t
*reply
)
1555 Boolean processed
= FALSE
;
1558 * (attempt to) process SCHelper requests.
1560 processed
= helper_server(request
, reply
);
1566 * (attempt to) process (NO MORE SENDERS) notification messages.
1568 processed
= notify_server(request
, reply
);
1574 * unknown message ID, log and return an error.
1576 SCLog(TRUE
, LOG_ERR
, CFSTR("helper_demux(): unknown message ID (%d) received"), request
->msgh_id
);
1577 reply
->msgh_bits
= MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request
->msgh_bits
), 0);
1578 reply
->msgh_remote_port
= request
->msgh_remote_port
;
1579 reply
->msgh_size
= sizeof(mig_reply_error_t
); /* Minimal size */
1580 reply
->msgh_local_port
= MACH_PORT_NULL
;
1581 reply
->msgh_id
= request
->msgh_id
+ 100;
1582 ((mig_reply_error_t
*)reply
)->NDR
= NDR_record
;
1583 ((mig_reply_error_t
*)reply
)->RetCode
= MIG_BAD_ID
;
1589 #define MACH_MSG_BUFFER_SIZE 128
1593 helperCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1595 mig_reply_error_t
* bufRequest
= msg
;
1596 uint32_t bufReply_q
[MACH_MSG_BUFFER_SIZE
/sizeof(uint32_t)];
1597 mig_reply_error_t
* bufReply
= (mig_reply_error_t
*)bufReply_q
;
1598 static CFIndex bufSize
= 0;
1599 mach_msg_return_t mr
;
1603 // get max size for MiG reply buffers
1604 bufSize
= _helper_subsystem
.maxsize
;
1606 // check if our on-the-stack reply buffer will be big enough
1607 if (bufSize
> sizeof(bufReply_q
)) {
1608 SCLog(TRUE
, LOG_NOTICE
,
1609 CFSTR("helperCallback(): buffer size should be increased > %d"),
1610 _helper_subsystem
.maxsize
);
1614 if (bufSize
> sizeof(bufReply_q
)) {
1615 bufReply
= CFAllocatorAllocate(NULL
, _helper_subsystem
.maxsize
, 0);
1617 bufReply
->RetCode
= 0;
1619 /* we have a request message */
1620 (void) helper_demux(&bufRequest
->Head
, &bufReply
->Head
);
1622 if (!(bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)) {
1623 if (bufReply
->RetCode
== MIG_NO_REPLY
) {
1624 bufReply
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1625 } else if ((bufReply
->RetCode
!= KERN_SUCCESS
) &&
1626 (bufRequest
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)) {
1628 * destroy the request - but not the reply port
1630 bufRequest
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1631 mach_msg_destroy(&bufRequest
->Head
);
1635 if (bufReply
->Head
.msgh_remote_port
!= MACH_PORT_NULL
) {
1639 * We don't want to block indefinitely because the client
1640 * isn't receiving messages from the reply port.
1641 * If we have a send-once right for the reply port, then
1642 * this isn't a concern because the send won't block.
1643 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
1644 * To avoid falling off the kernel's fast RPC path unnecessarily,
1645 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
1648 options
= MACH_SEND_MSG
;
1649 if (MACH_MSGH_BITS_REMOTE(bufReply
->Head
.msgh_bits
) != MACH_MSG_TYPE_MOVE_SEND_ONCE
) {
1650 options
|= MACH_SEND_TIMEOUT
;
1652 mr
= mach_msg(&bufReply
->Head
, /* msg */
1653 options
, /* option */
1654 bufReply
->Head
.msgh_size
, /* send_size */
1656 MACH_PORT_NULL
, /* rcv_name */
1657 MACH_MSG_TIMEOUT_NONE
, /* timeout */
1658 MACH_PORT_NULL
); /* notify */
1660 /* Has a message error occurred? */
1662 case MACH_SEND_INVALID_DEST
:
1663 case MACH_SEND_TIMED_OUT
:
1666 /* Includes success case. */
1671 if (bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
1672 mach_msg_destroy(&bufReply
->Head
);
1677 if (bufReply
!= (mig_reply_error_t
*)bufReply_q
)
1678 CFAllocatorDeallocate(NULL
, bufReply
);
1684 initMPCopyDescription(const void *info
)
1686 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<SCHelper MP>"));
1692 _helperinit(mach_port_t server
,
1693 mach_port_t
*newSession
,
1695 audit_token_t audit_token
)
1697 CFMachPortContext context
= { 0
1701 , initMPCopyDescription
1704 mach_port_t oldNotify
;
1705 SCHelperSessionRef session
;
1706 SCHelperSessionPrivateRef sessionPrivate
;
1707 pthread_attr_t tattr
;
1710 session
= __SCHelperSessionFindWithPort(server
);
1711 if (session
!= NULL
) {
1713 SCLog(TRUE
, LOG_DEBUG
, CFSTR("_helperinit(): session is already open."));
1715 *status
= kSCStatusFailed
; /* you can't re-open an "open" session */
1716 return KERN_SUCCESS
;
1719 session
= __SCHelperSessionCreate(NULL
);
1720 sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1722 // create per-session port
1723 (void) mach_port_allocate(mach_task_self(),
1724 MACH_PORT_RIGHT_RECEIVE
,
1725 &sessionPrivate
->port
);
1726 *newSession
= sessionPrivate
->port
;
1729 // Note: we create the CFMachPort *before* we insert a send
1730 // right present to ensure that CF does not establish
1731 // its dead name notification.
1733 context
.info
= (void *)session
;
1734 sessionPrivate
->mp
= _SC_CFMachPortCreateWithPort("SCHelper/session",
1739 /* Request a notification when/if the client dies */
1740 kr
= mach_port_request_notification(mach_task_self(),
1742 MACH_NOTIFY_NO_SENDERS
,
1745 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
1747 if (kr
!= KERN_SUCCESS
) {
1748 SCLog(TRUE
, LOG_ERR
, CFSTR("_helperinit() mach_port_request_notification() failed: %s"), mach_error_string(kr
));
1750 // clean up CFMachPort, mach port rights
1751 CFMachPortInvalidate(sessionPrivate
->mp
);
1752 CFRelease(sessionPrivate
->mp
);
1753 sessionPrivate
->mp
= NULL
;
1754 (void) mach_port_mod_refs(mach_task_self(), *newSession
, MACH_PORT_RIGHT_RECEIVE
, -1);
1755 *newSession
= MACH_PORT_NULL
;
1756 *status
= kSCStatusFailed
;
1760 if (oldNotify
!= MACH_PORT_NULL
) {
1761 SCLog(TRUE
, LOG_ERR
, CFSTR("_helperinit(): oldNotify != MACH_PORT_NULL"));
1764 // add send right (that will be passed back to the client)
1765 (void) mach_port_insert_right(mach_task_self(),
1768 MACH_MSG_TYPE_MAKE_SEND
);
1771 sessionPrivate
->auditToken
= audit_token
;
1774 // Note: at this time we should be holding ONE send right and
1775 // ONE receive right to the server. The send right is
1776 // moved to the caller.
1779 // start per-session thread
1780 pthread_attr_init(&tattr
);
1781 pthread_attr_setscope(&tattr
, PTHREAD_SCOPE_SYSTEM
);
1782 pthread_attr_setdetachstate(&tattr
, PTHREAD_CREATE_DETACHED
);
1783 pthread_attr_setstacksize(&tattr
, 96 * 1024); // each thread gets a 96K stack
1784 pthread_create(&tid
, &tattr
, newHelper
, (void *)session
);
1785 pthread_attr_destroy(&tattr
);
1787 *status
= kSCStatusOK
;
1792 return KERN_SUCCESS
;
1798 _helperexec(mach_port_t server
,
1800 xmlData_t dataRef
, /* raw XML bytes */
1801 mach_msg_type_number_t dataLen
,
1803 xmlDataOut_t
*replyRef
, /* raw XML bytes */
1804 mach_msg_type_number_t
*replyLen
)
1806 CFDataRef data
= NULL
;
1808 CFDataRef reply
= NULL
;
1809 SCHelperSessionRef session
;
1811 *status
= kSCStatusOK
;
1815 if ((dataRef
!= NULL
) && (dataLen
> 0)) {
1816 if (!_SCUnserializeData(&data
, (void *)dataRef
, dataLen
)) {
1817 *status
= SCError();
1818 return KERN_SUCCESS
;
1822 session
= __SCHelperSessionFindWithPort(server
);
1823 if (session
== NULL
) {
1824 *status
= kSCStatusFailed
; /* you must have an open session to play */
1828 i
= findCommand(msgID
);
1830 SCLog(TRUE
, LOG_ERR
, CFSTR("received unknown command : %u"), msgID
);
1831 *status
= kSCStatusInvalidArgument
;
1835 SCLog(debug
, LOG_DEBUG
,
1836 CFSTR("%p : processing command \"%s\"%s"),
1838 helpers
[i
].commandName
,
1839 (data
!= NULL
) ? " w/data" : "");
1841 if (helpers
[i
].needsAuthorization
&& !hasAuthorization(session
)) {
1842 SCLog(debug
, LOG_DEBUG
,
1843 CFSTR("%p : command \"%s\" : not authorized"),
1845 helpers
[i
].commandName
);
1846 *status
= kSCStatusAccessError
;
1849 if (*status
== kSCStatusOK
) {
1850 (*helpers
[i
].func
)(session
, helpers
[i
].info
, data
, status
, &reply
);
1853 if ((*status
!= -1) || (reply
!= NULL
)) {
1856 SCLog(debug
, LOG_DEBUG
,
1857 CFSTR("%p : sending status %u%s"),
1860 (reply
!= NULL
) ? " w/reply" : "");
1862 /* serialize the data */
1863 if (reply
!= NULL
) {
1864 ok
= _SCSerializeData(reply
, (void **)replyRef
, (CFIndex
*)replyLen
);
1868 *status
= SCError();
1876 if (data
!= NULL
) CFRelease(data
);
1877 if (reply
!= NULL
) CFRelease(reply
);
1878 return KERN_SUCCESS
;
1883 helperMPCopyDescription(const void *info
)
1885 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<main SCHelper MP>"));
1890 init_MiG_1(const launch_data_t l_obj
, const char *name
, void *info
)
1892 CFMachPortContext context
= { 0
1896 , helperMPCopyDescription
1898 launch_data_type_t l_type
;
1900 int *n_listeners
= (int *)info
;
1901 CFRunLoopSourceRef rls
;
1902 mach_port_t service_port
;
1904 // get the mach port
1905 l_type
= (l_obj
!= NULL
) ? launch_data_get_type(l_obj
) : 0;
1906 if (l_type
!= LAUNCH_DATA_MACHPORT
) {
1907 SCLog(TRUE
, LOG_ERR
,
1908 CFSTR("SCHelper: error w/MachServices \"%s\" port (%p, %d)"),
1909 (name
!= NULL
) ? name
: "?",
1914 service_port
= launch_data_get_machport(l_obj
);
1916 // add a run loop source to listen for new requests
1917 mp
= _SC_CFMachPortCreateWithPort("SCHelper/server",
1921 rls
= CFMachPortCreateRunLoopSource(NULL
, mp
, 0);
1923 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
1926 *n_listeners
= *n_listeners
+ 1;
1933 init_MiG(launch_data_t l_reply
, int *n_listeners
)
1935 launch_data_t l_machservices
;
1936 launch_data_type_t l_type
;
1938 l_machservices
= launch_data_dict_lookup(l_reply
, LAUNCH_JOBKEY_MACHSERVICES
);
1939 l_type
= (l_machservices
!= NULL
) ? launch_data_get_type(l_machservices
) : 0;
1940 if (l_type
!= LAUNCH_DATA_DICTIONARY
) {
1941 SCLog(TRUE
, LOG_ERR
,
1942 CFSTR("SCHelper: error w/" LAUNCH_JOBKEY_MACHSERVICES
" (%p, %d)"),
1948 launch_data_dict_iterate(l_machservices
, init_MiG_1
, (void *)n_listeners
);
1957 static const struct option longopts
[] = {
1958 { "debug", no_argument
, 0, 'd' },
1964 main(int argc
, char **argv
)
1966 Boolean done
= FALSE
;
1968 int gen_reported
= 0;
1970 launch_data_t l_msg
;
1971 launch_data_t l_reply
;
1972 launch_data_type_t l_type
;
1973 int n_listeners
= 0;
1978 openlog("SCHelper", LOG_CONS
|LOG_PID
, LOG_DAEMON
);
1980 // process any arguments
1981 while ((opt
= getopt_long(argc
, argv
, "d", longopts
, &opti
)) != -1) {
1987 // if (strcmp(longopts[opti].name, "debug") == 1) {
1992 SCLog(TRUE
, LOG_ERR
,
1993 CFSTR("ignoring unknown or ambiguous command line option"));
2000 if (geteuid() != 0) {
2001 SCLog(TRUE
, LOG_ERR
, CFSTR("%s"), strerror(EACCES
));
2005 main_runLoop
= CFRunLoopGetCurrent();
2007 l_msg
= launch_data_new_string(LAUNCH_KEY_CHECKIN
);
2008 l_reply
= launch_msg(l_msg
);
2009 launch_data_free(l_msg
);
2010 l_type
= (l_reply
!= NULL
) ? launch_data_get_type(l_reply
) : 0;
2011 if (l_type
!= LAUNCH_DATA_DICTIONARY
) {
2012 SCLog(TRUE
, LOG_ERR
,
2013 CFSTR("SCHelper: error w/launchd " LAUNCH_KEY_CHECKIN
" dictionary (%p, %d)"),
2020 err
= init_MiG(l_reply
, &n_listeners
);
2027 if (l_reply
!= NULL
) launch_data_free(l_reply
);
2029 if ((err
!= 0) || (n_listeners
== 0)) {
2033 pthread_setname_np("SCHelper main thread");
2039 rlStatus
= CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 15.0, TRUE
);
2041 pthread_mutex_lock(&sessions_lock
);
2043 if (sessions
!= NULL
) {
2044 if (rlStatus
== kCFRunLoopRunTimedOut
) {
2047 if ((CFSetGetCount(sessions
) == 0) && (sessions_closed
== 0)) {
2048 // if we don't have any open sessions and no
2049 // sessions have recently been closed
2056 gen_current
= sessions_generation
;
2057 sessions_closed
= 0;
2059 if (!done
&& (idle
>= (2 * 60 / 15))) {
2060 if (gen_reported
!= gen_current
) {
2061 SCLog(TRUE
, LOG_NOTICE
, CFSTR("active (but IDLE) sessions"));
2062 CFSetApplyFunction(sessions
, __SCHelperSessionLog
, NULL
);
2063 gen_reported
= gen_current
;
2068 pthread_mutex_unlock(&sessions_lock
);