2 * Copyright (c) 2005-2012 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 <Security/Security.h>
39 #include <Security/SecTask.h>
40 #include <SystemConfiguration/SystemConfiguration.h>
41 #include <SystemConfiguration/SCPrivate.h>
42 #include <SystemConfiguration/SCValidation.h>
44 #include "SCPreferencesInternal.h"
45 #include "SCHelper_client.h"
46 #include "helper_types.h"
50 #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 typedef enum { NO
= 0, YES
, UNKNOWN
} lazyBoolean
;
65 typedef const struct __SCHelperSession
* SCHelperSessionRef
;
69 // base CFType information
76 AuthorizationRef authorization
;
77 Boolean use_entitlement
;
83 // Mach security audit trailer for evaluating credentials
84 audit_token_t auditToken
;
86 // write access entitlement associated with this session
87 lazyBoolean callerWriteAccess
;
89 // VPN configuration filtering
93 SCPreferencesRef prefs
;
96 CFMutableSetRef backtraces
;
98 } SCHelperSessionPrivate
, *SCHelperSessionPrivateRef
;
101 static CFStringRef
__SCHelperSessionCopyDescription (CFTypeRef cf
);
102 static void __SCHelperSessionDeallocate (CFTypeRef cf
);
105 static void __SCHelperSessionLogBacktrace (const void *value
, void *context
);
108 static CFTypeID __kSCHelperSessionTypeID
= _kCFRuntimeNotATypeID
;
109 static Boolean debug
= FALSE
;
110 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
111 static CFRunLoopRef main_runLoop
= NULL
;
112 static CFMutableSetRef sessions
= NULL
;
113 static int sessions_closed
= 0; // count of sessions recently closed
114 static int sessions_generation
= 0;
115 static pthread_mutex_t sessions_lock
= PTHREAD_MUTEX_INITIALIZER
;
119 #pragma mark Helper session management
122 #if !TARGET_OS_IPHONE
124 __SCHelperSessionUseEntitlement(SCHelperSessionRef session
)
126 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
128 return sessionPrivate
->use_entitlement
;
130 #endif //!TARGET_OS_IPHONE
133 static AuthorizationRef
134 __SCHelperSessionGetAuthorization(SCHelperSessionRef session
)
136 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
138 return sessionPrivate
->authorization
;
143 __SCHelperSessionSetAuthorization(SCHelperSessionRef session
, CFTypeRef authorizationData
)
146 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
148 pthread_mutex_lock(&sessionPrivate
->lock
);
150 if (sessionPrivate
->authorization
!= NULL
) {
151 #if !TARGET_OS_IPHONE
152 if (!__SCHelperSessionUseEntitlement(session
)) {
153 AuthorizationFree(sessionPrivate
->authorization
, kAuthorizationFlagDefaults
);
154 // AuthorizationFree(sessionPrivate->authorization, kAuthorizationFlagDestroyRights);
155 sessionPrivate
->authorization
= NULL
;
157 #endif //!TARGET_OS_IPHONE
158 CFRelease(sessionPrivate
->authorization
);
159 sessionPrivate
->authorization
= NULL
;
160 #if !TARGET_OS_IPHONE
162 #endif //!TARGET_OS_IPHONE
163 sessionPrivate
->use_entitlement
= FALSE
;
166 #if !TARGET_OS_IPHONE
167 if (isA_CFData(authorizationData
)) {
168 AuthorizationExternalForm extForm
;
170 if (CFDataGetLength(authorizationData
) == sizeof(extForm
.bytes
)) {
173 bcopy(CFDataGetBytePtr(authorizationData
), extForm
.bytes
, sizeof(extForm
.bytes
));
174 err
= AuthorizationCreateFromExternalForm(&extForm
,
175 &sessionPrivate
->authorization
);
176 if (err
!= errAuthorizationSuccess
) {
178 CFSTR("AuthorizationCreateFromExternalForm() failed: status = %d"),
180 sessionPrivate
->authorization
= NULL
;
185 #endif //!TARGET_OS_IPHONE
187 if (isA_CFString(authorizationData
)) {
188 sessionPrivate
->authorization
= (void *)CFRetain(authorizationData
);
189 sessionPrivate
->use_entitlement
= TRUE
;
192 pthread_mutex_unlock(&sessionPrivate
->lock
);
198 static SCPreferencesRef
199 __SCHelperSessionGetPreferences(SCHelperSessionRef session
)
201 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
203 return sessionPrivate
->prefs
;
208 __SCHelperSessionSetThreadName(SCHelperSessionRef session
)
214 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
217 if (sessionPrivate
->mp
== NULL
) {
221 if (sessionPrivate
->prefs
!= NULL
) {
222 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
224 if (prefsPrivate
->name
!= NULL
) {
225 caller
= _SC_cfstring_to_cstring(prefsPrivate
->name
,
228 kCFStringEncodingUTF8
);
231 path
= (prefsPrivate
->newPath
!= NULL
) ? prefsPrivate
->newPath
: prefsPrivate
->path
;
233 path_s
= strrchr(path
, '/');
234 if (path_s
!= NULL
) {
240 if (caller
!= NULL
) {
241 snprintf(name
, sizeof(name
), "SESSION|%p|%s|%s%s",
242 (void *)(uintptr_t)CFMachPortGetPort(sessionPrivate
->mp
),
243 (caller
!= NULL
) ? caller
: "?",
244 (path_s
!= NULL
) ? "*/" : "",
245 (path
!= NULL
) ? path
: "?");
246 CFAllocatorDeallocate(NULL
, caller
);
248 snprintf(name
, sizeof(name
), "SESSION|%p",
249 (void *)(uintptr_t)CFMachPortGetPort(sessionPrivate
->mp
));
252 pthread_setname_np(name
);
259 __SCHelperSessionSetPreferences(SCHelperSessionRef session
, SCPreferencesRef prefs
)
261 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
263 pthread_mutex_lock(&sessionPrivate
->lock
);
268 if (sessionPrivate
->prefs
!= NULL
) {
269 SCLog(debug
, LOG_DEBUG
,
272 CFRelease(sessionPrivate
->prefs
);
275 SCLog(debug
, LOG_DEBUG
,
276 CFSTR("%p : open, prefs = %@"),
280 sessionPrivate
->prefs
= prefs
;
282 __SCHelperSessionSetThreadName(session
);
284 pthread_mutex_unlock(&sessionPrivate
->lock
);
291 __SCHelperSessionGetVPNFilter(SCHelperSessionRef session
)
293 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
295 return sessionPrivate
->vpnFilter
;
300 __SCHelperSessionSetVPNFilter(SCHelperSessionRef session
, CFArrayRef vpnFilter
)
302 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
304 pthread_mutex_lock(&sessionPrivate
->lock
);
306 if (vpnFilter
!= NULL
) {
309 if (sessionPrivate
->vpnFilter
!= NULL
) {
310 CFRelease(sessionPrivate
->vpnFilter
);
312 sessionPrivate
->vpnFilter
= vpnFilter
;
314 pthread_mutex_unlock(&sessionPrivate
->lock
);
321 __SCHelperSessionLog(const void *value
, void *context
)
323 SCHelperSessionRef session
= (SCHelperSessionRef
)value
;
324 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
325 FILE **logFile
= (FILE **)context
;
327 pthread_mutex_lock(&sessionPrivate
->lock
);
329 if ((sessionPrivate
->mp
!= NULL
) && (sessionPrivate
->prefs
!= NULL
)) {
330 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
332 SCLog(TRUE
, LOG_NOTICE
,
333 CFSTR(" %p {port = %p, caller = %@, path = %s%s}"),
335 CFMachPortGetPort(sessionPrivate
->mp
),
337 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
,
338 prefsPrivate
->locked
? ", locked" : "");
340 if ((sessionPrivate
->backtraces
!= NULL
) &&
341 (CFSetGetCount(sessionPrivate
->backtraces
) > 0)) {
342 // log/report all collected backtraces
343 CFSetApplyFunction(sessionPrivate
->backtraces
,
344 __SCHelperSessionLogBacktrace
,
347 // to ensure that we don't log the same backtraces multiple
348 // times we remove any reported traces
349 CFRelease(sessionPrivate
->backtraces
);
350 sessionPrivate
->backtraces
= NULL
;
354 pthread_mutex_unlock(&sessionPrivate
->lock
);
363 static const CFRuntimeClass __SCHelperSessionClass
= {
365 "SCHelperSession", // className
368 __SCHelperSessionDeallocate
, // dealloc
371 NULL
, // copyFormattingDesc
372 __SCHelperSessionCopyDescription
// copyDebugDesc
377 __SCHelperSessionCopyDescription(CFTypeRef cf
)
379 CFAllocatorRef allocator
= CFGetAllocator(cf
);
380 CFMutableStringRef result
;
381 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)cf
;
383 pthread_mutex_lock(&sessionPrivate
->lock
);
385 result
= CFStringCreateMutable(allocator
, 0);
386 CFStringAppendFormat(result
, NULL
, CFSTR("<SCHelperSession %p [%p]> {"), cf
, allocator
);
387 CFStringAppendFormat(result
, NULL
, CFSTR("authorization = %p"), sessionPrivate
->authorization
);
388 if (sessionPrivate
->mp
!= NULL
) {
389 CFStringAppendFormat(result
, NULL
,
390 CFSTR(", mp = %p (port = %p)"),
392 CFMachPortGetPort(sessionPrivate
->mp
));
394 if (sessionPrivate
->prefs
!= NULL
) {
395 CFStringAppendFormat(result
, NULL
, CFSTR(", prefs = %@"), sessionPrivate
->prefs
);
397 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
399 pthread_mutex_unlock(&sessionPrivate
->lock
);
406 __SCHelperSessionDeallocate(CFTypeRef cf
)
408 SCHelperSessionRef session
= (SCHelperSessionRef
)cf
;
409 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
411 // we're releasing "a" session so take the global lock...
412 pthread_mutex_lock(&sessions_lock
);
415 __SCHelperSessionSetAuthorization(session
, NULL
);
416 __SCHelperSessionSetPreferences (session
, NULL
);
417 __SCHelperSessionSetVPNFilter (session
, NULL
);
418 pthread_mutex_destroy(&sessionPrivate
->lock
);
419 if (sessionPrivate
->backtraces
!= NULL
) {
420 CFRelease(sessionPrivate
->backtraces
);
423 // we no longer need/want to track this session
424 CFSetRemoveValue(sessions
, sessionPrivate
);
425 sessions_generation
++;
428 // release the global lock, wake up the main runloop, all done
429 pthread_mutex_unlock(&sessions_lock
);
430 CFRunLoopWakeUp(main_runLoop
);
437 __SCHelperSessionInitialize(void)
439 __kSCHelperSessionTypeID
= _CFRuntimeRegisterClass(&__SCHelperSessionClass
);
444 static SCHelperSessionRef
445 __SCHelperSessionCreate(CFAllocatorRef allocator
)
447 SCHelperSessionPrivateRef sessionPrivate
;
450 // initialize runtime
451 pthread_once(&initialized
, __SCHelperSessionInitialize
);
454 size
= sizeof(SCHelperSessionPrivate
) - sizeof(CFRuntimeBase
);
455 sessionPrivate
= (SCHelperSessionPrivateRef
)_CFRuntimeCreateInstance(allocator
,
456 __kSCHelperSessionTypeID
,
459 if (sessionPrivate
== NULL
) {
463 if (pthread_mutex_init(&sessionPrivate
->lock
, NULL
) != 0) {
464 SCLog(TRUE
, LOG_ERR
, CFSTR("pthread_mutex_init(): failure to initialize per session lock"));
465 CFRelease(sessionPrivate
);
468 sessionPrivate
->authorization
= NULL
;
469 sessionPrivate
->use_entitlement
= FALSE
;
470 sessionPrivate
->port
= MACH_PORT_NULL
;
471 sessionPrivate
->mp
= NULL
;
472 sessionPrivate
->callerWriteAccess
= UNKNOWN
;
473 sessionPrivate
->vpnFilter
= NULL
;
474 sessionPrivate
->prefs
= NULL
;
475 sessionPrivate
->backtraces
= NULL
;
477 // keep track this session
478 pthread_mutex_lock(&sessions_lock
);
479 if (sessions
== NULL
) {
480 const CFSetCallBacks mySetCallBacks
= { 0, NULL
, NULL
, CFCopyDescription
, CFEqual
, CFHash
};
482 // create a non-retaining set
483 sessions
= CFSetCreateMutable(NULL
, 0, &mySetCallBacks
);
485 CFSetAddValue(sessions
, sessionPrivate
);
486 sessions_generation
++;
487 pthread_mutex_unlock(&sessions_lock
);
489 return (SCHelperSessionRef
)sessionPrivate
;
496 static SCHelperSessionRef
497 __SCHelperSessionFindWithPort(mach_port_t port
)
499 SCHelperSessionRef session
= NULL
;
501 // keep track this session
502 pthread_mutex_lock(&sessions_lock
);
503 if (sessions
!= NULL
) {
505 CFIndex n
= CFSetGetCount(sessions
);
506 const void * vals_q
[16];
507 const void ** vals
= vals_q
;
509 if (n
> (CFIndex
)(sizeof(vals_q
) / sizeof(SCHelperSessionRef
)))
510 vals
= CFAllocatorAllocate(NULL
, n
* sizeof(CFStringRef
), 0);
511 CFSetGetValues(sessions
, vals
);
512 for (i
= 0; i
< n
; i
++) {
513 SCHelperSessionPrivateRef sessionPrivate
;
515 sessionPrivate
= (SCHelperSessionPrivateRef
)vals
[i
];
516 if (sessionPrivate
->port
== port
) {
517 session
= (SCHelperSessionRef
)sessionPrivate
;
522 CFAllocatorDeallocate(NULL
, vals
);
524 pthread_mutex_unlock(&sessions_lock
);
531 #pragma mark Session backtrace logging
535 __SCHelperSessionAddBacktrace(SCHelperSessionRef session
, CFStringRef backtrace
, const char * command
)
537 CFStringRef logEntry
;
538 SCPreferencesRef prefs
;
539 SCPreferencesPrivateRef prefsPrivate
;
540 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
542 prefs
= __SCHelperSessionGetPreferences((SCHelperSessionRef
)sessionPrivate
);
547 prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
549 logEntry
= CFStringCreateWithFormat(NULL
, NULL
,
550 CFSTR("%@ [%s]: %s\n\n%@"),
552 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
,
556 pthread_mutex_lock(&sessionPrivate
->lock
);
558 if (sessionPrivate
->backtraces
== NULL
) {
559 sessionPrivate
->backtraces
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
561 CFSetSetValue(sessionPrivate
->backtraces
, logEntry
);
563 pthread_mutex_unlock(&sessionPrivate
->lock
);
571 __SCHelperSessionLogBacktrace(const void *value
, void *context
)
573 CFSetRef backtrace
= (CFSetRef
)value
;
574 FILE **logFile
= (FILE **)context
;
576 if (*logFile
== NULL
) {
579 struct timeval tv_now
;
581 (void)gettimeofday(&tv_now
, NULL
);
582 (void)localtime_r(&tv_now
.tv_sec
, &tm_now
);
586 "/Library/Logs/CrashReporter/SCHelper-%4d-%02d-%02d-%02d%02d%02d.log",
587 tm_now
.tm_year
+ 1900,
594 *logFile
= fopen(path
, "a");
595 if (*logFile
== NULL
) {
596 // if log file could not be created
600 SCLog(TRUE
, LOG_INFO
, CFSTR("created backtrace log: %s"), path
);
603 SCPrint(TRUE
, *logFile
, CFSTR("%@\n"), backtrace
);
615 * (out) status = SCError()
619 do_Exit(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
628 * (in) data = authorizationDict (in 2 flavors)
629 * kSCHelperAuthAuthorization - use provided AuthorizationExternalForm
630 * kSCHelperAuthCallerInfo - use entitlement
631 * (out) status = OSStatus
635 do_Auth(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
637 CFDictionaryRef authorizationDict
;
638 #if !TARGET_OS_IPHONE
639 CFDataRef authorizationData
= NULL
;
643 if (_SCUnserialize((CFPropertyListRef
*)&authorizationDict
, data
, NULL
, 0) == FALSE
) {
647 if (isA_CFDictionary(authorizationDict
) == FALSE
) {
648 CFRelease(authorizationDict
);
652 #if !TARGET_OS_IPHONE
653 authorizationData
= CFDictionaryGetValue(authorizationDict
, kSCHelperAuthAuthorization
);
654 if (authorizationData
!= NULL
&& isA_CFData(authorizationData
)) {
655 ok
= __SCHelperSessionSetAuthorization(session
, authorizationData
);
659 CFStringRef authorizationInfo
;
661 authorizationInfo
= CFDictionaryGetValue(authorizationDict
, kSCHelperAuthCallerInfo
);
662 if (authorizationInfo
!= NULL
&& isA_CFString(authorizationInfo
)) {
663 ok
= __SCHelperSessionSetAuthorization(session
, authorizationInfo
);
667 CFRelease(authorizationDict
);
668 *status
= ok
? 0 : 1;
673 #if !TARGET_OS_IPHONE
677 * SCHELPER_MSG_KEYCHAIN_COPY
678 * (in) data = unique_id
679 * (out) status = SCError()
680 * (out) reply = password
683 do_keychain_copy(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
686 SCPreferencesRef prefs
;
687 CFStringRef unique_id
= NULL
;
689 if ((data
!= NULL
) && !_SCUnserializeString(&unique_id
, data
, NULL
, 0)) {
693 if (unique_id
!= NULL
) {
694 if (isA_CFString(unique_id
)) {
695 prefs
= __SCHelperSessionGetPreferences(session
);
696 *reply
= _SCPreferencesSystemKeychainPasswordItemCopy(prefs
, unique_id
);
697 if (*reply
== NULL
) {
703 CFRelease(unique_id
);
711 * SCHELPER_MSG_KEYCHAIN_EXISTS
712 * (in) data = unique_id
713 * (out) status = SCError()
717 do_keychain_exists(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
720 SCPreferencesRef prefs
;
721 CFStringRef unique_id
= NULL
;
723 if ((data
!= NULL
) && !_SCUnserializeString(&unique_id
, data
, NULL
, 0)) {
727 if (unique_id
!= NULL
) {
728 if (isA_CFString(unique_id
)) {
729 prefs
= __SCHelperSessionGetPreferences(session
);
730 ok
= _SCPreferencesSystemKeychainPasswordItemExists(prefs
, unique_id
);
736 CFRelease(unique_id
);
744 * SCHELPER_MSG_KEYCHAIN_REMOVE
745 * (in) data = unique_id
746 * (out) status = SCError()
750 do_keychain_remove(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
753 SCPreferencesRef prefs
;
754 CFStringRef unique_id
= NULL
;
756 if ((data
!= NULL
) && !_SCUnserializeString(&unique_id
, data
, NULL
, 0)) {
760 if (unique_id
!= NULL
) {
761 if (isA_CFString(unique_id
)) {
762 prefs
= __SCHelperSessionGetPreferences(session
);
763 ok
= _SCPreferencesSystemKeychainPasswordItemRemove(prefs
, unique_id
);
769 CFRelease(unique_id
);
777 * SCHELPER_MSG_KEYCHAIN_SET
778 * (in) data = options dictionary
779 * (out) status = SCError()
783 do_keychain_set(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
786 CFStringRef description
;
787 CFArrayRef executablePaths
= NULL
;
790 CFDictionaryRef options
= NULL
;
792 SCPreferencesRef prefs
;
793 CFStringRef unique_id
;
795 if ((data
!= NULL
) && !_SCUnserialize((CFPropertyListRef
*)&options
, data
, NULL
, 0)) {
799 if (options
!= NULL
) {
800 if (!isA_CFDictionary(options
)) {
808 if (CFDictionaryGetValueIfPresent(options
,
809 kSCKeychainOptionsAllowedExecutables
,
810 (const void **)&executablePaths
)) {
811 CFMutableArrayRef executableURLs
;
814 CFMutableDictionaryRef newOptions
;
816 executableURLs
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
817 n
= CFArrayGetCount(executablePaths
);
818 for (i
= 0; i
< n
; i
++) {
822 path
= CFArrayGetValueAtIndex(executablePaths
, i
);
823 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
824 CFDataGetBytePtr(path
),
825 CFDataGetLength(path
),
828 CFArrayAppendValue(executableURLs
, url
);
833 newOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, options
);
834 CFDictionarySetValue(newOptions
, kSCKeychainOptionsAllowedExecutables
, executableURLs
);
835 CFRelease(executableURLs
);
838 options
= newOptions
;
841 unique_id
= CFDictionaryGetValue(options
, kSCKeychainOptionsUniqueID
);
842 label
= CFDictionaryGetValue(options
, kSCKeychainOptionsLabel
);
843 description
= CFDictionaryGetValue(options
, kSCKeychainOptionsDescription
);
844 account
= CFDictionaryGetValue(options
, kSCKeychainOptionsAccount
);
845 password
= CFDictionaryGetValue(options
, kSCKeychainOptionsPassword
);
847 prefs
= __SCHelperSessionGetPreferences(session
);
848 ok
= _SCPreferencesSystemKeychainPasswordItemSet(prefs
,
864 #endif // !TARGET_OS_IPHONE
868 * SCHELPER_MSG_INTERFACE_REFRESH
870 * (out) status = SCError()
874 do_interface_refresh(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
876 CFStringRef ifName
= NULL
;
879 if ((data
!= NULL
) && !_SCUnserializeString(&ifName
, data
, NULL
, 0)) {
880 *status
= kSCStatusInvalidArgument
;
881 SCLog(TRUE
, LOG_ERR
, CFSTR("interface name not valid"));
885 if (ifName
== NULL
) {
886 *status
= kSCStatusInvalidArgument
;
887 SCLog(TRUE
, LOG_ERR
, CFSTR("interface name not valid"));
891 if (isA_CFString(ifName
)) {
892 ok
= _SCNetworkInterfaceForceConfigurationRefresh(ifName
);
896 CFSTR("interface \"%@\" not refreshed: %s"),
898 SCErrorString(*status
));
901 *status
= kSCStatusInvalidArgument
;
902 SCLog(TRUE
, LOG_ERR
, CFSTR("interface name not valid"));
913 * (in) data = prefsID
914 * (out) status = SCError()
918 do_prefs_Open(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
921 CFDictionaryRef options
;
923 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
924 CFDictionaryRef prefsInfo
= NULL
;
926 CFStringRef prefsName
;
927 CFStringRef proc_name
;
933 if ((data
!= NULL
) && !_SCUnserialize((CFPropertyListRef
*)&prefsInfo
, data
, NULL
, 0)) {
934 SCLog(TRUE
, LOG_ERR
, CFSTR("data not valid, %@"), data
);
938 if ((prefsInfo
== NULL
) || !isA_CFDictionary(prefsInfo
)) {
939 SCLog(TRUE
, LOG_ERR
, CFSTR("info not valid"));
940 if (prefsInfo
!= NULL
) CFRelease(prefsInfo
);
944 // get [optional] prefsID
945 prefsID
= CFDictionaryGetValue(prefsInfo
, CFSTR("prefsID"));
946 prefsID
= isA_CFString(prefsID
);
947 if (prefsID
!= NULL
) {
948 if (CFStringHasPrefix(prefsID
, CFSTR("/")) ||
949 CFStringHasPrefix(prefsID
, CFSTR("../")) ||
950 CFStringHasSuffix(prefsID
, CFSTR("/..")) ||
951 (CFStringFind(prefsID
, CFSTR("/../"), 0).location
!= kCFNotFound
)) {
952 // if we're trying to escape from the preferences directory
953 SCLog(TRUE
, LOG_ERR
, CFSTR("prefsID (%@) not valid"), prefsID
);
954 CFRelease(prefsInfo
);
955 *status
= kSCStatusInvalidArgument
;
960 // get [optional] options
961 options
= CFDictionaryGetValue(prefsInfo
, CFSTR("options"));
962 options
= isA_CFDictionary(options
);
964 // get preferences session "name"
965 name
= CFDictionaryGetValue(prefsInfo
, CFSTR("name"));
966 if (!isA_CFString(name
)) {
967 SCLog(TRUE
, LOG_ERR
, CFSTR("session \"name\" not valid"));
968 CFRelease(prefsInfo
);
973 pid
= CFDictionaryGetValue(prefsInfo
, CFSTR("PID"));
974 if (!isA_CFNumber(pid
)) {
975 SCLog(TRUE
, LOG_ERR
, CFSTR("PID not valid"));
976 CFRelease(prefsInfo
);
980 // get process name of caller
981 proc_name
= CFDictionaryGetValue(prefsInfo
, CFSTR("PROC_NAME"));
982 if (!isA_CFString(proc_name
)) {
983 SCLog(TRUE
, LOG_ERR
, CFSTR("process name not valid"));
984 CFRelease(prefsInfo
);
988 // build [helper] preferences "name" (used for debugging) and estabish
989 // a preferences session.
990 prefsName
= CFStringCreateWithFormat(NULL
, NULL
,
996 prefs
= SCPreferencesCreateWithOptions(NULL
, prefsName
, prefsID
, NULL
, options
);
997 CFRelease(prefsName
);
998 CFRelease(prefsInfo
);
1000 __SCHelperSessionSetPreferences(session
, prefs
);
1002 if (prefs
!= NULL
) {
1005 CFRunLoopRef rl
= CFRunLoopGetCurrent();
1007 // [temporarily] schedule notifications to ensure that we can use
1008 // the SCDynamicStore to track helper sessions
1009 ok
= SCPreferencesScheduleWithRunLoop(prefs
, rl
, kCFRunLoopDefaultMode
);
1011 (void)SCPreferencesUnscheduleFromRunLoop(prefs
, rl
, kCFRunLoopDefaultMode
);
1015 // the session now holds a references to the SCPreferencesRef
1018 *status
= SCError();
1028 * (out) status = SCError()
1029 * (out) reply = current signature + current preferences
1032 do_prefs_Access(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1035 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1036 CFDataRef signature
;
1038 if (prefs
== NULL
) {
1042 signature
= SCPreferencesGetSignature(prefs
);
1043 if (signature
!= NULL
) {
1044 const void * dictKeys
[2];
1045 const void * dictVals
[2];
1046 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1047 CFDictionaryRef replyDict
;
1049 dictKeys
[0] = CFSTR("signature");
1050 dictVals
[0] = signature
;
1052 dictKeys
[1] = CFSTR("preferences");
1053 dictVals
[1] = prefsPrivate
->prefs
;
1055 replyDict
= CFDictionaryCreate(NULL
,
1056 (const void **)&dictKeys
,
1057 (const void **)&dictVals
,
1058 sizeof(dictKeys
)/sizeof(dictKeys
[0]),
1059 &kCFTypeDictionaryKeyCallBacks
,
1060 &kCFTypeDictionaryValueCallBacks
);
1062 ok
= _SCSerialize(replyDict
, reply
, NULL
, NULL
);
1063 CFRelease(replyDict
);
1068 *status
= SCError();
1077 * (in) data = client prefs signature (NULL if check not needed)
1078 * (out) status = SCError()
1082 do_prefs_Lock(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1084 CFDataRef clientSignature
= (CFDataRef
)data
;
1086 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1087 Boolean wait
= (info
== (void *)FALSE
) ? FALSE
: TRUE
;
1089 if (prefs
== NULL
) {
1093 ok
= SCPreferencesLock(prefs
, wait
);
1095 *status
= SCError();
1099 if (clientSignature
!= NULL
) {
1100 CFDataRef serverSignature
;
1102 serverSignature
= SCPreferencesGetSignature(prefs
);
1103 if (!CFEqual(clientSignature
, serverSignature
)) {
1104 (void)SCPreferencesUnlock(prefs
);
1105 *status
= kSCStatusStale
;
1115 * (in) data = new preferences (NULL if commit w/no changes)
1116 * (out) status = SCError()
1117 * (out) reply = new signature
1120 do_prefs_Commit(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1123 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1124 CFPropertyListRef prefsData
= NULL
;
1125 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1126 CFArrayRef vpnFilter
;
1128 if (prefs
== NULL
) {
1133 ok
= _SCUnserialize(&prefsData
, data
, NULL
, 0);
1138 if (!isA_CFDictionary(prefsData
)) {
1139 *status
= kSCStatusFailed
;
1145 vpnFilter
= __SCHelperSessionGetVPNFilter(session
);
1146 if (vpnFilter
!= NULL
) {
1149 if ((prefsPrivate
->prefs
!= NULL
) && (prefsData
!= NULL
)) {
1151 CFMutableDictionaryRef prefsNew
= NULL
;
1152 CFMutableDictionaryRef prefsOld
= NULL
;
1153 CFMutableDictionaryRef prefsSave
= prefsPrivate
->prefs
;
1154 CFRange range
= CFRangeMake(0, CFArrayGetCount(vpnFilter
));
1156 for (c
= 0; c
< 2; c
++) {
1157 CFArrayRef services
;
1161 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, prefsSave
);
1164 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, prefsData
);
1168 // filter out VPN services of the specified type
1169 services
= SCNetworkServiceCopyAll(prefs
);
1170 if (services
!= NULL
) {
1172 CFIndex n
= CFArrayGetCount(services
);
1174 for (i
= 0; i
< n
; i
++) {
1175 SCNetworkServiceRef service
;
1177 service
= CFArrayGetValueAtIndex(services
, i
);
1178 if (_SCNetworkServiceIsVPN(service
)) {
1179 SCNetworkInterfaceRef child
;
1180 CFStringRef childType
= NULL
;
1181 SCNetworkInterfaceRef interface
;
1182 CFStringRef interfaceType
;
1184 interface
= SCNetworkServiceGetInterface(service
);
1185 interfaceType
= SCNetworkInterfaceGetInterfaceType(interface
);
1186 child
= SCNetworkInterfaceGetInterface(interface
);
1187 if (child
!= NULL
) {
1188 childType
= SCNetworkInterfaceGetInterfaceType(child
);
1190 if (CFEqual(interfaceType
, kSCNetworkInterfaceTypeVPN
) &&
1191 (childType
!= NULL
) &&
1192 CFArrayContainsValue(vpnFilter
, range
, childType
)) {
1193 // filter out VPN service
1194 (void) SCNetworkServiceRemove(service
);
1196 // mark all other VPN services "enabled"
1197 (void) SCNetworkServiceSetEnabled(service
, TRUE
);
1202 CFRelease(services
);
1207 prefsOld
= prefsPrivate
->prefs
;
1210 prefsNew
= prefsPrivate
->prefs
;
1215 // compare the filtered configurations
1216 ok
= _SC_CFEqual(prefsOld
, prefsNew
);
1219 if (prefsOld
!= NULL
) CFRelease(prefsOld
);
1220 if (prefsNew
!= NULL
) CFRelease(prefsNew
);
1221 prefsPrivate
->prefs
= prefsSave
;
1225 *status
= kSCStatusAccessError
;
1230 if (prefsData
!= NULL
) {
1231 if (prefsPrivate
->prefs
!= NULL
) {
1232 CFRelease(prefsPrivate
->prefs
);
1234 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, prefsData
);
1235 prefsPrivate
->accessed
= TRUE
;
1236 prefsPrivate
->changed
= TRUE
;
1239 ok
= SCPreferencesCommitChanges(prefs
);
1241 *reply
= SCPreferencesGetSignature(prefs
);
1244 *status
= SCError();
1249 if (prefsData
!= NULL
) CFRelease(prefsData
);
1257 * (out) status = SCError()
1261 do_prefs_Apply(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1264 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1266 if (prefs
== NULL
) {
1270 ok
= SCPreferencesApplyChanges(prefs
);
1272 *status
= SCError();
1282 * (out) status = SCError()
1286 do_prefs_Unlock(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1289 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1291 if (prefs
== NULL
) {
1295 ok
= SCPreferencesUnlock(prefs
);
1297 *status
= SCError();
1307 * (out) status = SCError()
1311 do_prefs_Close(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1313 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1315 if (prefs
== NULL
) {
1319 __SCHelperSessionSetPreferences(session
, NULL
);
1328 * (out) status = kSCStatusOK
1332 do_prefs_Synchronize(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1334 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1336 if (prefs
== NULL
) {
1340 SCPreferencesSynchronize(prefs
);
1341 *status
= kSCStatusOK
;
1347 #pragma mark Process commands
1351 sessionName(SCHelperSessionRef session
)
1353 CFStringRef name
= NULL
;
1354 SCPreferencesRef prefs
;
1356 prefs
= __SCHelperSessionGetPreferences(session
);
1357 if (prefs
!= NULL
) {
1358 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1360 name
= prefsPrivate
->name
;
1363 return (name
!= NULL
) ? name
: CFSTR("???");
1368 copyEntitlement(SCHelperSessionRef session
, CFStringRef entitlement
)
1370 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1372 CFTypeRef value
= NULL
;
1374 // Create the security task from the audit token
1375 task
= SecTaskCreateWithAuditToken(NULL
, sessionPrivate
->auditToken
);
1377 CFErrorRef error
= NULL
;
1379 // Get the value for the entitlement
1380 value
= SecTaskCopyValueForEntitlement(task
, entitlement
, &error
);
1381 if ((value
== NULL
) && (error
!= NULL
)) {
1382 CFIndex code
= CFErrorGetCode(error
);
1383 CFStringRef domain
= CFErrorGetDomain(error
);
1385 if (!CFEqual(domain
, kCFErrorDomainMach
) ||
1386 ((code
!= kIOReturnInvalid
) && (code
!= kIOReturnNotFound
))) {
1387 // if unexpected error
1388 SCLog(TRUE
, LOG_ERR
,
1389 CFSTR("SecTaskCopyValueForEntitlement(,\"%@\",) failed, error = %@ : %@"),
1392 sessionName(session
));
1399 SCLog(TRUE
, LOG_ERR
,
1400 CFSTR("SecTaskCreateWithAuditToken() failed: %@"),
1401 sessionName(session
));
1409 hasAuthorization(SCHelperSessionRef session
)
1411 AuthorizationRef authorization
= __SCHelperSessionGetAuthorization(session
);
1412 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1414 if (authorization
== NULL
) {
1418 #if !TARGET_OS_IPHONE
1419 if (!__SCHelperSessionUseEntitlement(session
)) {
1420 AuthorizationFlags flags
;
1421 AuthorizationItem items
[1];
1422 AuthorizationRights rights
;
1425 items
[0].name
= kSCPreferencesWriteAuthorizationRight
;
1426 items
[0].value
= NULL
;
1427 items
[0].valueLength
= 0;
1430 rights
.count
= sizeof(items
) / sizeof(items
[0]);
1431 rights
.items
= items
;
1433 flags
= kAuthorizationFlagDefaults
;
1434 flags
|= kAuthorizationFlagExtendRights
;
1435 flags
|= kAuthorizationFlagInteractionAllowed
;
1436 // flags |= kAuthorizationFlagPartialRights;
1437 // flags |= kAuthorizationFlagPreAuthorize;
1439 status
= AuthorizationCopyRights(authorization
,
1441 kAuthorizationEmptyEnvironment
,
1444 if (status
!= errAuthorizationSuccess
) {
1450 #endif // !TARGET_OS_IPHONE
1452 if (sessionPrivate
->callerWriteAccess
== UNKNOWN
) {
1453 CFArrayRef entitlement
;
1454 CFStringRef prefsID
;
1455 SCPreferencesPrivateRef prefsPrivate
;
1457 // assume that the client DOES NOT have the entitlement
1458 sessionPrivate
->callerWriteAccess
= NO
;
1460 prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
1461 prefsID
= (prefsPrivate
->prefsID
!= NULL
) ? prefsPrivate
->prefsID
: PREFS_DEFAULT_CONFIG
;
1463 entitlement
= copyEntitlement(session
, kSCWriteEntitlementName
);
1464 if (entitlement
!= NULL
) {
1465 if (isA_CFArray(entitlement
)) {
1466 if (CFArrayContainsValue(entitlement
,
1467 CFRangeMake(0, CFArrayGetCount(entitlement
)),
1469 // if client DOES have entitlement
1470 sessionPrivate
->callerWriteAccess
= YES
;
1473 SCLog(TRUE
, LOG_ERR
,
1474 CFSTR("hasAuthorization: entitlement not valid: %@"),
1475 sessionName(session
));
1478 CFRelease(entitlement
);
1481 // make an exception for VPN configuration management
1482 if (sessionPrivate
->callerWriteAccess
!= YES
) {
1483 entitlement
= copyEntitlement(session
, kSCVPNFilterEntitlementName
);
1484 if (entitlement
!= NULL
) {
1485 if (isA_CFArray(entitlement
)) {
1486 if (CFEqual(prefsID
, PREFS_DEFAULT_CONFIG
)) {
1487 // save the VPN bundle identifiers
1488 __SCHelperSessionSetVPNFilter(session
, entitlement
);
1490 // and grant a "filtered" exception
1491 sessionPrivate
->callerWriteAccess
= YES
;
1492 } else if (CFStringHasPrefix(prefsID
, CFSTR("VPN-")) &&
1493 CFStringHasSuffix(prefsID
, CFSTR(".plist"))) {
1497 range
.location
= sizeof("VPN-") - 1;
1498 range
.length
= CFStringGetLength(prefsID
)
1499 - (sizeof("VPN-") - 1) // trim VPN-
1500 - (sizeof(".plist") - 1); // trim .plist
1501 vpnID
= CFStringCreateWithSubstring(NULL
, prefsID
, range
);
1502 if (CFArrayContainsValue(entitlement
,
1503 CFRangeMake(0, CFArrayGetCount(entitlement
)),
1505 // grant an exception
1506 sessionPrivate
->callerWriteAccess
= YES
;
1512 CFRelease(entitlement
);
1516 if (sessionPrivate
->callerWriteAccess
!= YES
) {
1517 SCLog(TRUE
, LOG_ERR
,
1518 CFSTR("SCPreferences write access to \"%@\" denied, no entitlement for \"%@\""),
1520 sessionName(session
));
1524 return (sessionPrivate
->callerWriteAccess
== YES
) ? TRUE
: FALSE
;
1528 typedef Boolean (*helperFunction
) (SCHelperSessionRef session
,
1535 static const struct helper
{
1537 const char *commandName
;
1538 Boolean needsAuthorization
;
1539 helperFunction func
;
1542 { SCHELPER_MSG_AUTH
, "AUTH", FALSE
, do_Auth
, NULL
},
1544 { SCHELPER_MSG_PREFS_OPEN
, "PREFS open", FALSE
, do_prefs_Open
, NULL
},
1545 { SCHELPER_MSG_PREFS_ACCESS
, "PREFS access", TRUE
, do_prefs_Access
, NULL
},
1546 { SCHELPER_MSG_PREFS_LOCK
, "PREFS lock", TRUE
, do_prefs_Lock
, (void *)FALSE
},
1547 { SCHELPER_MSG_PREFS_LOCKWAIT
, "PREFS lock/wait", TRUE
, do_prefs_Lock
, (void *)TRUE
},
1548 { SCHELPER_MSG_PREFS_COMMIT
, "PREFS commit", TRUE
, do_prefs_Commit
, NULL
},
1549 { SCHELPER_MSG_PREFS_APPLY
, "PREFS apply", TRUE
, do_prefs_Apply
, NULL
},
1550 { SCHELPER_MSG_PREFS_UNLOCK
, "PREFS unlock", FALSE
, do_prefs_Unlock
, NULL
},
1551 { SCHELPER_MSG_PREFS_CLOSE
, "PREFS close", FALSE
, do_prefs_Close
, NULL
},
1552 { SCHELPER_MSG_PREFS_SYNCHRONIZE
, "PREFS synchronize", FALSE
, do_prefs_Synchronize
, NULL
},
1554 { SCHELPER_MSG_INTERFACE_REFRESH
, "INTERFACE refresh", TRUE
, do_interface_refresh
, NULL
},
1556 #if !TARGET_OS_IPHONE
1557 { SCHELPER_MSG_KEYCHAIN_COPY
, "KEYCHAIN copy", TRUE
, do_keychain_copy
, NULL
},
1558 { SCHELPER_MSG_KEYCHAIN_EXISTS
, "KEYCHAIN exists", TRUE
, do_keychain_exists
, NULL
},
1559 { SCHELPER_MSG_KEYCHAIN_REMOVE
, "KEYCHAIN remove", TRUE
, do_keychain_remove
, NULL
},
1560 { SCHELPER_MSG_KEYCHAIN_SET
, "KEYCHAIN set", TRUE
, do_keychain_set
, NULL
},
1561 #endif // !TARGET_OS_IPHONE
1563 { SCHELPER_MSG_EXIT
, "EXIT", FALSE
, do_Exit
, NULL
}
1565 #define nHELPERS (sizeof(helpers)/sizeof(struct helper))
1569 findCommand(uint32_t command
)
1573 for (i
= 0; i
< (int)nHELPERS
; i
++) {
1574 if (helpers
[i
].command
== command
) {
1584 newHelper(void *arg
)
1586 CFRunLoopSourceRef rls
= NULL
;
1587 SCHelperSessionRef session
= (SCHelperSessionRef
)arg
;
1588 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1590 __SCHelperSessionSetThreadName(session
);
1592 rls
= CFMachPortCreateRunLoopSource(NULL
, sessionPrivate
->mp
, 0);
1593 CFRelease(sessionPrivate
->mp
);
1596 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
1599 SCLog(debug
, LOG_DEBUG
, CFSTR("%p : start"), session
);
1601 SCLog(debug
, LOG_DEBUG
, CFSTR("%p : stop"), session
);
1609 #pragma mark Main loop
1612 // MiG generated externals and functions
1613 extern struct mig_subsystem _helper_subsystem
;
1614 extern boolean_t
helper_server(mach_msg_header_t
*, mach_msg_header_t
*);
1619 notify_server(mach_msg_header_t
*request
, mach_msg_header_t
*reply
)
1621 mach_no_senders_notification_t
*Request
= (mach_no_senders_notification_t
*)request
;
1622 mig_reply_error_t
*Reply
= (mig_reply_error_t
*)reply
;
1624 reply
->msgh_bits
= MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request
->msgh_bits
), 0);
1625 reply
->msgh_remote_port
= request
->msgh_remote_port
;
1626 reply
->msgh_size
= sizeof(mig_reply_error_t
); /* Minimal size: update as needed */
1627 reply
->msgh_local_port
= MACH_PORT_NULL
;
1628 reply
->msgh_id
= request
->msgh_id
+ 100;
1630 if ((Request
->not_header
.msgh_id
> MACH_NOTIFY_LAST
) ||
1631 (Request
->not_header
.msgh_id
< MACH_NOTIFY_FIRST
)) {
1632 Reply
->NDR
= NDR_record
;
1633 Reply
->RetCode
= MIG_BAD_ID
;
1634 return FALSE
; /* if this is not a notification message */
1637 switch (Request
->not_header
.msgh_id
) {
1638 case MACH_NOTIFY_NO_SENDERS
: {
1639 SCHelperSessionRef session
;
1641 __MACH_PORT_DEBUG(TRUE
, "*** notify_server MACH_NOTIFY_NO_SENDERS", Request
->not_header
.msgh_local_port
);
1644 session
= __SCHelperSessionFindWithPort(Request
->not_header
.msgh_local_port
);
1645 if (session
!= NULL
) {
1646 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1648 // release CFMachPort *and* SCHelperSession
1649 CFMachPortInvalidate(sessionPrivate
->mp
);
1652 __MACH_PORT_DEBUG(TRUE
, "*** notify_server after invalidate", Request
->not_header
.msgh_local_port
);
1654 // and, lastly, remove our receive right.
1655 (void) mach_port_mod_refs(mach_task_self(),
1656 Request
->not_header
.msgh_local_port
,
1657 MACH_PORT_RIGHT_RECEIVE
, -1);
1659 Reply
->Head
.msgh_bits
= 0;
1660 Reply
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1661 Reply
->RetCode
= KERN_SUCCESS
;
1669 SCLog(TRUE
, LOG_ERR
, CFSTR("HELP!, Received notification: port=%d, msgh_id=%d"),
1670 Request
->not_header
.msgh_local_port
,
1671 Request
->not_header
.msgh_id
);
1673 Reply
->NDR
= NDR_record
;
1674 Reply
->RetCode
= MIG_BAD_ID
;
1675 return FALSE
; /* if this is not a notification we are handling */
1681 helper_demux(mach_msg_header_t
*request
, mach_msg_header_t
*reply
)
1683 Boolean processed
= FALSE
;
1686 * (attempt to) process SCHelper requests.
1688 processed
= helper_server(request
, reply
);
1694 * (attempt to) process (NO MORE SENDERS) notification messages.
1696 processed
= notify_server(request
, reply
);
1702 * unknown message ID, log and return an error.
1704 SCLog(TRUE
, LOG_ERR
, CFSTR("helper_demux(): unknown message ID (%d) received"), request
->msgh_id
);
1705 reply
->msgh_bits
= MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request
->msgh_bits
), 0);
1706 reply
->msgh_remote_port
= request
->msgh_remote_port
;
1707 reply
->msgh_size
= sizeof(mig_reply_error_t
); /* Minimal size */
1708 reply
->msgh_local_port
= MACH_PORT_NULL
;
1709 reply
->msgh_id
= request
->msgh_id
+ 100;
1710 ((mig_reply_error_t
*)reply
)->NDR
= NDR_record
;
1711 ((mig_reply_error_t
*)reply
)->RetCode
= MIG_BAD_ID
;
1717 #define MACH_MSG_BUFFER_SIZE 128
1721 helperCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1723 mig_reply_error_t
* bufRequest
= msg
;
1724 uint32_t bufReply_q
[MACH_MSG_BUFFER_SIZE
/sizeof(uint32_t)];
1725 mig_reply_error_t
* bufReply
= (mig_reply_error_t
*)bufReply_q
;
1726 static CFIndex bufSize
= 0;
1727 mach_msg_return_t mr
;
1731 // get max size for MiG reply buffers
1732 bufSize
= _helper_subsystem
.maxsize
;
1734 // check if our on-the-stack reply buffer will be big enough
1735 if (bufSize
> sizeof(bufReply_q
)) {
1736 SCLog(TRUE
, LOG_NOTICE
,
1737 CFSTR("helperCallback(): buffer size should be increased > %d"),
1738 _helper_subsystem
.maxsize
);
1742 if (bufSize
> sizeof(bufReply_q
)) {
1743 bufReply
= CFAllocatorAllocate(NULL
, _helper_subsystem
.maxsize
, 0);
1745 bufReply
->RetCode
= 0;
1747 /* we have a request message */
1748 (void) helper_demux(&bufRequest
->Head
, &bufReply
->Head
);
1750 if (!(bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)) {
1751 if (bufReply
->RetCode
== MIG_NO_REPLY
) {
1752 bufReply
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1753 } else if ((bufReply
->RetCode
!= KERN_SUCCESS
) &&
1754 (bufRequest
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)) {
1756 * destroy the request - but not the reply port
1758 bufRequest
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1759 mach_msg_destroy(&bufRequest
->Head
);
1763 if (bufReply
->Head
.msgh_remote_port
!= MACH_PORT_NULL
) {
1767 * We don't want to block indefinitely because the client
1768 * isn't receiving messages from the reply port.
1769 * If we have a send-once right for the reply port, then
1770 * this isn't a concern because the send won't block.
1771 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
1772 * To avoid falling off the kernel's fast RPC path unnecessarily,
1773 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
1776 options
= MACH_SEND_MSG
;
1777 if (MACH_MSGH_BITS_REMOTE(bufReply
->Head
.msgh_bits
) != MACH_MSG_TYPE_MOVE_SEND_ONCE
) {
1778 options
|= MACH_SEND_TIMEOUT
;
1780 mr
= mach_msg(&bufReply
->Head
, /* msg */
1781 options
, /* option */
1782 bufReply
->Head
.msgh_size
, /* send_size */
1784 MACH_PORT_NULL
, /* rcv_name */
1785 MACH_MSG_TIMEOUT_NONE
, /* timeout */
1786 MACH_PORT_NULL
); /* notify */
1788 /* Has a message error occurred? */
1790 case MACH_SEND_INVALID_DEST
:
1791 case MACH_SEND_TIMED_OUT
:
1794 /* Includes success case. */
1799 if (bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
1800 mach_msg_destroy(&bufReply
->Head
);
1805 if (bufReply
!= (mig_reply_error_t
*)bufReply_q
)
1806 CFAllocatorDeallocate(NULL
, bufReply
);
1812 initMPCopyDescription(const void *info
)
1814 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<SCHelper MP>"));
1820 _helperinit(mach_port_t server
,
1821 mach_port_t
*newSession
,
1823 audit_token_t audit_token
)
1825 CFMachPortContext context
= { 0
1829 , initMPCopyDescription
1832 mach_port_t oldNotify
;
1833 SCHelperSessionRef session
;
1834 SCHelperSessionPrivateRef sessionPrivate
;
1835 pthread_attr_t tattr
;
1838 session
= __SCHelperSessionFindWithPort(server
);
1839 if (session
!= NULL
) {
1841 SCLog(TRUE
, LOG_DEBUG
, CFSTR("_helperinit(): session is already open."));
1843 *status
= kSCStatusFailed
; /* you can't re-open an "open" session */
1844 return KERN_SUCCESS
;
1847 session
= __SCHelperSessionCreate(NULL
);
1848 sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1850 // create per-session port
1851 (void) mach_port_allocate(mach_task_self(),
1852 MACH_PORT_RIGHT_RECEIVE
,
1853 &sessionPrivate
->port
);
1854 *newSession
= sessionPrivate
->port
;
1857 // Note: we create the CFMachPort *before* we insert a send
1858 // right present to ensure that CF does not establish
1859 // its dead name notification.
1861 context
.info
= (void *)session
;
1862 sessionPrivate
->mp
= _SC_CFMachPortCreateWithPort("SCHelper/session",
1867 /* Request a notification when/if the client dies */
1868 kr
= mach_port_request_notification(mach_task_self(),
1870 MACH_NOTIFY_NO_SENDERS
,
1873 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
1875 if (kr
!= KERN_SUCCESS
) {
1876 SCLog(TRUE
, LOG_ERR
, CFSTR("_helperinit() mach_port_request_notification() failed: %s"), mach_error_string(kr
));
1878 // clean up CFMachPort, mach port rights
1879 CFMachPortInvalidate(sessionPrivate
->mp
);
1880 CFRelease(sessionPrivate
->mp
);
1881 sessionPrivate
->mp
= NULL
;
1882 (void) mach_port_mod_refs(mach_task_self(), *newSession
, MACH_PORT_RIGHT_RECEIVE
, -1);
1883 *newSession
= MACH_PORT_NULL
;
1884 *status
= kSCStatusFailed
;
1888 if (oldNotify
!= MACH_PORT_NULL
) {
1889 SCLog(TRUE
, LOG_ERR
, CFSTR("_helperinit(): oldNotify != MACH_PORT_NULL"));
1892 // add send right (that will be passed back to the client)
1893 (void) mach_port_insert_right(mach_task_self(),
1896 MACH_MSG_TYPE_MAKE_SEND
);
1899 sessionPrivate
->auditToken
= audit_token
;
1902 // Note: at this time we should be holding ONE send right and
1903 // ONE receive right to the server. The send right is
1904 // moved to the caller.
1907 // start per-session thread
1908 pthread_attr_init(&tattr
);
1909 pthread_attr_setscope(&tattr
, PTHREAD_SCOPE_SYSTEM
);
1910 pthread_attr_setdetachstate(&tattr
, PTHREAD_CREATE_DETACHED
);
1911 pthread_attr_setstacksize(&tattr
, 96 * 1024); // each thread gets a 96K stack
1912 pthread_create(&tid
, &tattr
, newHelper
, (void *)session
);
1913 pthread_attr_destroy(&tattr
);
1915 *status
= kSCStatusOK
;
1920 return KERN_SUCCESS
;
1926 _helperexec(mach_port_t server
,
1928 xmlData_t dataRef
, /* raw XML bytes */
1929 mach_msg_type_number_t dataLen
,
1930 xmlData_t traceRef
, /* raw XML bytes */
1931 mach_msg_type_number_t traceLen
,
1933 xmlDataOut_t
*replyRef
, /* raw XML bytes */
1934 mach_msg_type_number_t
*replyLen
)
1936 CFStringRef backtrace
= NULL
;
1937 CFDataRef data
= NULL
;
1939 CFDataRef reply
= NULL
;
1940 SCHelperSessionRef session
;
1942 *status
= kSCStatusOK
;
1946 if ((dataRef
!= NULL
) && (dataLen
> 0)) {
1947 if (!_SCUnserializeData(&data
, (void *)dataRef
, dataLen
)) {
1948 *status
= SCError();
1952 if ((traceRef
!= NULL
) && (traceLen
> 0)) {
1953 if (!_SCUnserializeString(&backtrace
, NULL
, (void *)traceRef
, traceLen
)) {
1954 *status
= SCError();
1958 if (*status
!= kSCStatusOK
) {
1962 session
= __SCHelperSessionFindWithPort(server
);
1963 if (session
== NULL
) {
1964 *status
= kSCStatusFailed
; /* you must have an open session to play */
1968 i
= findCommand(msgID
);
1970 SCLog(TRUE
, LOG_ERR
, CFSTR("received unknown command : %u"), msgID
);
1971 *status
= kSCStatusInvalidArgument
;
1975 SCLog(debug
, LOG_DEBUG
,
1976 CFSTR("%p : processing command \"%s\"%s"),
1978 helpers
[i
].commandName
,
1979 (data
!= NULL
) ? " w/data" : "");
1981 if (helpers
[i
].needsAuthorization
&& !hasAuthorization(session
)) {
1982 SCLog(debug
, LOG_DEBUG
,
1983 CFSTR("%p : command \"%s\" : not authorized"),
1985 helpers
[i
].commandName
);
1986 *status
= kSCStatusAccessError
;
1989 if (*status
== kSCStatusOK
) {
1990 if (backtrace
!= NULL
) {
1991 __SCHelperSessionAddBacktrace(session
, backtrace
, helpers
[i
].commandName
);
1993 (*helpers
[i
].func
)(session
, helpers
[i
].info
, data
, status
, &reply
);
1996 if ((*status
!= -1) || (reply
!= NULL
)) {
1999 SCLog(debug
, LOG_DEBUG
,
2000 CFSTR("%p : sending status %u%s"),
2003 (reply
!= NULL
) ? " w/reply" : "");
2005 /* serialize the data */
2006 if (reply
!= NULL
) {
2009 ok
= _SCSerializeData(reply
, (void **)replyRef
, &len
);
2014 *status
= SCError();
2022 if (data
!= NULL
) CFRelease(data
);
2023 if (backtrace
!= NULL
) CFRelease(backtrace
);
2024 if (reply
!= NULL
) CFRelease(reply
);
2025 return KERN_SUCCESS
;
2030 helperMPCopyDescription(const void *info
)
2032 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<main SCHelper MP>"));
2037 init_MiG_1(const launch_data_t l_obj
, const char *name
, void *info
)
2039 CFMachPortContext context
= { 0
2043 , helperMPCopyDescription
2045 launch_data_type_t l_type
;
2047 int *n_listeners
= (int *)info
;
2048 CFRunLoopSourceRef rls
;
2049 mach_port_t service_port
;
2051 // get the mach port
2052 l_type
= (l_obj
!= NULL
) ? launch_data_get_type(l_obj
) : 0;
2053 if (l_type
!= LAUNCH_DATA_MACHPORT
) {
2054 SCLog(TRUE
, LOG_ERR
,
2055 CFSTR("SCHelper: error w/MachServices \"%s\" port (%p, %d)"),
2056 (name
!= NULL
) ? name
: "?",
2061 service_port
= launch_data_get_machport(l_obj
);
2063 // add a run loop source to listen for new requests
2064 mp
= _SC_CFMachPortCreateWithPort("SCHelper/server",
2068 rls
= CFMachPortCreateRunLoopSource(NULL
, mp
, 0);
2070 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
2073 *n_listeners
= *n_listeners
+ 1;
2080 init_MiG(launch_data_t l_reply
, int *n_listeners
)
2082 launch_data_t l_machservices
;
2083 launch_data_type_t l_type
;
2085 l_machservices
= launch_data_dict_lookup(l_reply
, LAUNCH_JOBKEY_MACHSERVICES
);
2086 l_type
= (l_machservices
!= NULL
) ? launch_data_get_type(l_machservices
) : 0;
2087 if (l_type
!= LAUNCH_DATA_DICTIONARY
) {
2088 SCLog(TRUE
, LOG_ERR
,
2089 CFSTR("SCHelper: error w/" LAUNCH_JOBKEY_MACHSERVICES
" (%p, %d)"),
2095 launch_data_dict_iterate(l_machservices
, init_MiG_1
, (void *)n_listeners
);
2104 static const struct option longopts
[] = {
2105 { "debug", no_argument
, 0, 'd' },
2111 main(int argc
, char **argv
)
2113 Boolean done
= FALSE
;
2115 int gen_reported
= 0;
2117 launch_data_t l_msg
;
2118 launch_data_t l_reply
;
2119 launch_data_type_t l_type
;
2120 int n_listeners
= 0;
2121 // extern int optind;
2125 openlog("SCHelper", LOG_CONS
|LOG_PID
, LOG_DAEMON
);
2127 // process any arguments
2128 while ((opt
= getopt_long(argc
, argv
, "d", longopts
, &opti
)) != -1) {
2134 // if (strcmp(longopts[opti].name, "debug") == 1) {
2139 SCLog(TRUE
, LOG_ERR
,
2140 CFSTR("ignoring unknown or ambiguous command line option"));
2147 if (geteuid() != 0) {
2148 SCLog(TRUE
, LOG_ERR
, CFSTR("%s"), strerror(EACCES
));
2152 main_runLoop
= CFRunLoopGetCurrent();
2154 l_msg
= launch_data_new_string(LAUNCH_KEY_CHECKIN
);
2155 l_reply
= launch_msg(l_msg
);
2156 launch_data_free(l_msg
);
2157 l_type
= (l_reply
!= NULL
) ? launch_data_get_type(l_reply
) : 0;
2158 if (l_type
!= LAUNCH_DATA_DICTIONARY
) {
2159 SCLog(TRUE
, LOG_ERR
,
2160 CFSTR("SCHelper: error w/launchd " LAUNCH_KEY_CHECKIN
" dictionary (%p, %d)"),
2167 err
= init_MiG(l_reply
, &n_listeners
);
2174 if (l_reply
!= NULL
) launch_data_free(l_reply
);
2176 if ((err
!= 0) || (n_listeners
== 0)) {
2180 pthread_setname_np("SCHelper main thread");
2186 rlStatus
= CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 15.0, TRUE
);
2188 pthread_mutex_lock(&sessions_lock
);
2190 if (sessions
!= NULL
) {
2191 if (rlStatus
== kCFRunLoopRunTimedOut
) {
2194 if ((CFSetGetCount(sessions
) == 0) && (sessions_closed
== 0)) {
2195 // if we don't have any open sessions and no
2196 // sessions have recently been closed
2203 gen_current
= sessions_generation
;
2204 sessions_closed
= 0;
2206 if (!done
&& (idle
>= (2 * 60 / 15))) {
2207 if (gen_reported
!= gen_current
) {
2208 FILE *logFile
= NULL
;
2210 SCLog(TRUE
, LOG_NOTICE
, CFSTR("active (but IDLE) sessions"));
2211 CFSetApplyFunction(sessions
, __SCHelperSessionLog
, (void *)&logFile
);
2212 gen_reported
= gen_current
;
2214 if (logFile
!= NULL
) {
2215 (void) fclose(logFile
);
2221 pthread_mutex_unlock(&sessions_lock
);