2 * Copyright (c) 2005-2015 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@
29 #include <bsm/libbsm.h>
30 #include <servers/bootstrap.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"
49 #pragma mark SCHelper session managment
53 // entitlement used to control read (or write) access to a given "prefsID"
55 #define kSCReadEntitlementName CFSTR("com.apple.SystemConfiguration.SCPreferences-read-access")
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 callerReadAccess
;
88 lazyBoolean callerWriteAccess
;
90 // configuration filtering
91 lazyBoolean isSetChange
; // only network "set" changes
92 lazyBoolean isVPNChange
; // only VPN configuration changes
96 SCPreferencesRef prefs
;
99 CFMutableSetRef backtraces
;
101 } SCHelperSessionPrivate
, *SCHelperSessionPrivateRef
;
104 static CFStringRef
__SCHelperSessionCopyDescription (CFTypeRef cf
);
105 static void __SCHelperSessionDeallocate (CFTypeRef cf
);
108 static void __SCHelperSessionLogBacktrace (const void *value
, void *context
);
111 static CFTypeID __kSCHelperSessionTypeID
= _kCFRuntimeNotATypeID
;
112 static Boolean debug
= FALSE
;
113 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
114 static CFRunLoopRef main_runLoop
= NULL
;
115 static CFMutableSetRef sessions
= NULL
;
116 static int sessions_closed
= 0; // count of sessions recently closed
117 static int sessions_generation
= 0;
118 static pthread_mutex_t sessions_lock
= PTHREAD_MUTEX_INITIALIZER
;
122 #pragma mark Helper session management
125 #if !TARGET_OS_IPHONE
127 __SCHelperSessionUseEntitlement(SCHelperSessionRef session
)
129 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
131 return sessionPrivate
->use_entitlement
;
133 #endif //!TARGET_OS_IPHONE
136 static AuthorizationRef
137 __SCHelperSessionGetAuthorization(SCHelperSessionRef session
)
139 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
141 return sessionPrivate
->authorization
;
146 __SCHelperSessionSetAuthorization(SCHelperSessionRef session
, CFTypeRef authorizationData
)
149 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
152 * On OSX, the authorizationData can either be a CFData-wrapped/externalized
153 * "authorization" or a CFString indicating that we should check entitlements.
155 * On iOS, the authorizationData is a CFString indicating that we should
156 * check entitlements.
158 pthread_mutex_lock(&sessionPrivate
->lock
);
160 if (sessionPrivate
->authorization
!= NULL
) {
161 #if !TARGET_OS_IPHONE
162 if (!__SCHelperSessionUseEntitlement(session
)) {
165 status
= AuthorizationFree(sessionPrivate
->authorization
, kAuthorizationFlagDefaults
);
166 // status = AuthorizationFree(sessionPrivate->authorization, kAuthorizationFlagDestroyRights);
167 if (status
!= errAuthorizationSuccess
) {
168 SC_log(LOG_DEBUG
, "AuthorizationFree() failed: status = %d",
172 CFRelease(sessionPrivate
->authorization
);
174 #else //!TARGET_OS_IPHONE
175 CFRelease(sessionPrivate
->authorization
);
176 #endif //!TARGET_OS_IPHONE
177 sessionPrivate
->authorization
= NULL
;
178 sessionPrivate
->use_entitlement
= FALSE
;
181 #if !TARGET_OS_IPHONE
182 if (isA_CFData(authorizationData
)) {
183 AuthorizationExternalForm extForm
;
185 if (CFDataGetLength(authorizationData
) == sizeof(extForm
.bytes
)) {
188 bcopy(CFDataGetBytePtr(authorizationData
), extForm
.bytes
, sizeof(extForm
.bytes
));
189 status
= AuthorizationCreateFromExternalForm(&extForm
,
190 &sessionPrivate
->authorization
);
191 if (status
!= errAuthorizationSuccess
) {
192 SC_log(LOG_NOTICE
, "AuthorizationCreateFromExternalForm() failed: status = %d",
194 sessionPrivate
->authorization
= NULL
;
198 } else if (isA_CFString(authorizationData
)) {
199 sessionPrivate
->authorization
= (void *)CFRetain(authorizationData
);
200 sessionPrivate
->use_entitlement
= TRUE
;
202 #else //!TARGET_OS_IPHONE
203 if (isA_CFString(authorizationData
)) {
204 sessionPrivate
->authorization
= (void *)CFRetain(authorizationData
);
205 sessionPrivate
->use_entitlement
= TRUE
;
207 #endif //!TARGET_OS_IPHONE
209 pthread_mutex_unlock(&sessionPrivate
->lock
);
215 static SCPreferencesRef
216 __SCHelperSessionGetPreferences(SCHelperSessionRef session
)
218 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
220 return sessionPrivate
->prefs
;
225 __SCHelperSessionSetThreadName(SCHelperSessionRef session
)
231 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
234 if (sessionPrivate
->mp
== NULL
) {
238 if (sessionPrivate
->prefs
!= NULL
) {
239 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
241 if (prefsPrivate
->name
!= NULL
) {
242 caller
= _SC_cfstring_to_cstring(prefsPrivate
->name
,
245 kCFStringEncodingUTF8
);
248 path
= (prefsPrivate
->newPath
!= NULL
) ? prefsPrivate
->newPath
: prefsPrivate
->path
;
250 path_s
= strrchr(path
, '/');
251 if (path_s
!= NULL
) {
257 if (caller
!= NULL
) {
258 snprintf(name
, sizeof(name
), "SESSION|%p|%s|%s%s",
259 (void *)(uintptr_t)CFMachPortGetPort(sessionPrivate
->mp
),
260 (caller
!= NULL
) ? caller
: "?",
261 (path_s
!= NULL
) ? "*/" : "",
262 (path
!= NULL
) ? path
: "?");
263 CFAllocatorDeallocate(NULL
, caller
);
265 snprintf(name
, sizeof(name
), "SESSION|%p",
266 (void *)(uintptr_t)CFMachPortGetPort(sessionPrivate
->mp
));
269 pthread_setname_np(name
);
276 __SCHelperSessionSetPreferences(SCHelperSessionRef session
, SCPreferencesRef prefs
)
278 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
280 pthread_mutex_lock(&sessionPrivate
->lock
);
285 if (sessionPrivate
->prefs
!= NULL
) {
286 SC_log(LOG_INFO
, "%p : close", session
);
287 CFRelease(sessionPrivate
->prefs
);
290 SC_log(LOG_INFO
, "%p : open, prefs = %@", session
, prefs
);
292 sessionPrivate
->prefs
= prefs
;
294 __SCHelperSessionSetThreadName(session
);
296 pthread_mutex_unlock(&sessionPrivate
->lock
);
303 __SCHelperSessionSetNetworkSetFilter(SCHelperSessionRef session
, Boolean setChange
)
305 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
307 pthread_mutex_lock(&sessionPrivate
->lock
);
309 sessionPrivate
->isSetChange
= setChange
? YES
: NO
;
311 pthread_mutex_unlock(&sessionPrivate
->lock
);
318 __SCHelperSessionUseNetworkSetFilter(SCHelperSessionRef session
)
320 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
322 return (sessionPrivate
->isSetChange
== YES
) ? TRUE
: FALSE
;
327 __SCHelperSessionSetVPNFilter(SCHelperSessionRef session
, Boolean vpnChange
, CFArrayRef vpnTypes
)
329 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
331 pthread_mutex_lock(&sessionPrivate
->lock
);
333 sessionPrivate
->isVPNChange
= vpnChange
? YES
: NO
;
335 if (vpnTypes
!= NULL
) {
338 if (sessionPrivate
->vpnTypes
!= NULL
) {
339 CFRelease(sessionPrivate
->vpnTypes
);
341 sessionPrivate
->vpnTypes
= vpnTypes
;
343 pthread_mutex_unlock(&sessionPrivate
->lock
);
350 __SCHelperSessionUseVPNFilter(SCHelperSessionRef session
, CFArrayRef
*vpnTypes
)
352 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
354 *vpnTypes
= sessionPrivate
->vpnTypes
;
355 return (sessionPrivate
->vpnTypes
!= NULL
) ? TRUE
: FALSE
;
360 __SCHelperSessionLog(const void *value
, void *context
)
362 SCHelperSessionRef session
= (SCHelperSessionRef
)value
;
363 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
364 FILE **logFile
= (FILE **)context
;
366 pthread_mutex_lock(&sessionPrivate
->lock
);
368 if ((sessionPrivate
->mp
!= NULL
) && (sessionPrivate
->prefs
!= NULL
)) {
369 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
371 SC_log(LOG_INFO
, " %p {port = %p, caller = %@, path = %s%s}",
373 (void *)(uintptr_t)CFMachPortGetPort(sessionPrivate
->mp
),
375 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
,
376 prefsPrivate
->locked
? ", locked" : "");
378 if ((sessionPrivate
->backtraces
!= NULL
) &&
379 (CFSetGetCount(sessionPrivate
->backtraces
) > 0)) {
380 // log/report all collected backtraces
381 CFSetApplyFunction(sessionPrivate
->backtraces
,
382 __SCHelperSessionLogBacktrace
,
385 // to ensure that we don't log the same backtraces multiple
386 // times we remove any reported traces
387 CFRelease(sessionPrivate
->backtraces
);
388 sessionPrivate
->backtraces
= NULL
;
392 pthread_mutex_unlock(&sessionPrivate
->lock
);
401 static const CFRuntimeClass __SCHelperSessionClass
= {
403 "SCHelperSession", // className
406 __SCHelperSessionDeallocate
, // dealloc
409 NULL
, // copyFormattingDesc
410 __SCHelperSessionCopyDescription
// copyDebugDesc
415 __SCHelperSessionCopyDescription(CFTypeRef cf
)
417 CFAllocatorRef allocator
= CFGetAllocator(cf
);
418 CFMutableStringRef result
;
419 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)cf
;
421 pthread_mutex_lock(&sessionPrivate
->lock
);
423 result
= CFStringCreateMutable(allocator
, 0);
424 CFStringAppendFormat(result
, NULL
, CFSTR("<SCHelperSession %p [%p]> {"), cf
, allocator
);
425 CFStringAppendFormat(result
, NULL
, CFSTR("authorization = %p"), sessionPrivate
->authorization
);
426 if (sessionPrivate
->mp
!= NULL
) {
427 CFStringAppendFormat(result
, NULL
,
428 CFSTR(", mp = %p (port = 0x%x)"),
430 CFMachPortGetPort(sessionPrivate
->mp
));
432 if (sessionPrivate
->prefs
!= NULL
) {
433 CFStringAppendFormat(result
, NULL
, CFSTR(", prefs = %@"), sessionPrivate
->prefs
);
435 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
437 pthread_mutex_unlock(&sessionPrivate
->lock
);
444 __SCHelperSessionDeallocate(CFTypeRef cf
)
446 SCHelperSessionRef session
= (SCHelperSessionRef
)cf
;
447 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
449 // we're releasing "a" session so take the global lock...
450 pthread_mutex_lock(&sessions_lock
);
453 __SCHelperSessionSetAuthorization(session
, NULL
);
454 __SCHelperSessionSetPreferences(session
, NULL
);
455 __SCHelperSessionSetNetworkSetFilter(session
, FALSE
);
456 __SCHelperSessionSetVPNFilter(session
, FALSE
, NULL
);
457 pthread_mutex_destroy(&sessionPrivate
->lock
);
458 if (sessionPrivate
->backtraces
!= NULL
) {
459 CFRelease(sessionPrivate
->backtraces
);
462 // we no longer need/want to track this session
463 CFSetRemoveValue(sessions
, sessionPrivate
);
464 sessions_generation
++;
467 // release the global lock, wake up the main runloop, all done
468 pthread_mutex_unlock(&sessions_lock
);
469 CFRunLoopWakeUp(main_runLoop
);
476 __SCHelperSessionInitialize(void)
478 __kSCHelperSessionTypeID
= _CFRuntimeRegisterClass(&__SCHelperSessionClass
);
483 static SCHelperSessionRef
484 __SCHelperSessionCreate(CFAllocatorRef allocator
)
486 SCHelperSessionPrivateRef sessionPrivate
;
489 // initialize runtime
490 pthread_once(&initialized
, __SCHelperSessionInitialize
);
493 size
= sizeof(SCHelperSessionPrivate
) - sizeof(CFRuntimeBase
);
494 sessionPrivate
= (SCHelperSessionPrivateRef
)_CFRuntimeCreateInstance(allocator
,
495 __kSCHelperSessionTypeID
,
498 if (sessionPrivate
== NULL
) {
502 if (pthread_mutex_init(&sessionPrivate
->lock
, NULL
) != 0) {
503 SC_log(LOG_NOTICE
, "pthread_mutex_init(): failure to initialize per session lock");
504 CFRelease(sessionPrivate
);
507 sessionPrivate
->authorization
= NULL
;
508 sessionPrivate
->use_entitlement
= FALSE
;
509 sessionPrivate
->port
= MACH_PORT_NULL
;
510 sessionPrivate
->mp
= NULL
;
511 sessionPrivate
->callerReadAccess
= UNKNOWN
;
512 sessionPrivate
->callerWriteAccess
= UNKNOWN
;
513 sessionPrivate
->isSetChange
= UNKNOWN
;
514 sessionPrivate
->isVPNChange
= UNKNOWN
;
515 sessionPrivate
->vpnTypes
= NULL
;
516 sessionPrivate
->prefs
= NULL
;
517 sessionPrivate
->backtraces
= NULL
;
519 // keep track this session
520 pthread_mutex_lock(&sessions_lock
);
521 if (sessions
== NULL
) {
522 const CFSetCallBacks mySetCallBacks
= { 0, NULL
, NULL
, CFCopyDescription
, CFEqual
, CFHash
};
524 // create a non-retaining set
525 sessions
= CFSetCreateMutable(NULL
, 0, &mySetCallBacks
);
527 CFSetAddValue(sessions
, sessionPrivate
);
528 sessions_generation
++;
529 pthread_mutex_unlock(&sessions_lock
);
531 return (SCHelperSessionRef
)sessionPrivate
;
538 static SCHelperSessionRef
539 __SCHelperSessionFindWithPort(mach_port_t port
)
541 SCHelperSessionRef session
= NULL
;
543 // keep track this session
544 pthread_mutex_lock(&sessions_lock
);
545 if (sessions
!= NULL
) {
547 CFIndex n
= CFSetGetCount(sessions
);
548 const void * vals_q
[16];
549 const void ** vals
= vals_q
;
551 if (n
> (CFIndex
)(sizeof(vals_q
) / sizeof(SCHelperSessionRef
)))
552 vals
= CFAllocatorAllocate(NULL
, n
* sizeof(CFStringRef
), 0);
553 CFSetGetValues(sessions
, vals
);
554 for (i
= 0; i
< n
; i
++) {
555 SCHelperSessionPrivateRef sessionPrivate
;
557 sessionPrivate
= (SCHelperSessionPrivateRef
)vals
[i
];
558 if (sessionPrivate
->port
== port
) {
559 session
= (SCHelperSessionRef
)sessionPrivate
;
564 CFAllocatorDeallocate(NULL
, vals
);
566 pthread_mutex_unlock(&sessions_lock
);
573 #pragma mark Session backtrace logging
577 __SCHelperSessionAddBacktrace(SCHelperSessionRef session
, CFStringRef backtrace
, const char * command
)
579 CFStringRef logEntry
;
580 SCPreferencesRef prefs
;
581 SCPreferencesPrivateRef prefsPrivate
;
582 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
584 prefs
= __SCHelperSessionGetPreferences((SCHelperSessionRef
)sessionPrivate
);
589 prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
591 logEntry
= CFStringCreateWithFormat(NULL
, NULL
,
592 CFSTR("%@ [%s]: %s\n\n%@"),
594 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
,
598 pthread_mutex_lock(&sessionPrivate
->lock
);
600 if (sessionPrivate
->backtraces
== NULL
) {
601 sessionPrivate
->backtraces
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
603 CFSetSetValue(sessionPrivate
->backtraces
, logEntry
);
605 pthread_mutex_unlock(&sessionPrivate
->lock
);
613 __SCHelperSessionLogBacktrace(const void *value
, void *context
)
615 CFSetRef backtrace
= (CFSetRef
)value
;
616 FILE **logFile
= (FILE **)context
;
618 if (*logFile
== NULL
) {
621 struct timeval tv_now
;
623 (void)gettimeofday(&tv_now
, NULL
);
624 (void)localtime_r(&tv_now
.tv_sec
, &tm_now
);
628 "/Library/Logs/CrashReporter/SCHelper-%4d-%02d-%02d-%02d%02d%02d.log",
629 tm_now
.tm_year
+ 1900,
636 *logFile
= fopen(path
, "a");
637 if (*logFile
== NULL
) {
638 // if log file could not be created
642 SC_log(LOG_INFO
, "created backtrace log: %s", path
);
645 SCPrint(TRUE
, *logFile
, CFSTR("%@\n"), backtrace
);
657 * (out) status = SCError()
661 do_Exit(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
670 * (in) data = authorizationDict (in 2 flavors)
671 * kSCHelperAuthAuthorization - use provided AuthorizationExternalForm
672 * kSCHelperAuthCallerInfo - use entitlement
673 * (out) status = OSStatus
677 do_Auth(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
679 CFDictionaryRef authorizationDict
;
680 #if !TARGET_OS_IPHONE
681 CFDataRef authorizationData
= NULL
;
685 if (_SCUnserialize((CFPropertyListRef
*)&authorizationDict
, data
, NULL
, 0) == FALSE
) {
689 if (authorizationDict
== NULL
) {
693 if (!isA_CFDictionary(authorizationDict
)) {
694 CFRelease(authorizationDict
);
698 #if !TARGET_OS_IPHONE
699 authorizationData
= CFDictionaryGetValue(authorizationDict
, kSCHelperAuthAuthorization
);
700 if (authorizationData
!= NULL
&& isA_CFData(authorizationData
)) {
701 ok
= __SCHelperSessionSetAuthorization(session
, authorizationData
);
705 CFStringRef authorizationInfo
;
707 authorizationInfo
= CFDictionaryGetValue(authorizationDict
, kSCHelperAuthCallerInfo
);
708 if (authorizationInfo
!= NULL
&& isA_CFString(authorizationInfo
)) {
709 ok
= __SCHelperSessionSetAuthorization(session
, authorizationInfo
);
713 CFRelease(authorizationDict
);
714 *status
= ok
? 0 : 1;
719 #if !TARGET_OS_IPHONE
723 * SCHELPER_MSG_KEYCHAIN_COPY
724 * (in) data = unique_id
725 * (out) status = SCError()
726 * (out) reply = password
729 do_keychain_copy(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
732 SCPreferencesRef prefs
;
733 CFStringRef unique_id
= NULL
;
735 if ((data
!= NULL
) && !_SCUnserializeString(&unique_id
, data
, NULL
, 0)) {
739 if (unique_id
!= NULL
) {
740 if (isA_CFString(unique_id
)) {
741 prefs
= __SCHelperSessionGetPreferences(session
);
742 *reply
= _SCPreferencesSystemKeychainPasswordItemCopy(prefs
, unique_id
);
743 if (*reply
== NULL
) {
749 CFRelease(unique_id
);
757 * SCHELPER_MSG_KEYCHAIN_EXISTS
758 * (in) data = unique_id
759 * (out) status = SCError()
763 do_keychain_exists(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
766 SCPreferencesRef prefs
;
767 CFStringRef unique_id
= NULL
;
769 if ((data
!= NULL
) && !_SCUnserializeString(&unique_id
, data
, NULL
, 0)) {
773 if (unique_id
!= NULL
) {
774 if (isA_CFString(unique_id
)) {
775 prefs
= __SCHelperSessionGetPreferences(session
);
776 ok
= _SCPreferencesSystemKeychainPasswordItemExists(prefs
, unique_id
);
782 CFRelease(unique_id
);
790 * SCHELPER_MSG_KEYCHAIN_REMOVE
791 * (in) data = unique_id
792 * (out) status = SCError()
796 do_keychain_remove(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
799 SCPreferencesRef prefs
;
800 CFStringRef unique_id
= NULL
;
802 if ((data
!= NULL
) && !_SCUnserializeString(&unique_id
, data
, NULL
, 0)) {
806 if (unique_id
!= NULL
) {
807 if (isA_CFString(unique_id
)) {
808 prefs
= __SCHelperSessionGetPreferences(session
);
809 ok
= _SCPreferencesSystemKeychainPasswordItemRemove(prefs
, unique_id
);
815 CFRelease(unique_id
);
823 * SCHELPER_MSG_KEYCHAIN_SET
824 * (in) data = options dictionary
825 * (out) status = SCError()
829 do_keychain_set(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
832 CFStringRef description
;
833 CFArrayRef executablePaths
= NULL
;
836 CFDictionaryRef options
= NULL
;
838 SCPreferencesRef prefs
;
839 CFStringRef unique_id
;
841 if ((data
!= NULL
) && !_SCUnserialize((CFPropertyListRef
*)&options
, data
, NULL
, 0)) {
845 if (options
!= NULL
) {
846 if (!isA_CFDictionary(options
)) {
854 if (CFDictionaryGetValueIfPresent(options
,
855 kSCKeychainOptionsAllowedExecutables
,
856 (const void **)&executablePaths
)) {
857 CFMutableArrayRef executableURLs
;
860 CFMutableDictionaryRef newOptions
;
862 executableURLs
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
863 n
= CFArrayGetCount(executablePaths
);
864 for (i
= 0; i
< n
; i
++) {
868 path
= CFArrayGetValueAtIndex(executablePaths
, i
);
869 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
870 CFDataGetBytePtr(path
),
871 CFDataGetLength(path
),
874 CFArrayAppendValue(executableURLs
, url
);
879 newOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, options
);
880 CFDictionarySetValue(newOptions
, kSCKeychainOptionsAllowedExecutables
, executableURLs
);
881 CFRelease(executableURLs
);
884 options
= newOptions
;
887 unique_id
= CFDictionaryGetValue(options
, kSCKeychainOptionsUniqueID
);
888 label
= CFDictionaryGetValue(options
, kSCKeychainOptionsLabel
);
889 description
= CFDictionaryGetValue(options
, kSCKeychainOptionsDescription
);
890 account
= CFDictionaryGetValue(options
, kSCKeychainOptionsAccount
);
891 password
= CFDictionaryGetValue(options
, kSCKeychainOptionsPassword
);
893 prefs
= __SCHelperSessionGetPreferences(session
);
894 ok
= _SCPreferencesSystemKeychainPasswordItemSet(prefs
,
910 #endif // !TARGET_OS_IPHONE
914 * SCHELPER_MSG_INTERFACE_REFRESH
916 * (out) status = SCError()
920 do_interface_refresh(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
922 CFStringRef ifName
= NULL
;
925 if ((data
!= NULL
) && !_SCUnserializeString(&ifName
, data
, NULL
, 0)) {
926 *status
= kSCStatusInvalidArgument
;
927 SC_log(LOG_NOTICE
, "interface name not valid");
931 if (ifName
== NULL
) {
932 *status
= kSCStatusInvalidArgument
;
933 SC_log(LOG_NOTICE
, "interface name not valid");
937 if (isA_CFString(ifName
)) {
938 ok
= _SCNetworkInterfaceForceConfigurationRefresh(ifName
);
941 SC_log(LOG_NOTICE
, "interface \"%@\" not refreshed: %s",
943 SCErrorString(*status
));
946 *status
= kSCStatusInvalidArgument
;
947 SC_log(LOG_NOTICE
, "interface name not valid");
958 * (in) data = prefsID
959 * (out) status = SCError()
963 do_prefs_Open(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
966 CFDictionaryRef options
;
968 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
969 CFDictionaryRef prefsInfo
= NULL
;
971 CFStringRef prefsName
;
972 CFStringRef proc_name
;
978 if ((data
!= NULL
) && !_SCUnserialize((CFPropertyListRef
*)&prefsInfo
, data
, NULL
, 0)) {
979 SC_log(LOG_NOTICE
, "data not valid, %@", data
);
983 if ((prefsInfo
== NULL
) || !isA_CFDictionary(prefsInfo
)) {
984 SC_log(LOG_NOTICE
, "info not valid");
985 if (prefsInfo
!= NULL
) CFRelease(prefsInfo
);
989 // get [optional] prefsID
990 prefsID
= CFDictionaryGetValue(prefsInfo
, CFSTR("prefsID"));
991 prefsID
= isA_CFString(prefsID
);
992 if (prefsID
!= NULL
) {
993 if (CFStringHasPrefix(prefsID
, CFSTR("/")) ||
994 CFStringHasPrefix(prefsID
, CFSTR("../")) ||
995 CFStringHasSuffix(prefsID
, CFSTR("/..")) ||
996 (CFStringFind(prefsID
, CFSTR("/../"), 0).location
!= kCFNotFound
)) {
997 // if we're trying to escape from the preferences directory
998 SC_log(LOG_NOTICE
, "prefsID (%@) not valid", prefsID
);
999 CFRelease(prefsInfo
);
1000 *status
= kSCStatusInvalidArgument
;
1005 // get [optional] options
1006 options
= CFDictionaryGetValue(prefsInfo
, CFSTR("options"));
1007 options
= isA_CFDictionary(options
);
1009 // get preferences session "name"
1010 name
= CFDictionaryGetValue(prefsInfo
, CFSTR("name"));
1011 if (!isA_CFString(name
)) {
1012 SC_log(LOG_NOTICE
, "session \"name\" not valid");
1013 CFRelease(prefsInfo
);
1017 // get PID of caller
1018 pid
= CFDictionaryGetValue(prefsInfo
, CFSTR("PID"));
1019 if (!isA_CFNumber(pid
)) {
1020 SC_log(LOG_NOTICE
, "PID not valid");
1021 CFRelease(prefsInfo
);
1025 // get process name of caller
1026 proc_name
= CFDictionaryGetValue(prefsInfo
, CFSTR("PROC_NAME"));
1027 if (!isA_CFString(proc_name
)) {
1028 SC_log(LOG_NOTICE
, "process name not valid");
1029 CFRelease(prefsInfo
);
1033 // build [helper] preferences "name" (used for debugging) and estabish
1034 // a preferences session.
1035 prefsName
= CFStringCreateWithFormat(NULL
, NULL
,
1041 prefs
= SCPreferencesCreateWithOptions(NULL
, prefsName
, prefsID
, NULL
, options
);
1042 CFRelease(prefsName
);
1043 CFRelease(prefsInfo
);
1045 __SCHelperSessionSetPreferences(session
, prefs
);
1047 if (prefs
!= NULL
) {
1050 CFRunLoopRef rl
= CFRunLoopGetCurrent();
1052 // [temporarily] schedule notifications to ensure that we can use
1053 // the SCDynamicStore to track helper sessions
1054 ok
= SCPreferencesScheduleWithRunLoop(prefs
, rl
, kCFRunLoopDefaultMode
);
1056 (void)SCPreferencesUnscheduleFromRunLoop(prefs
, rl
, kCFRunLoopDefaultMode
);
1060 // the session now holds a references to the SCPreferencesRef
1063 *status
= SCError();
1073 * (out) status = SCError()
1074 * (out) reply = current signature + current preferences
1077 do_prefs_Access(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1080 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1081 CFDataRef signature
;
1083 if (prefs
== NULL
) {
1087 signature
= SCPreferencesGetSignature(prefs
);
1088 if (signature
!= NULL
) {
1089 const void * dictKeys
[2];
1090 const void * dictVals
[2];
1091 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1092 CFDictionaryRef replyDict
;
1094 dictKeys
[0] = CFSTR("signature");
1095 dictVals
[0] = signature
;
1097 dictKeys
[1] = CFSTR("preferences");
1098 dictVals
[1] = prefsPrivate
->prefs
;
1100 replyDict
= CFDictionaryCreate(NULL
,
1101 (const void **)&dictKeys
,
1102 (const void **)&dictVals
,
1103 sizeof(dictKeys
)/sizeof(dictKeys
[0]),
1104 &kCFTypeDictionaryKeyCallBacks
,
1105 &kCFTypeDictionaryValueCallBacks
);
1107 ok
= _SCSerialize(replyDict
, reply
, NULL
, NULL
);
1108 CFRelease(replyDict
);
1113 *status
= SCError();
1122 * (in) data = client prefs signature (NULL if check not needed)
1123 * (out) status = SCError()
1127 do_prefs_Lock(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1129 CFDataRef clientSignature
= (CFDataRef
)data
;
1131 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1132 Boolean wait
= (info
== (void *)FALSE
) ? FALSE
: TRUE
;
1134 if (prefs
== NULL
) {
1138 ok
= SCPreferencesLock(prefs
, wait
);
1140 *status
= SCError();
1144 if (clientSignature
!= NULL
) {
1145 CFDataRef serverSignature
;
1147 serverSignature
= SCPreferencesGetSignature(prefs
);
1148 if (!CFEqual(clientSignature
, serverSignature
)) {
1149 (void)SCPreferencesUnlock(prefs
);
1150 *status
= kSCStatusStale
;
1160 * (in) data = new preferences (NULL if commit w/no changes)
1161 * (out) status = SCError()
1162 * (out) reply = new signature
1165 do_prefs_Commit(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1168 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1169 CFPropertyListRef prefsData
= NULL
;
1170 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1171 Boolean saveAccessed
;
1172 Boolean saveChanged
;
1173 CFMutableDictionaryRef savePrefs
= NULL
;
1174 Boolean saveValid
= FALSE
;
1175 Boolean useSetFilter
;
1176 Boolean useVPNFilter
;
1177 CFArrayRef vpnTypes
= NULL
;
1179 if (prefs
== NULL
) {
1184 // if commit with no changes
1188 ok
= _SCUnserialize(&prefsData
, data
, NULL
, 0);
1193 if (!isA_CFDictionary(prefsData
)) {
1194 *status
= kSCStatusFailed
;
1199 useSetFilter
= __SCHelperSessionUseNetworkSetFilter(session
);
1200 useVPNFilter
= __SCHelperSessionUseVPNFilter(session
, &vpnTypes
);
1201 if (useSetFilter
|| useVPNFilter
) {
1204 if (prefsPrivate
->prefs
!= NULL
) {
1206 CFMutableDictionaryRef prefsNew
= NULL
;
1207 CFMutableDictionaryRef prefsOld
= NULL
;
1208 CFMutableDictionaryRef prefsSave
= prefsPrivate
->prefs
;
1210 for (c
= 0; c
< 2; c
++) {
1214 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, prefsSave
);
1217 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, prefsData
);
1222 // filter out current network set selection
1223 (void) SCPreferencesRemoveValue(prefs
, kSCPrefCurrentSet
);
1224 } else if (useVPNFilter
) {
1225 CFRange range
= CFRangeMake(0, CFArrayGetCount(vpnTypes
));
1226 CFArrayRef services
;
1228 // filter out VPN services of the specified type
1229 services
= SCNetworkServiceCopyAll(prefs
);
1230 if (services
!= NULL
) {
1232 CFIndex n
= CFArrayGetCount(services
);
1234 for (i
= 0; i
< n
; i
++) {
1235 SCNetworkServiceRef service
;
1237 service
= CFArrayGetValueAtIndex(services
, i
);
1238 if (_SCNetworkServiceIsVPN(service
)) {
1239 SCNetworkInterfaceRef child
;
1240 CFStringRef childType
= NULL
;
1241 SCNetworkInterfaceRef interface
;
1242 CFStringRef interfaceType
;
1244 interface
= SCNetworkServiceGetInterface(service
);
1245 interfaceType
= SCNetworkInterfaceGetInterfaceType(interface
);
1246 child
= SCNetworkInterfaceGetInterface(interface
);
1247 if (child
!= NULL
) {
1248 childType
= SCNetworkInterfaceGetInterfaceType(child
);
1250 if (CFEqual(interfaceType
, kSCNetworkInterfaceTypeVPN
) &&
1251 (childType
!= NULL
) &&
1252 CFArrayContainsValue(vpnTypes
, range
, childType
)) {
1253 // filter out VPN service
1254 (void) SCNetworkServiceRemove(service
);
1256 // mark all other VPN services "enabled"
1257 (void) SCNetworkServiceSetEnabled(service
, TRUE
);
1262 CFRelease(services
);
1268 prefsOld
= prefsPrivate
->prefs
;
1271 prefsNew
= prefsPrivate
->prefs
;
1276 // compare the filtered configurations
1277 ok
= _SC_CFEqual(prefsOld
, prefsNew
);
1280 if (prefsOld
!= NULL
) CFRelease(prefsOld
);
1281 if (prefsNew
!= NULL
) CFRelease(prefsNew
);
1282 prefsPrivate
->prefs
= prefsSave
;
1286 *status
= kSCStatusAccessError
;
1291 /* Take a backup of prefs, accessed bit, changed bit to
1292 restore them IFF the commit fails. Pretend as if the
1293 commit never happened!
1295 savePrefs
= prefsPrivate
->prefs
;
1296 saveAccessed
= prefsPrivate
->accessed
;
1297 saveChanged
= prefsPrivate
->changed
;
1299 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, prefsData
);
1300 prefsPrivate
->accessed
= TRUE
;
1301 prefsPrivate
->changed
= TRUE
;
1306 ok
= SCPreferencesCommitChanges(prefs
);
1308 if (savePrefs
!= NULL
) {
1309 CFRelease(savePrefs
);
1311 *reply
= SCPreferencesGetSignature(prefs
);
1314 /* Restore the backup we took earlier */
1316 if (prefsPrivate
->prefs
!= NULL
) {
1317 CFRelease(prefsPrivate
->prefs
);
1320 prefsPrivate
->prefs
= savePrefs
;
1321 prefsPrivate
->accessed
= saveAccessed
;
1322 prefsPrivate
->changed
= saveChanged
;
1324 *status
= SCError();
1329 if (prefsData
!= NULL
) CFRelease(prefsData
);
1337 * (out) status = SCError()
1341 do_prefs_Apply(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1344 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1346 if (prefs
== NULL
) {
1350 ok
= SCPreferencesApplyChanges(prefs
);
1352 *status
= SCError();
1362 * (out) status = SCError()
1366 do_prefs_Unlock(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1369 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1371 if (prefs
== NULL
) {
1375 ok
= SCPreferencesUnlock(prefs
);
1377 *status
= SCError();
1387 * (out) status = SCError()
1391 do_prefs_Close(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1393 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1395 if (prefs
== NULL
) {
1399 __SCHelperSessionSetPreferences(session
, NULL
);
1408 * (out) status = kSCStatusOK
1412 do_prefs_Synchronize(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1414 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1416 if (prefs
== NULL
) {
1420 SCPreferencesSynchronize(prefs
);
1421 *status
= kSCStatusOK
;
1427 #pragma mark Process commands
1431 sessionName(SCHelperSessionRef session
)
1433 CFStringRef name
= NULL
;
1434 SCPreferencesRef prefs
;
1436 prefs
= __SCHelperSessionGetPreferences(session
);
1437 if (prefs
!= NULL
) {
1438 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1440 name
= prefsPrivate
->name
;
1443 return (name
!= NULL
) ? name
: CFSTR("???");
1448 sessionPrefsID(SCHelperSessionRef session
)
1450 CFStringRef prefsID
;
1451 SCPreferencesPrivateRef prefsPrivate
;
1452 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1454 prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
1455 if ((prefsPrivate
!= NULL
) && (prefsPrivate
->prefsID
!= NULL
)) {
1456 prefsID
= prefsPrivate
->prefsID
;
1458 prefsID
= PREFS_DEFAULT_CONFIG
;
1466 copyEntitlement(SCHelperSessionRef session
, CFStringRef entitlement
)
1468 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1470 CFTypeRef value
= NULL
;
1472 // Create the security task from the audit token
1473 task
= SecTaskCreateWithAuditToken(NULL
, sessionPrivate
->auditToken
);
1475 CFErrorRef error
= NULL
;
1477 // Get the value for the entitlement
1478 value
= SecTaskCopyValueForEntitlement(task
, entitlement
, &error
);
1479 if ((value
== NULL
) && (error
!= NULL
)) {
1480 CFIndex code
= CFErrorGetCode(error
);
1481 CFStringRef domain
= CFErrorGetDomain(error
);
1483 if (!CFEqual(domain
, kCFErrorDomainMach
) ||
1484 ((code
!= kIOReturnInvalid
) && (code
!= kIOReturnNotFound
))) {
1485 // if unexpected error
1486 SC_log(LOG_NOTICE
, "SecTaskCopyValueForEntitlement(,\"%@\",) failed, error = %@ : %@",
1489 sessionName(session
));
1496 SC_log(LOG_NOTICE
, "SecTaskCreateWithAuditToken() failed: %@",
1497 sessionName(session
));
1504 #if !TARGET_OS_IPHONE
1506 isSetChange(SCHelperSessionRef session
)
1508 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1510 if (sessionPrivate
->isSetChange
== UNKNOWN
) {
1511 CFBooleanRef bVal
= NULL
;
1512 CFStringRef prefsID
;
1513 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
1514 Boolean setFilter
= FALSE
;
1516 prefsID
= sessionPrefsID(session
);
1517 if (CFEqual(prefsID
, PREFS_DEFAULT_CONFIG
) &&
1518 isA_CFDictionary(prefsPrivate
->options
) &&
1519 CFDictionaryGetValueIfPresent(prefsPrivate
->options
,
1520 kSCPreferencesOptionChangeNetworkSet
,
1521 (const void **)&bVal
) &&
1522 isA_CFBoolean(bVal
) &&
1523 CFBooleanGetValue(bVal
)) {
1527 // establish network set (location) filter
1528 __SCHelperSessionSetNetworkSetFilter(session
, setFilter
);
1531 return (sessionPrivate
->isSetChange
== YES
) ? TRUE
: FALSE
;
1533 #endif // !TARGET_OS_IPHONE
1537 isVPNChange(SCHelperSessionRef session
)
1539 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1541 if (sessionPrivate
->isVPNChange
== UNKNOWN
) {
1542 CFArrayRef entitlement
;
1543 Boolean vpnChange
= FALSE
;
1544 CFArrayRef vpnTypes
= NULL
;
1546 entitlement
= copyEntitlement(session
, kSCVPNFilterEntitlementName
);
1547 if (entitlement
!= NULL
) {
1548 if (isA_CFArray(entitlement
)) {
1549 CFStringRef prefsID
;
1551 prefsID
= sessionPrefsID(session
);
1552 if (CFEqual(prefsID
, PREFS_DEFAULT_CONFIG
)) {
1553 // save the VPN type identifiers
1554 vpnTypes
= CFRetain(entitlement
);
1556 // grant an exception
1558 } else if (CFStringHasPrefix(prefsID
, CFSTR("VPN-")) &&
1559 CFStringHasSuffix(prefsID
, CFSTR(".plist"))) {
1563 range
.location
= sizeof("VPN-") - 1;
1564 range
.length
= CFStringGetLength(prefsID
)
1565 - (sizeof("VPN-") - 1) // trim VPN-
1566 - (sizeof(".plist") - 1); // trim .plist
1567 vpnID
= CFStringCreateWithSubstring(NULL
, prefsID
, range
);
1568 if (CFArrayContainsValue(entitlement
,
1569 CFRangeMake(0, CFArrayGetCount(entitlement
)),
1571 // grant an exception
1578 CFRelease(entitlement
);
1581 __SCHelperSessionSetVPNFilter(session
, vpnChange
, vpnTypes
);
1582 if (vpnTypes
!= NULL
) {
1583 CFRelease(vpnTypes
);
1587 return (sessionPrivate
->isVPNChange
== YES
) ? TRUE
: FALSE
;
1592 checkEntitlement(SCHelperSessionRef session
, CFStringRef prefsID
, CFStringRef entitlement_name
)
1594 CFArrayRef entitlement
;
1595 Boolean hasEntitlement
= FALSE
;
1597 entitlement
= copyEntitlement(session
, entitlement_name
);
1598 if (entitlement
!= NULL
) {
1599 if (isA_CFArray(entitlement
)) {
1600 if (CFArrayContainsValue(entitlement
,
1601 CFRangeMake(0, CFArrayGetCount(entitlement
)),
1603 // if client DOES have entitlement
1604 hasEntitlement
= TRUE
;
1607 SC_log(LOG_NOTICE
, "hasAuthorization() session=%@: entitlement=%@: not valid",
1608 sessionName(session
),
1612 CFRelease(entitlement
);
1615 #if TARGET_OS_IPHONE
1616 // make an exception for VPN configuration management
1617 if (!hasEntitlement
) {
1618 if (isVPNChange(session
)) {
1619 // grant a "filtered" exception
1620 hasEntitlement
= TRUE
;
1623 #endif // TARGET_OS_IPHONE
1625 return hasEntitlement
;
1630 hasAuthorization(SCHelperSessionRef session
, Boolean needWrite
)
1632 AuthorizationRef authorization
= __SCHelperSessionGetAuthorization(session
);
1633 CFStringRef prefsID
;
1634 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1636 if (authorization
== NULL
) {
1640 #if !TARGET_OS_IPHONE
1641 if (!__SCHelperSessionUseEntitlement(session
)) {
1642 AuthorizationFlags flags
;
1643 AuthorizationItem items
[1];
1644 AuthorizationRights rights
;
1647 if (isSetChange(session
)) {
1648 items
[0].name
= kSCPreferencesAuthorizationRight_network_set
;
1649 items
[0].value
= NULL
;
1650 items
[0].valueLength
= 0;
1652 } else if (isVPNChange(session
)) {
1653 items
[0].name
= kSCPreferencesAuthorizationRight_write
;
1654 items
[0].value
= NULL
;
1655 items
[0].valueLength
= 0;
1658 items
[0].name
= kSCPreferencesAuthorizationRight_write
;
1659 items
[0].value
= NULL
;
1660 items
[0].valueLength
= 0;
1664 rights
.count
= sizeof(items
) / sizeof(items
[0]);
1665 rights
.items
= items
;
1667 flags
= kAuthorizationFlagDefaults
;
1668 flags
|= kAuthorizationFlagInteractionAllowed
;
1669 flags
|= kAuthorizationFlagExtendRights
;
1670 // flags |= kAuthorizationFlagPartialRights;
1671 // flags |= kAuthorizationFlagPreAuthorize;
1673 status
= AuthorizationCopyRights(authorization
,
1675 kAuthorizationEmptyEnvironment
,
1678 if (status
!= errAuthorizationSuccess
) {
1679 SC_log(LOG_INFO
, "AuthorizationCopyRights() failed: status = %d",
1686 #endif // !TARGET_OS_IPHONE
1688 prefsID
= sessionPrefsID(session
);
1690 if (sessionPrivate
->callerWriteAccess
== UNKNOWN
) {
1691 if (checkEntitlement(session
, prefsID
, kSCWriteEntitlementName
)) {
1692 sessionPrivate
->callerWriteAccess
= YES
;
1693 sessionPrivate
->callerReadAccess
= YES
; // implied
1695 sessionPrivate
->callerWriteAccess
= NO
;
1700 if (sessionPrivate
->callerWriteAccess
== YES
) {
1703 SC_log(LOG_NOTICE
, "SCPreferences write access to \"%@\" denied, no entitlement for \"%@\"",
1705 sessionName(session
));
1710 if (sessionPrivate
->callerReadAccess
== UNKNOWN
) {
1711 if (checkEntitlement(session
, prefsID
, kSCReadEntitlementName
)) {
1712 sessionPrivate
->callerReadAccess
= YES
;
1714 sessionPrivate
->callerWriteAccess
= NO
;
1718 if (sessionPrivate
->callerReadAccess
== YES
) {
1722 SC_log(LOG_NOTICE
, "SCPreferences access to \"%@\" denied, no entitlement for \"%@\"",
1724 sessionName(session
));
1729 typedef Boolean (*helperFunction
) (SCHelperSessionRef session
,
1736 static const struct helper
{
1738 const char *commandName
;
1739 Boolean needsAuthorization
;
1741 helperFunction func
;
1744 { SCHELPER_MSG_AUTH
, "AUTH", FALSE
, FALSE
, do_Auth
, NULL
},
1746 { SCHELPER_MSG_PREFS_OPEN
, "PREFS open", FALSE
, FALSE
, do_prefs_Open
, NULL
},
1747 { SCHELPER_MSG_PREFS_ACCESS
, "PREFS access", TRUE
, FALSE
, do_prefs_Access
, NULL
},
1748 { SCHELPER_MSG_PREFS_LOCK
, "PREFS lock", TRUE
, TRUE
, do_prefs_Lock
, (void *)FALSE
},
1749 { SCHELPER_MSG_PREFS_LOCKWAIT
, "PREFS lock/wait", TRUE
, TRUE
, do_prefs_Lock
, (void *)TRUE
},
1750 { SCHELPER_MSG_PREFS_COMMIT
, "PREFS commit", TRUE
, TRUE
, do_prefs_Commit
, NULL
},
1751 { SCHELPER_MSG_PREFS_APPLY
, "PREFS apply", TRUE
, TRUE
, do_prefs_Apply
, NULL
},
1752 { SCHELPER_MSG_PREFS_UNLOCK
, "PREFS unlock", FALSE
, TRUE
, do_prefs_Unlock
, NULL
},
1753 { SCHELPER_MSG_PREFS_CLOSE
, "PREFS close", FALSE
, FALSE
, do_prefs_Close
, NULL
},
1754 { SCHELPER_MSG_PREFS_SYNCHRONIZE
, "PREFS synchronize", FALSE
, FALSE
, do_prefs_Synchronize
, NULL
},
1756 { SCHELPER_MSG_INTERFACE_REFRESH
, "INTERFACE refresh", TRUE
, TRUE
, do_interface_refresh
, NULL
},
1758 #if !TARGET_OS_IPHONE
1759 { SCHELPER_MSG_KEYCHAIN_COPY
, "KEYCHAIN copy", TRUE
, FALSE
, do_keychain_copy
, NULL
},
1760 { SCHELPER_MSG_KEYCHAIN_EXISTS
, "KEYCHAIN exists", TRUE
, FALSE
, do_keychain_exists
, NULL
},
1761 { SCHELPER_MSG_KEYCHAIN_REMOVE
, "KEYCHAIN remove", TRUE
, TRUE
, do_keychain_remove
, NULL
},
1762 { SCHELPER_MSG_KEYCHAIN_SET
, "KEYCHAIN set", TRUE
, TRUE
, do_keychain_set
, NULL
},
1763 #endif // !TARGET_OS_IPHONE
1765 { SCHELPER_MSG_EXIT
, "EXIT", FALSE
, FALSE
, do_Exit
, NULL
}
1767 #define nHELPERS (sizeof(helpers)/sizeof(struct helper))
1771 findCommand(uint32_t command
)
1775 for (i
= 0; i
< (int)nHELPERS
; i
++) {
1776 if (helpers
[i
].command
== command
) {
1786 newHelper(void *arg
)
1788 CFRunLoopSourceRef rls
= NULL
;
1789 SCHelperSessionRef session
= (SCHelperSessionRef
)arg
;
1790 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1792 assert(session
!= NULL
);
1793 assert(sessionPrivate
->mp
!= NULL
);
1795 __SCHelperSessionSetThreadName(session
);
1797 rls
= CFMachPortCreateRunLoopSource(NULL
, sessionPrivate
->mp
, 0);
1798 CFRelease(sessionPrivate
->mp
);
1801 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
1804 SC_log(LOG_INFO
, "%p : start", session
);
1806 SC_log(LOG_INFO
, "%p : stop", session
);
1814 #pragma mark Main loop
1817 // MiG generated externals and functions
1818 extern struct mig_subsystem _helper_subsystem
;
1819 extern boolean_t
helper_server(mach_msg_header_t
*, mach_msg_header_t
*);
1824 notify_server(mach_msg_header_t
*request
, mach_msg_header_t
*reply
)
1826 mach_no_senders_notification_t
*Request
= (mach_no_senders_notification_t
*)request
;
1827 mig_reply_error_t
*Reply
= (mig_reply_error_t
*)reply
;
1829 reply
->msgh_bits
= MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request
->msgh_bits
), 0);
1830 reply
->msgh_remote_port
= request
->msgh_remote_port
;
1831 reply
->msgh_size
= sizeof(mig_reply_error_t
); /* Minimal size: update as needed */
1832 reply
->msgh_local_port
= MACH_PORT_NULL
;
1833 reply
->msgh_id
= request
->msgh_id
+ 100;
1835 if ((Request
->not_header
.msgh_id
> MACH_NOTIFY_LAST
) ||
1836 (Request
->not_header
.msgh_id
< MACH_NOTIFY_FIRST
)) {
1837 Reply
->NDR
= NDR_record
;
1838 Reply
->RetCode
= MIG_BAD_ID
;
1839 return FALSE
; /* if this is not a notification message */
1842 switch (Request
->not_header
.msgh_id
) {
1843 case MACH_NOTIFY_NO_SENDERS
: {
1844 SCHelperSessionRef session
;
1846 __MACH_PORT_DEBUG(TRUE
, "*** notify_server MACH_NOTIFY_NO_SENDERS", Request
->not_header
.msgh_local_port
);
1849 session
= __SCHelperSessionFindWithPort(Request
->not_header
.msgh_local_port
);
1850 if (session
!= NULL
) {
1851 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1853 // release CFMachPort *and* SCHelperSession
1854 CFMachPortInvalidate(sessionPrivate
->mp
);
1857 __MACH_PORT_DEBUG(TRUE
, "*** notify_server after invalidate", Request
->not_header
.msgh_local_port
);
1859 // and, lastly, remove our receive right.
1860 (void) mach_port_mod_refs(mach_task_self(),
1861 Request
->not_header
.msgh_local_port
,
1862 MACH_PORT_RIGHT_RECEIVE
, -1);
1864 Reply
->Head
.msgh_bits
= 0;
1865 Reply
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1866 Reply
->RetCode
= KERN_SUCCESS
;
1874 SC_log(LOG_NOTICE
, "HELP!, Received notification: port=%d, msgh_id=%d",
1875 Request
->not_header
.msgh_local_port
,
1876 Request
->not_header
.msgh_id
);
1878 Reply
->NDR
= NDR_record
;
1879 Reply
->RetCode
= MIG_BAD_ID
;
1880 return FALSE
; /* if this is not a notification we are handling */
1886 helper_demux(mach_msg_header_t
*request
, mach_msg_header_t
*reply
)
1888 Boolean processed
= FALSE
;
1891 * (attempt to) process SCHelper requests.
1893 processed
= helper_server(request
, reply
);
1899 * (attempt to) process (NO MORE SENDERS) notification messages.
1901 processed
= notify_server(request
, reply
);
1907 * unknown message ID, log and return an error.
1909 SC_log(LOG_NOTICE
, "helper_demux(): unknown message ID (%d) received", request
->msgh_id
);
1910 reply
->msgh_bits
= MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request
->msgh_bits
), 0);
1911 reply
->msgh_remote_port
= request
->msgh_remote_port
;
1912 reply
->msgh_size
= sizeof(mig_reply_error_t
); /* Minimal size */
1913 reply
->msgh_local_port
= MACH_PORT_NULL
;
1914 reply
->msgh_id
= request
->msgh_id
+ 100;
1915 ((mig_reply_error_t
*)reply
)->NDR
= NDR_record
;
1916 ((mig_reply_error_t
*)reply
)->RetCode
= MIG_BAD_ID
;
1922 #define MACH_MSG_BUFFER_SIZE 128
1926 helperCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1928 os_activity_t activity_id
;
1929 mig_reply_error_t
* bufRequest
= msg
;
1930 uint32_t bufReply_q
[MACH_MSG_BUFFER_SIZE
/sizeof(uint32_t)];
1931 mig_reply_error_t
* bufReply
= (mig_reply_error_t
*)bufReply_q
;
1932 static CFIndex bufSize
= 0;
1933 mach_msg_return_t mr
;
1936 activity_id
= os_activity_start("processing SCHelper request",
1937 OS_ACTIVITY_FLAG_DEFAULT
);
1940 // get max size for MiG reply buffers
1941 bufSize
= _helper_subsystem
.maxsize
;
1943 // check if our on-the-stack reply buffer will be big enough
1944 if (bufSize
> sizeof(bufReply_q
)) {
1945 SC_log(LOG_NOTICE
, "buffer size should be increased > %d",
1946 _helper_subsystem
.maxsize
);
1950 if (bufSize
> sizeof(bufReply_q
)) {
1951 bufReply
= CFAllocatorAllocate(NULL
, _helper_subsystem
.maxsize
, 0);
1953 bufReply
->RetCode
= 0;
1955 /* we have a request message */
1956 (void) helper_demux(&bufRequest
->Head
, &bufReply
->Head
);
1958 if (!(bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)) {
1959 if (bufReply
->RetCode
== MIG_NO_REPLY
) {
1960 bufReply
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1961 } else if ((bufReply
->RetCode
!= KERN_SUCCESS
) &&
1962 (bufRequest
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)) {
1964 * destroy the request - but not the reply port
1966 bufRequest
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1967 mach_msg_destroy(&bufRequest
->Head
);
1971 if (bufReply
->Head
.msgh_remote_port
!= MACH_PORT_NULL
) {
1975 * We don't want to block indefinitely because the client
1976 * isn't receiving messages from the reply port.
1977 * If we have a send-once right for the reply port, then
1978 * this isn't a concern because the send won't block.
1979 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
1980 * To avoid falling off the kernel's fast RPC path unnecessarily,
1981 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
1984 options
= MACH_SEND_MSG
;
1985 if (MACH_MSGH_BITS_REMOTE(bufReply
->Head
.msgh_bits
) != MACH_MSG_TYPE_MOVE_SEND_ONCE
) {
1986 options
|= MACH_SEND_TIMEOUT
;
1988 mr
= mach_msg(&bufReply
->Head
, /* msg */
1989 options
, /* option */
1990 bufReply
->Head
.msgh_size
, /* send_size */
1992 MACH_PORT_NULL
, /* rcv_name */
1993 MACH_MSG_TIMEOUT_NONE
, /* timeout */
1994 MACH_PORT_NULL
); /* notify */
1996 /* Has a message error occurred? */
1998 case MACH_SEND_INVALID_DEST
:
1999 case MACH_SEND_TIMED_OUT
:
2002 /* Includes success case. */
2007 if (bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
2008 mach_msg_destroy(&bufReply
->Head
);
2013 if (bufReply
!= (mig_reply_error_t
*)bufReply_q
)
2014 CFAllocatorDeallocate(NULL
, bufReply
);
2016 os_activity_end(activity_id
);
2023 initMPCopyDescription(const void *info
)
2025 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<SCHelper MP>"));
2031 _helperinit(mach_port_t server
,
2032 mach_port_t
*newSession
,
2034 audit_token_t audit_token
)
2036 CFMachPortContext context
= { 0
2040 , initMPCopyDescription
2043 mach_port_t oldNotify
;
2044 SCHelperSessionRef session
;
2045 SCHelperSessionPrivateRef sessionPrivate
;
2046 pthread_attr_t tattr
;
2049 session
= __SCHelperSessionFindWithPort(server
);
2050 if (session
!= NULL
) {
2052 SC_log(LOG_DEBUG
, "session is already open");
2054 *status
= kSCStatusFailed
; /* you can't re-open an "open" session */
2055 return KERN_SUCCESS
;
2058 session
= __SCHelperSessionCreate(NULL
);
2059 assert(session
!= NULL
);
2060 sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
2062 // create per-session port
2063 kr
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, &sessionPrivate
->port
);
2064 if (kr
!= KERN_SUCCESS
) {
2065 SC_log(LOG_ERR
, "mach_port_allocate() failed: %s", mach_error_string(kr
));
2070 *newSession
= sessionPrivate
->port
;
2072 (void) mach_port_set_attributes(mach_task_self(),
2074 MACH_PORT_IMPORTANCE_RECEIVER
,
2079 // Note: we create the CFMachPort *before* we insert a send
2080 // right present to ensure that CF does not establish
2081 // its dead name notification.
2083 context
.info
= (void *)session
;
2084 sessionPrivate
->mp
= _SC_CFMachPortCreateWithPort("SCHelper/session",
2089 /* Request a notification when/if the client dies */
2090 kr
= mach_port_request_notification(mach_task_self(),
2092 MACH_NOTIFY_NO_SENDERS
,
2095 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
2097 if (kr
!= KERN_SUCCESS
) {
2098 SC_log(LOG_NOTICE
, "mach_port_request_notification() failed: %s", mach_error_string(kr
));
2100 // clean up CFMachPort, mach port rights
2101 CFMachPortInvalidate(sessionPrivate
->mp
);
2102 CFRelease(sessionPrivate
->mp
);
2103 sessionPrivate
->mp
= NULL
;
2104 (void) mach_port_mod_refs(mach_task_self(), *newSession
, MACH_PORT_RIGHT_RECEIVE
, -1);
2105 *newSession
= MACH_PORT_NULL
;
2106 *status
= kSCStatusFailed
;
2110 if (oldNotify
!= MACH_PORT_NULL
) {
2111 SC_log(LOG_NOTICE
, "oldNotify != MACH_PORT_NULL");
2114 // add send right (that will be passed back to the client)
2115 (void) mach_port_insert_right(mach_task_self(),
2118 MACH_MSG_TYPE_MAKE_SEND
);
2121 sessionPrivate
->auditToken
= audit_token
;
2124 // Note: at this time we should be holding ONE send right and
2125 // ONE receive right to the server. The send right is
2126 // moved to the caller.
2129 // start per-session thread
2130 pthread_attr_init(&tattr
);
2131 pthread_attr_setscope(&tattr
, PTHREAD_SCOPE_SYSTEM
);
2132 pthread_attr_setdetachstate(&tattr
, PTHREAD_CREATE_DETACHED
);
2133 pthread_attr_setstacksize(&tattr
, 96 * 1024); // each thread gets a 96K stack
2134 pthread_create(&tid
, &tattr
, newHelper
, (void *)session
);
2135 pthread_attr_destroy(&tattr
);
2137 *status
= kSCStatusOK
;
2142 return KERN_SUCCESS
;
2148 _helperexec(mach_port_t server
,
2150 xmlData_t dataRef
, /* raw XML bytes */
2151 mach_msg_type_number_t dataLen
,
2152 xmlData_t traceRef
, /* raw XML bytes */
2153 mach_msg_type_number_t traceLen
,
2155 xmlDataOut_t
*replyRef
, /* raw XML bytes */
2156 mach_msg_type_number_t
*replyLen
)
2158 CFStringRef backtrace
= NULL
;
2159 CFDataRef data
= NULL
;
2161 CFDataRef reply
= NULL
;
2162 SCHelperSessionRef session
;
2164 *status
= kSCStatusOK
;
2168 if ((dataRef
!= NULL
) && (dataLen
> 0)) {
2169 if (!_SCUnserializeData(&data
, (void *)dataRef
, dataLen
)) {
2170 *status
= SCError();
2174 if ((traceRef
!= NULL
) && (traceLen
> 0)) {
2175 if (!_SCUnserializeString(&backtrace
, NULL
, (void *)traceRef
, traceLen
)) {
2176 *status
= SCError();
2180 if (*status
!= kSCStatusOK
) {
2184 session
= __SCHelperSessionFindWithPort(server
);
2185 if (session
== NULL
) {
2186 *status
= kSCStatusFailed
; /* you must have an open session to play */
2190 i
= findCommand(msgID
);
2192 SC_log(LOG_NOTICE
, "received unknown command : %u", msgID
);
2193 *status
= kSCStatusInvalidArgument
;
2197 SC_log(LOG_INFO
, "%p : processing command \"%s\"%s",
2199 helpers
[i
].commandName
,
2200 (data
!= NULL
) ? " w/data" : "");
2202 if (helpers
[i
].needsAuthorization
&&
2203 !hasAuthorization(session
, helpers
[i
].needsWrite
)) {
2204 SC_log(LOG_INFO
, "%p : command \"%s\" : not authorized",
2206 helpers
[i
].commandName
);
2207 *status
= kSCStatusAccessError
;
2210 if (*status
== kSCStatusOK
) {
2211 if (backtrace
!= NULL
) {
2212 __SCHelperSessionAddBacktrace(session
, backtrace
, helpers
[i
].commandName
);
2214 (*helpers
[i
].func
)(session
, helpers
[i
].info
, data
, status
, &reply
);
2217 if ((*status
!= -1) || (reply
!= NULL
)) {
2220 SC_log(LOG_INFO
, "%p : sending status %u%s",
2223 (reply
!= NULL
) ? " w/reply" : "");
2225 /* serialize the data */
2226 if (reply
!= NULL
) {
2229 ok
= _SCSerializeData(reply
, (void **)replyRef
, &len
);
2230 *replyLen
= (mach_msg_type_number_t
)len
;
2234 *status
= SCError();
2242 if (data
!= NULL
) CFRelease(data
);
2243 if (backtrace
!= NULL
) CFRelease(backtrace
);
2244 if (reply
!= NULL
) CFRelease(reply
);
2245 return KERN_SUCCESS
;
2250 helperMPCopyDescription(const void *info
)
2252 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<main SCHelper MP>"));
2257 init_MiG(const char *service_name
, int *n_listeners
)
2259 CFMachPortContext context
= { 0
2263 , helperMPCopyDescription
2267 CFRunLoopSourceRef rls
;
2268 mach_port_t service_port
= MACH_PORT_NULL
;
2270 kr
= bootstrap_check_in(bootstrap_port
, service_name
, &service_port
);
2271 if (kr
!= BOOTSTRAP_SUCCESS
) {
2272 SC_log(LOG_NOTICE
, "bootstrap_check_in() failed: %s",
2273 bootstrap_strerror(kr
));
2277 // add a run loop source to listen for new requests
2278 mp
= _SC_CFMachPortCreateWithPort("SCHelper/server",
2282 rls
= CFMachPortCreateRunLoopSource(NULL
, mp
, 0);
2284 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
2287 *n_listeners
= *n_listeners
+ 1;
2297 static const struct option longopts
[] = {
2298 { "debug", no_argument
, 0, 'd' },
2304 main(int argc
, char **argv
)
2306 Boolean done
= FALSE
;
2308 int gen_reported
= 0;
2310 int n_listeners
= 0;
2311 // extern int optind;
2315 openlog("SCHelper", LOG_CONS
|LOG_PID
, LOG_DAEMON
);
2317 // process any arguments
2318 while ((opt
= getopt_long(argc
, argv
, "d", longopts
, &opti
)) != -1) {
2324 // if (strcmp(longopts[opti].name, "debug") == 1) {
2329 SC_log(LOG_NOTICE
, "ignoring unknown or ambiguous command line option");
2336 if (geteuid() != 0) {
2337 SC_log(LOG_NOTICE
, "%s", strerror(EACCES
));
2341 main_runLoop
= CFRunLoopGetCurrent();
2343 err
= init_MiG("com.apple.SystemConfiguration.helper", &n_listeners
);
2344 if ((err
!= 0) || (n_listeners
== 0)) {
2348 pthread_setname_np("SCHelper main thread");
2354 rlStatus
= CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 15.0, TRUE
);
2356 pthread_mutex_lock(&sessions_lock
);
2358 if (sessions
!= NULL
) {
2359 if (rlStatus
== kCFRunLoopRunTimedOut
) {
2362 if ((CFSetGetCount(sessions
) == 0) && (sessions_closed
== 0)) {
2363 // if we don't have any open sessions and no
2364 // sessions have recently been closed
2371 gen_current
= sessions_generation
;
2372 sessions_closed
= 0;
2374 if (!done
&& (idle
>= (2 * 60 / 15))) {
2375 if (gen_reported
!= gen_current
) {
2376 FILE *logFile
= NULL
;
2378 SC_log(LOG_INFO
, "active (but IDLE) sessions");
2379 CFSetApplyFunction(sessions
, __SCHelperSessionLog
, (void *)&logFile
);
2380 gen_reported
= gen_current
;
2382 if (logFile
!= NULL
) {
2383 (void) fclose(logFile
);
2389 pthread_mutex_unlock(&sessions_lock
);