2 * Copyright (c) 2005-2018 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>
41 #define SC_LOG_HANDLE __log_SCHelper()
42 #define SC_LOG_HANDLE_TYPE static
43 #include "SCPreferencesInternal.h"
44 #include "SCHelper_client.h"
45 #include "helper_types.h"
48 #pragma mark SCHelper session managment
52 // entitlement used to control read (or write) access to a given "prefsID"
54 #define kSCReadEntitlementName CFSTR("com.apple.SystemConfiguration.SCPreferences-read-access")
55 #define kSCWriteEntitlementName CFSTR("com.apple.SystemConfiguration.SCPreferences-write-access")
58 // entitlement used to allow limited [VPN configuration] write access to the "preferences.plist"
60 #define kSCVPNFilterEntitlementName CFSTR("com.apple.networking.vpn.configuration")
62 typedef enum { NO
= 0, YES
, UNKNOWN
} lazyBoolean
;
64 typedef const struct __SCHelperSession
* SCHelperSessionRef
;
68 // base CFType information
75 AuthorizationRef authorization
;
76 Boolean use_entitlement
;
82 // Mach security audit trailer for evaluating credentials
83 audit_token_t auditToken
;
85 // write access entitlement associated with this session
86 lazyBoolean callerReadAccess
;
87 lazyBoolean callerWriteAccess
;
89 // configuration filtering
90 lazyBoolean isSetChange
; // only network "set" changes
91 lazyBoolean isVPNChange
; // only VPN configuration changes
95 SCPreferencesRef prefs
;
98 CFMutableSetRef backtraces
;
100 } SCHelperSessionPrivate
, *SCHelperSessionPrivateRef
;
103 static CFStringRef
__SCHelperSessionCopyDescription (CFTypeRef cf
);
104 static void __SCHelperSessionDeallocate (CFTypeRef cf
);
107 static void __SCHelperSessionLogBacktrace (const void *value
, void *context
);
110 static CFTypeID __kSCHelperSessionTypeID
= _kCFRuntimeNotATypeID
;
111 static Boolean debug
= FALSE
;
112 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
113 static CFRunLoopRef main_runLoop
= NULL
;
114 static CFMutableSetRef sessions
= NULL
;
115 static int sessions_closed
= 0; // count of sessions recently closed
116 static int sessions_generation
= 0;
117 static pthread_mutex_t sessions_lock
= PTHREAD_MUTEX_INITIALIZER
;
127 static os_log_t log
= NULL
;
130 log
= os_log_create("com.apple.SystemConfiguration", "SCPreferences");
138 #pragma mark Helper session management
141 #if !TARGET_OS_IPHONE
143 __SCHelperSessionUseEntitlement(SCHelperSessionRef session
)
145 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
147 return sessionPrivate
->use_entitlement
;
149 #endif //!TARGET_OS_IPHONE
152 static AuthorizationRef
153 __SCHelperSessionGetAuthorization(SCHelperSessionRef session
)
155 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
157 return sessionPrivate
->authorization
;
162 __SCHelperSessionSetAuthorization(SCHelperSessionRef session
, CFTypeRef authorizationData
)
165 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
168 * On OSX, the authorizationData can either be a CFData-wrapped/externalized
169 * "authorization" or a CFString indicating that we should check entitlements.
171 * On iOS, the authorizationData is a CFString indicating that we should
172 * check entitlements.
174 pthread_mutex_lock(&sessionPrivate
->lock
);
176 if (sessionPrivate
->authorization
!= NULL
) {
177 #if !TARGET_OS_IPHONE
178 if (!__SCHelperSessionUseEntitlement(session
)) {
181 status
= AuthorizationFree(sessionPrivate
->authorization
, kAuthorizationFlagDefaults
);
182 // status = AuthorizationFree(sessionPrivate->authorization, kAuthorizationFlagDestroyRights);
183 if (status
!= errAuthorizationSuccess
) {
184 SC_log(LOG_DEBUG
, "AuthorizationFree() failed: status = %d",
188 CFRelease(sessionPrivate
->authorization
);
190 #else //!TARGET_OS_IPHONE
191 CFRelease(sessionPrivate
->authorization
);
192 #endif //!TARGET_OS_IPHONE
193 sessionPrivate
->authorization
= NULL
;
194 sessionPrivate
->use_entitlement
= FALSE
;
197 #if !TARGET_OS_IPHONE
198 if (isA_CFData(authorizationData
)) {
199 AuthorizationExternalForm extForm
;
201 if (CFDataGetLength(authorizationData
) == sizeof(extForm
.bytes
)) {
204 memcpy(extForm
.bytes
, CFDataGetBytePtr(authorizationData
), sizeof(extForm
.bytes
));
205 status
= AuthorizationCreateFromExternalForm(&extForm
,
206 &sessionPrivate
->authorization
);
207 if (status
!= errAuthorizationSuccess
) {
208 SC_log(LOG_NOTICE
, "AuthorizationCreateFromExternalForm() failed: status = %d",
210 sessionPrivate
->authorization
= NULL
;
214 } else if (isA_CFString(authorizationData
)) {
215 sessionPrivate
->authorization
= (void *)CFRetain(authorizationData
);
216 sessionPrivate
->use_entitlement
= TRUE
;
218 #else //!TARGET_OS_IPHONE
219 if (isA_CFString(authorizationData
)) {
220 sessionPrivate
->authorization
= (void *)CFRetain(authorizationData
);
221 sessionPrivate
->use_entitlement
= TRUE
;
223 #endif //!TARGET_OS_IPHONE
225 pthread_mutex_unlock(&sessionPrivate
->lock
);
231 static SCPreferencesRef
232 __SCHelperSessionGetPreferences(SCHelperSessionRef session
)
234 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
236 return sessionPrivate
->prefs
;
241 __SCHelperSessionSetThreadName(SCHelperSessionRef session
)
247 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
249 if (sessionPrivate
->mp
== NULL
) {
253 if (sessionPrivate
->prefs
!= NULL
) {
254 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
256 if (prefsPrivate
->name
!= NULL
) {
257 caller
= _SC_cfstring_to_cstring(prefsPrivate
->name
,
260 kCFStringEncodingUTF8
);
263 path
= (prefsPrivate
->newPath
!= NULL
) ? prefsPrivate
->newPath
: prefsPrivate
->path
;
265 path_s
= strrchr(path
, '/');
266 if (path_s
!= NULL
) {
272 if (caller
!= NULL
) {
273 snprintf(name
, sizeof(name
), "SESSION|%p|%s|%s%s",
274 (void *)(uintptr_t)CFMachPortGetPort(sessionPrivate
->mp
),
276 (path_s
!= NULL
) ? "*/" : "",
277 (path
!= NULL
) ? path
: "?");
278 CFAllocatorDeallocate(NULL
, caller
);
280 snprintf(name
, sizeof(name
), "SESSION|%p",
281 (void *)(uintptr_t)CFMachPortGetPort(sessionPrivate
->mp
));
284 pthread_setname_np(name
);
291 __SCHelperSessionSetPreferences(SCHelperSessionRef session
, SCPreferencesRef prefs
)
293 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
295 pthread_mutex_lock(&sessionPrivate
->lock
);
300 if (sessionPrivate
->prefs
!= NULL
) {
301 SC_log(LOG_INFO
, "%p : close", session
);
302 CFRelease(sessionPrivate
->prefs
);
305 SC_log(LOG_INFO
, "%p : open, prefs = %@", session
, prefs
);
307 sessionPrivate
->prefs
= prefs
;
309 __SCHelperSessionSetThreadName(session
);
311 pthread_mutex_unlock(&sessionPrivate
->lock
);
318 __SCHelperSessionSetNetworkSetFilter(SCHelperSessionRef session
, Boolean setChange
)
320 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
322 pthread_mutex_lock(&sessionPrivate
->lock
);
324 sessionPrivate
->isSetChange
= setChange
? YES
: NO
;
326 pthread_mutex_unlock(&sessionPrivate
->lock
);
333 __SCHelperSessionUseNetworkSetFilter(SCHelperSessionRef session
)
335 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
337 return (sessionPrivate
->isSetChange
== YES
) ? TRUE
: FALSE
;
342 __SCHelperSessionSetVPNFilter(SCHelperSessionRef session
, Boolean vpnChange
, CFArrayRef vpnTypes
)
344 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
346 pthread_mutex_lock(&sessionPrivate
->lock
);
348 sessionPrivate
->isVPNChange
= vpnChange
? YES
: NO
;
350 if (vpnTypes
!= NULL
) {
353 if (sessionPrivate
->vpnTypes
!= NULL
) {
354 CFRelease(sessionPrivate
->vpnTypes
);
356 sessionPrivate
->vpnTypes
= vpnTypes
;
358 pthread_mutex_unlock(&sessionPrivate
->lock
);
365 __SCHelperSessionUseVPNFilter(SCHelperSessionRef session
, CFArrayRef
*vpnTypes
)
367 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
369 *vpnTypes
= sessionPrivate
->vpnTypes
;
370 return (sessionPrivate
->vpnTypes
!= NULL
) ? TRUE
: FALSE
;
375 __SCHelperSessionLog(const void *value
, void *context
)
377 SCHelperSessionRef session
= (SCHelperSessionRef
)value
;
378 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
379 FILE **logFile
= (FILE **)context
;
381 pthread_mutex_lock(&sessionPrivate
->lock
);
383 if ((sessionPrivate
->mp
!= NULL
) && (sessionPrivate
->prefs
!= NULL
)) {
384 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
386 SC_log(LOG_INFO
, " %p {port = %p, caller = %@, path = %s%s}",
388 (void *)(uintptr_t)CFMachPortGetPort(sessionPrivate
->mp
),
390 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
,
391 prefsPrivate
->locked
? ", locked" : "");
393 if ((sessionPrivate
->backtraces
!= NULL
) &&
394 (CFSetGetCount(sessionPrivate
->backtraces
) > 0)) {
395 // log/report all collected backtraces
396 CFSetApplyFunction(sessionPrivate
->backtraces
,
397 __SCHelperSessionLogBacktrace
,
400 // to ensure that we don't log the same backtraces multiple
401 // times we remove any reported traces
402 CFRelease(sessionPrivate
->backtraces
);
403 sessionPrivate
->backtraces
= NULL
;
407 pthread_mutex_unlock(&sessionPrivate
->lock
);
416 static const CFRuntimeClass __SCHelperSessionClass
= {
418 "SCHelperSession", // className
421 __SCHelperSessionDeallocate
, // dealloc
424 NULL
, // copyFormattingDesc
425 __SCHelperSessionCopyDescription
// copyDebugDesc
430 __SCHelperSessionCopyDescription(CFTypeRef cf
)
432 CFAllocatorRef allocator
= CFGetAllocator(cf
);
433 CFMutableStringRef result
;
434 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)cf
;
436 pthread_mutex_lock(&sessionPrivate
->lock
);
438 result
= CFStringCreateMutable(allocator
, 0);
439 CFStringAppendFormat(result
, NULL
, CFSTR("<SCHelperSession %p [%p]> {"), cf
, allocator
);
440 CFStringAppendFormat(result
, NULL
, CFSTR("authorization = %p"), sessionPrivate
->authorization
);
441 if (sessionPrivate
->mp
!= NULL
) {
442 CFStringAppendFormat(result
, NULL
,
443 CFSTR(", mp = %p (port = 0x%x)"),
445 CFMachPortGetPort(sessionPrivate
->mp
));
447 if (sessionPrivate
->prefs
!= NULL
) {
448 CFStringAppendFormat(result
, NULL
, CFSTR(", prefs = %@"), sessionPrivate
->prefs
);
450 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
452 pthread_mutex_unlock(&sessionPrivate
->lock
);
459 __SCHelperSessionDeallocate(CFTypeRef cf
)
461 SCHelperSessionRef session
= (SCHelperSessionRef
)cf
;
462 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
464 // we're releasing "a" session so take the global lock...
465 pthread_mutex_lock(&sessions_lock
);
468 __SCHelperSessionSetAuthorization(session
, NULL
);
469 __SCHelperSessionSetPreferences(session
, NULL
);
470 __SCHelperSessionSetNetworkSetFilter(session
, FALSE
);
471 __SCHelperSessionSetVPNFilter(session
, FALSE
, NULL
);
472 pthread_mutex_destroy(&sessionPrivate
->lock
);
473 if (sessionPrivate
->backtraces
!= NULL
) {
474 CFRelease(sessionPrivate
->backtraces
);
477 // we no longer need/want to track this session
478 CFSetRemoveValue(sessions
, sessionPrivate
);
479 sessions_generation
++;
482 // release the global lock, wake up the main runloop, all done
483 pthread_mutex_unlock(&sessions_lock
);
484 CFRunLoopWakeUp(main_runLoop
);
491 __SCHelperSessionInitialize(void)
493 __kSCHelperSessionTypeID
= _CFRuntimeRegisterClass(&__SCHelperSessionClass
);
498 static SCHelperSessionRef
499 __SCHelperSessionCreate(CFAllocatorRef allocator
)
501 SCHelperSessionPrivateRef sessionPrivate
;
504 // initialize runtime
505 pthread_once(&initialized
, __SCHelperSessionInitialize
);
508 size
= sizeof(SCHelperSessionPrivate
) - sizeof(CFRuntimeBase
);
509 sessionPrivate
= (SCHelperSessionPrivateRef
)_CFRuntimeCreateInstance(allocator
,
510 __kSCHelperSessionTypeID
,
513 if (sessionPrivate
== NULL
) {
517 /* initialize non-zero/NULL members */
518 if (pthread_mutex_init(&sessionPrivate
->lock
, NULL
) != 0) {
519 SC_log(LOG_NOTICE
, "pthread_mutex_init(): failure to initialize per session lock");
520 CFRelease(sessionPrivate
);
524 pthread_mutex_lock(&sessionPrivate
->lock
);
525 sessionPrivate
->callerReadAccess
= UNKNOWN
;
526 sessionPrivate
->callerWriteAccess
= UNKNOWN
;
527 sessionPrivate
->isSetChange
= UNKNOWN
;
528 sessionPrivate
->isVPNChange
= UNKNOWN
;
529 pthread_mutex_unlock(&sessionPrivate
->lock
);
531 // keep track this session
532 pthread_mutex_lock(&sessions_lock
);
533 if (sessions
== NULL
) {
534 const CFSetCallBacks mySetCallBacks
= { 0, NULL
, NULL
, CFCopyDescription
, CFEqual
, CFHash
};
536 // create a non-retaining set
537 sessions
= CFSetCreateMutable(NULL
, 0, &mySetCallBacks
);
539 CFSetAddValue(sessions
, sessionPrivate
);
540 sessions_generation
++;
541 pthread_mutex_unlock(&sessions_lock
);
543 return (SCHelperSessionRef
)sessionPrivate
;
550 static SCHelperSessionRef
551 __SCHelperSessionFindWithPort(mach_port_t port
)
553 SCHelperSessionRef session
= NULL
;
555 // keep track this session
556 pthread_mutex_lock(&sessions_lock
);
557 if (sessions
!= NULL
) {
559 CFIndex n
= CFSetGetCount(sessions
);
560 const void * vals_q
[16];
561 const void ** vals
= vals_q
;
563 if (n
> (CFIndex
)(sizeof(vals_q
) / sizeof(SCHelperSessionRef
)))
564 vals
= CFAllocatorAllocate(NULL
, n
* sizeof(CFStringRef
), 0);
565 CFSetGetValues(sessions
, vals
);
566 for (i
= 0; i
< n
; i
++) {
567 SCHelperSessionPrivateRef sessionPrivate
;
569 sessionPrivate
= (SCHelperSessionPrivateRef
)vals
[i
];
570 if (sessionPrivate
->port
== port
) {
571 session
= (SCHelperSessionRef
)sessionPrivate
;
576 CFAllocatorDeallocate(NULL
, vals
);
578 pthread_mutex_unlock(&sessions_lock
);
585 #pragma mark Session backtrace logging
589 __SCHelperSessionAddBacktrace(SCHelperSessionRef session
, CFStringRef backtrace
, const char * command
)
591 CFStringRef logEntry
;
592 SCPreferencesRef prefs
;
593 SCPreferencesPrivateRef prefsPrivate
;
594 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
596 prefs
= __SCHelperSessionGetPreferences((SCHelperSessionRef
)sessionPrivate
);
601 prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
603 logEntry
= CFStringCreateWithFormat(NULL
, NULL
,
604 CFSTR("%@ [%s]: %s\n\n%@"),
606 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
,
610 pthread_mutex_lock(&sessionPrivate
->lock
);
612 if (sessionPrivate
->backtraces
== NULL
) {
613 sessionPrivate
->backtraces
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
615 CFSetSetValue(sessionPrivate
->backtraces
, logEntry
);
617 pthread_mutex_unlock(&sessionPrivate
->lock
);
625 __SCHelperSessionLogBacktrace(const void *value
, void *context
)
627 CFSetRef backtrace
= (CFSetRef
)value
;
628 FILE **logFile
= (FILE **)context
;
630 if (*logFile
== NULL
) {
633 struct timeval tv_now
;
635 (void)gettimeofday(&tv_now
, NULL
);
636 (void)localtime_r(&tv_now
.tv_sec
, &tm_now
);
640 _SC_CRASH_DIR
"/" "SCHelper-%4d-%02d-%02d-%02d%02d%02d.log",
641 tm_now
.tm_year
+ 1900,
648 *logFile
= fopen(path
, "a");
649 if (*logFile
== NULL
) {
650 // if log file could not be created
654 SC_log(LOG_INFO
, "created backtrace log: %s", path
);
657 SCPrint(TRUE
, *logFile
, CFSTR("%@\n"), backtrace
);
666 #define HELPER_STATUS_NO_REPLY UINT32_MAX
672 * (out) status = SCError()
676 do_Exit(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
678 #pragma unused(session)
681 #pragma unused(reply)
682 *status
= HELPER_STATUS_NO_REPLY
;
689 * (in) data = authorizationDict (in 2 flavors)
690 * kSCHelperAuthAuthorization - use provided AuthorizationExternalForm
691 * kSCHelperAuthCallerInfo - use entitlement
692 * (out) status = OSStatus
696 do_Auth(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
699 #pragma unused(reply)
700 CFDictionaryRef authorizationDict
;
701 #if !TARGET_OS_IPHONE
702 CFDataRef authorizationData
= NULL
;
703 #endif // !TARGET_OS_IPHONE
706 if (!_SCUnserialize((CFPropertyListRef
*)&authorizationDict
, data
, NULL
, 0)) {
710 if (authorizationDict
== NULL
) {
714 if (!isA_CFDictionary(authorizationDict
)) {
715 CFRelease(authorizationDict
);
719 #if !TARGET_OS_IPHONE
720 authorizationData
= CFDictionaryGetValue(authorizationDict
, kSCHelperAuthAuthorization
);
721 if (authorizationData
!= NULL
&& isA_CFData(authorizationData
)) {
722 ok
= __SCHelperSessionSetAuthorization(session
, authorizationData
);
724 #endif // !TARGET_OS_IPHONE
726 CFStringRef authorizationInfo
;
728 authorizationInfo
= CFDictionaryGetValue(authorizationDict
, kSCHelperAuthCallerInfo
);
729 if (authorizationInfo
!= NULL
&& isA_CFString(authorizationInfo
)) {
730 ok
= __SCHelperSessionSetAuthorization(session
, authorizationInfo
);
734 CFRelease(authorizationDict
);
735 *status
= ok
? 0 : 1;
740 #if !TARGET_OS_IPHONE
744 * SCHELPER_MSG_KEYCHAIN_COPY
745 * (in) data = unique_id
746 * (out) status = SCError()
747 * (out) reply = password
750 do_keychain_copy(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
754 SCPreferencesRef prefs
;
755 CFStringRef unique_id
= NULL
;
757 if ((data
!= NULL
) && !_SCUnserializeString(&unique_id
, data
, NULL
, 0)) {
761 if (unique_id
!= NULL
) {
762 if (isA_CFString(unique_id
)) {
763 prefs
= __SCHelperSessionGetPreferences(session
);
764 *reply
= _SCPreferencesSystemKeychainPasswordItemCopy(prefs
, unique_id
);
765 if (*reply
== NULL
) {
771 CFRelease(unique_id
);
779 * SCHELPER_MSG_KEYCHAIN_EXISTS
780 * (in) data = unique_id
781 * (out) status = SCError()
785 do_keychain_exists(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
788 #pragma unused(reply)
790 SCPreferencesRef prefs
;
791 CFStringRef unique_id
= NULL
;
793 if ((data
!= NULL
) && !_SCUnserializeString(&unique_id
, data
, NULL
, 0)) {
797 if (unique_id
!= NULL
) {
798 if (isA_CFString(unique_id
)) {
799 prefs
= __SCHelperSessionGetPreferences(session
);
800 ok
= _SCPreferencesSystemKeychainPasswordItemExists(prefs
, unique_id
);
806 CFRelease(unique_id
);
814 * SCHELPER_MSG_KEYCHAIN_REMOVE
815 * (in) data = unique_id
816 * (out) status = SCError()
820 do_keychain_remove(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
823 #pragma unused(reply)
825 SCPreferencesRef prefs
;
826 CFStringRef unique_id
= NULL
;
828 if ((data
!= NULL
) && !_SCUnserializeString(&unique_id
, data
, NULL
, 0)) {
832 if (unique_id
!= NULL
) {
833 if (isA_CFString(unique_id
)) {
834 prefs
= __SCHelperSessionGetPreferences(session
);
835 ok
= _SCPreferencesSystemKeychainPasswordItemRemove(prefs
, unique_id
);
841 CFRelease(unique_id
);
849 * SCHELPER_MSG_KEYCHAIN_SET
850 * (in) data = options dictionary
851 * (out) status = SCError()
855 do_keychain_set(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
858 #pragma unused(reply)
860 CFStringRef description
;
861 CFArrayRef executablePaths
= NULL
;
864 CFDictionaryRef options
= NULL
;
866 SCPreferencesRef prefs
;
867 CFStringRef unique_id
;
869 if ((data
!= NULL
) && !_SCUnserialize((CFPropertyListRef
*)&options
, data
, NULL
, 0)) {
873 if (options
!= NULL
) {
874 if (!isA_CFDictionary(options
)) {
882 if (CFDictionaryGetValueIfPresent(options
,
883 kSCKeychainOptionsAllowedExecutables
,
884 (const void **)&executablePaths
)) {
885 CFMutableArrayRef executableURLs
;
888 CFMutableDictionaryRef newOptions
;
890 executableURLs
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
891 n
= CFArrayGetCount(executablePaths
);
892 for (i
= 0; i
< n
; i
++) {
896 path
= CFArrayGetValueAtIndex(executablePaths
, i
);
897 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
898 CFDataGetBytePtr(path
),
899 CFDataGetLength(path
),
902 CFArrayAppendValue(executableURLs
, url
);
907 newOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, options
);
908 CFDictionarySetValue(newOptions
, kSCKeychainOptionsAllowedExecutables
, executableURLs
);
909 CFRelease(executableURLs
);
912 options
= newOptions
;
915 unique_id
= CFDictionaryGetValue(options
, kSCKeychainOptionsUniqueID
);
916 label
= CFDictionaryGetValue(options
, kSCKeychainOptionsLabel
);
917 description
= CFDictionaryGetValue(options
, kSCKeychainOptionsDescription
);
918 account
= CFDictionaryGetValue(options
, kSCKeychainOptionsAccount
);
919 password
= CFDictionaryGetValue(options
, kSCKeychainOptionsPassword
);
921 prefs
= __SCHelperSessionGetPreferences(session
);
922 ok
= _SCPreferencesSystemKeychainPasswordItemSet(prefs
,
938 #endif // !TARGET_OS_IPHONE
942 * SCHELPER_MSG_INTERFACE_REFRESH
944 * (out) status = SCError()
948 do_interface_refresh(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
950 #pragma unused(session)
952 #pragma unused(reply)
953 CFStringRef ifName
= NULL
;
956 if ((data
!= NULL
) && !_SCUnserializeString(&ifName
, data
, NULL
, 0)) {
957 *status
= kSCStatusInvalidArgument
;
958 SC_log(LOG_NOTICE
, "interface name not valid");
962 if (ifName
== NULL
) {
963 *status
= kSCStatusInvalidArgument
;
964 SC_log(LOG_NOTICE
, "interface name not valid");
968 if (isA_CFString(ifName
)) {
969 ok
= _SCNetworkInterfaceForceConfigurationRefresh(ifName
);
972 SC_log(LOG_NOTICE
, "interface \"%@\" not refreshed: %s",
974 SCErrorString(*status
));
977 *status
= kSCStatusInvalidArgument
;
978 SC_log(LOG_NOTICE
, "interface name not valid");
989 * (in) data = prefsID
990 * (out) status = SCError()
994 do_prefs_Open(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
997 #pragma unused(reply)
999 CFDictionaryRef options
;
1001 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1002 CFDictionaryRef prefsInfo
= NULL
;
1003 CFStringRef prefsID
;
1004 CFStringRef prefsName
;
1005 CFStringRef proc_name
;
1007 if (prefs
!= NULL
) {
1011 if ((data
!= NULL
) && !_SCUnserialize((CFPropertyListRef
*)&prefsInfo
, data
, NULL
, 0)) {
1012 SC_log(LOG_NOTICE
, "data not valid, %@", data
);
1016 if ((prefsInfo
== NULL
) || !isA_CFDictionary(prefsInfo
)) {
1017 SC_log(LOG_NOTICE
, "info not valid");
1018 if (prefsInfo
!= NULL
) CFRelease(prefsInfo
);
1022 // get [optional] prefsID
1023 prefsID
= CFDictionaryGetValue(prefsInfo
, CFSTR("prefsID"));
1024 prefsID
= isA_CFString(prefsID
);
1025 if (prefsID
!= NULL
) {
1026 if (CFStringHasPrefix(prefsID
, CFSTR("/")) ||
1027 CFStringHasPrefix(prefsID
, CFSTR("../")) ||
1028 CFStringHasSuffix(prefsID
, CFSTR("/..")) ||
1029 (CFStringFind(prefsID
, CFSTR("/../"), 0).location
!= kCFNotFound
)) {
1030 // if we're trying to escape from the preferences directory
1031 SC_log(LOG_NOTICE
, "prefsID (%@) not valid", prefsID
);
1032 CFRelease(prefsInfo
);
1033 *status
= kSCStatusInvalidArgument
;
1038 // get [optional] options
1039 options
= CFDictionaryGetValue(prefsInfo
, CFSTR("options"));
1040 options
= isA_CFDictionary(options
);
1042 // get preferences session "name"
1043 name
= CFDictionaryGetValue(prefsInfo
, CFSTR("name"));
1044 if (!isA_CFString(name
)) {
1045 SC_log(LOG_NOTICE
, "session \"name\" not valid");
1046 CFRelease(prefsInfo
);
1050 // get PID of caller
1051 pid
= CFDictionaryGetValue(prefsInfo
, CFSTR("PID"));
1052 if (!isA_CFNumber(pid
)) {
1053 SC_log(LOG_NOTICE
, "PID not valid");
1054 CFRelease(prefsInfo
);
1058 // get process name of caller
1059 proc_name
= CFDictionaryGetValue(prefsInfo
, CFSTR("PROC_NAME"));
1060 if (!isA_CFString(proc_name
)) {
1061 SC_log(LOG_NOTICE
, "process name not valid");
1062 CFRelease(prefsInfo
);
1066 // build [helper] preferences "name" (used for debugging) and estabish
1067 // a preferences session.
1068 prefsName
= CFStringCreateWithFormat(NULL
, NULL
,
1074 prefs
= SCPreferencesCreateWithOptions(NULL
, prefsName
, prefsID
, NULL
, options
);
1075 CFRelease(prefsName
);
1076 CFRelease(prefsInfo
);
1078 __SCHelperSessionSetPreferences(session
, prefs
);
1080 if (prefs
!= NULL
) {
1083 CFRunLoopRef rl
= CFRunLoopGetCurrent();
1085 // [temporarily] schedule notifications to ensure that we can use
1086 // the SCDynamicStore to track helper sessions
1087 ok
= SCPreferencesScheduleWithRunLoop(prefs
, rl
, kCFRunLoopDefaultMode
);
1089 (void)SCPreferencesUnscheduleFromRunLoop(prefs
, rl
, kCFRunLoopDefaultMode
);
1093 // the session now holds a references to the SCPreferencesRef
1096 *status
= SCError();
1106 * (out) status = SCError()
1107 * (out) reply = current signature + current preferences
1110 do_prefs_Access(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1112 #pragma unused(info)
1113 #pragma unused(data)
1115 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1116 CFDataRef signature
;
1118 if (prefs
== NULL
) {
1122 signature
= SCPreferencesGetSignature(prefs
);
1123 if (signature
!= NULL
) {
1124 const void * dictKeys
[2];
1125 const void * dictVals
[2];
1126 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1127 CFDictionaryRef replyDict
;
1129 dictKeys
[0] = CFSTR("signature");
1130 dictVals
[0] = signature
;
1132 dictKeys
[1] = CFSTR("preferences");
1133 dictVals
[1] = prefsPrivate
->prefs
;
1135 replyDict
= CFDictionaryCreate(NULL
,
1136 (const void **)&dictKeys
,
1137 (const void **)&dictVals
,
1138 sizeof(dictKeys
)/sizeof(dictKeys
[0]),
1139 &kCFTypeDictionaryKeyCallBacks
,
1140 &kCFTypeDictionaryValueCallBacks
);
1142 ok
= _SCSerialize(replyDict
, reply
, NULL
, NULL
);
1143 CFRelease(replyDict
);
1148 *status
= SCError();
1157 * (in) data = client prefs signature (NULL if check not needed)
1158 * (out) status = SCError()
1162 do_prefs_Lock(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1164 #pragma unused(reply)
1165 CFDataRef clientSignature
= (CFDataRef
)data
;
1167 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1168 Boolean wait
= (info
== (void *)FALSE
) ? FALSE
: TRUE
;
1170 if (prefs
== NULL
) {
1174 ok
= SCPreferencesLock(prefs
, wait
);
1176 *status
= SCError();
1180 if (clientSignature
!= NULL
) {
1181 CFDataRef serverSignature
;
1183 serverSignature
= SCPreferencesGetSignature(prefs
);
1184 if (!CFEqual(clientSignature
, serverSignature
)) {
1185 (void)SCPreferencesUnlock(prefs
);
1186 *status
= kSCStatusStale
;
1196 * (in) data = new preferences (NULL if commit w/no changes)
1197 * (out) status = SCError()
1198 * (out) reply = new signature
1201 do_prefs_Commit(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1203 #pragma unused(info)
1205 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1206 CFPropertyListRef prefsData
= NULL
;
1207 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1208 Boolean saveAccessed
;
1209 Boolean saveChanged
;
1210 CFMutableDictionaryRef savePrefs
= NULL
;
1211 Boolean saveValid
= FALSE
;
1212 Boolean useSetFilter
;
1213 Boolean useVPNFilter
;
1214 CFArrayRef vpnTypes
= NULL
;
1216 if (prefs
== NULL
) {
1221 // if commit with no changes
1225 ok
= _SCUnserialize(&prefsData
, data
, NULL
, 0);
1230 if (!isA_CFDictionary(prefsData
)) {
1231 *status
= kSCStatusFailed
;
1236 useSetFilter
= __SCHelperSessionUseNetworkSetFilter(session
);
1237 useVPNFilter
= __SCHelperSessionUseVPNFilter(session
, &vpnTypes
);
1238 if (useSetFilter
|| useVPNFilter
) {
1241 if (prefsPrivate
->prefs
!= NULL
) {
1243 CFMutableDictionaryRef prefsNew
= NULL
;
1244 CFMutableDictionaryRef prefsOld
= NULL
;
1245 CFMutableDictionaryRef prefsSave
= prefsPrivate
->prefs
;
1247 for (c
= 0; c
< 2; c
++) {
1251 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, prefsSave
);
1254 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, prefsData
);
1259 // filter out current network set selection
1260 (void) SCPreferencesRemoveValue(prefs
, kSCPrefCurrentSet
);
1261 } else if (useVPNFilter
) {
1262 CFRange range
= CFRangeMake(0, CFArrayGetCount(vpnTypes
));
1263 CFArrayRef services
;
1265 // filter out VPN services of the specified type
1266 services
= SCNetworkServiceCopyAll(prefs
);
1267 if (services
!= NULL
) {
1269 CFIndex n
= CFArrayGetCount(services
);
1271 for (i
= 0; i
< n
; i
++) {
1272 SCNetworkServiceRef service
;
1274 service
= CFArrayGetValueAtIndex(services
, i
);
1275 if (_SCNetworkServiceIsVPN(service
)) {
1276 SCNetworkInterfaceRef child
;
1277 CFStringRef childType
= NULL
;
1278 SCNetworkInterfaceRef interface
;
1279 CFStringRef interfaceType
;
1281 interface
= SCNetworkServiceGetInterface(service
);
1282 interfaceType
= SCNetworkInterfaceGetInterfaceType(interface
);
1283 child
= SCNetworkInterfaceGetInterface(interface
);
1284 if (child
!= NULL
) {
1285 childType
= SCNetworkInterfaceGetInterfaceType(child
);
1287 if (CFEqual(interfaceType
, kSCNetworkInterfaceTypeVPN
) &&
1288 (childType
!= NULL
) &&
1289 CFArrayContainsValue(vpnTypes
, range
, childType
)) {
1290 // filter out VPN service
1291 (void) SCNetworkServiceRemove(service
);
1293 // mark all other VPN services "enabled"
1294 (void) SCNetworkServiceSetEnabled(service
, TRUE
);
1299 CFRelease(services
);
1305 prefsOld
= prefsPrivate
->prefs
;
1308 prefsNew
= prefsPrivate
->prefs
;
1313 // compare the filtered configurations
1314 ok
= _SC_CFEqual(prefsOld
, prefsNew
);
1317 if (prefsOld
!= NULL
) CFRelease(prefsOld
);
1318 if (prefsNew
!= NULL
) CFRelease(prefsNew
);
1319 prefsPrivate
->prefs
= prefsSave
;
1323 *status
= kSCStatusAccessError
;
1328 /* Take a backup of prefs, accessed bit, changed bit to
1329 restore them IFF the commit fails. Pretend as if the
1330 commit never happened!
1332 savePrefs
= prefsPrivate
->prefs
;
1333 saveAccessed
= prefsPrivate
->accessed
;
1334 saveChanged
= prefsPrivate
->changed
;
1336 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, prefsData
);
1337 prefsPrivate
->accessed
= TRUE
;
1338 prefsPrivate
->changed
= TRUE
;
1343 ok
= SCPreferencesCommitChanges(prefs
);
1345 if (savePrefs
!= NULL
) {
1346 CFRelease(savePrefs
);
1348 *reply
= SCPreferencesGetSignature(prefs
);
1351 /* Restore the backup we took earlier */
1353 if (prefsPrivate
->prefs
!= NULL
) {
1354 CFRelease(prefsPrivate
->prefs
);
1357 prefsPrivate
->prefs
= savePrefs
;
1358 prefsPrivate
->accessed
= saveAccessed
;
1359 prefsPrivate
->changed
= saveChanged
;
1361 *status
= SCError();
1366 if (prefsData
!= NULL
) CFRelease(prefsData
);
1374 * (out) status = SCError()
1378 do_prefs_Apply(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1380 #pragma unused(info)
1381 #pragma unused(data)
1382 #pragma unused(reply)
1384 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1386 if (prefs
== NULL
) {
1390 ok
= SCPreferencesApplyChanges(prefs
);
1392 *status
= SCError();
1402 * (out) status = SCError()
1406 do_prefs_Unlock(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1408 #pragma unused(info)
1409 #pragma unused(data)
1410 #pragma unused(reply)
1412 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1414 if (prefs
== NULL
) {
1418 ok
= SCPreferencesUnlock(prefs
);
1420 *status
= SCError();
1430 * (out) status = SCError()
1434 do_prefs_Close(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1436 #pragma unused(info)
1437 #pragma unused(data)
1438 #pragma unused(reply)
1439 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1441 if (prefs
== NULL
) {
1445 __SCHelperSessionSetPreferences(session
, NULL
);
1446 *status
= HELPER_STATUS_NO_REPLY
;
1454 * (out) status = kSCStatusOK
1458 do_prefs_Synchronize(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1460 #pragma unused(info)
1461 #pragma unused(data)
1462 #pragma unused(reply)
1463 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1465 if (prefs
== NULL
) {
1469 SCPreferencesSynchronize(prefs
);
1470 *status
= kSCStatusOK
;
1476 #pragma mark Process commands
1480 sessionName(SCHelperSessionRef session
)
1482 CFStringRef name
= NULL
;
1483 SCPreferencesRef prefs
;
1485 prefs
= __SCHelperSessionGetPreferences(session
);
1486 if (prefs
!= NULL
) {
1487 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1489 name
= prefsPrivate
->name
;
1492 return (name
!= NULL
) ? name
: CFSTR("???");
1497 sessionPrefsID(SCHelperSessionRef session
)
1499 CFStringRef prefsID
;
1500 SCPreferencesPrivateRef prefsPrivate
;
1501 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1503 prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
1504 if ((prefsPrivate
!= NULL
) && (prefsPrivate
->prefsID
!= NULL
)) {
1505 prefsID
= prefsPrivate
->prefsID
;
1507 prefsID
= PREFS_DEFAULT_CONFIG
;
1515 copyEntitlement(SCHelperSessionRef session
, CFStringRef entitlement
)
1517 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1519 CFTypeRef value
= NULL
;
1521 // Create the security task from the audit token
1522 task
= SecTaskCreateWithAuditToken(NULL
, sessionPrivate
->auditToken
);
1524 CFErrorRef error
= NULL
;
1526 // Get the value for the entitlement
1527 value
= SecTaskCopyValueForEntitlement(task
, entitlement
, &error
);
1528 if ((value
== NULL
) && (error
!= NULL
)) {
1529 CFIndex code
= CFErrorGetCode(error
);
1530 CFStringRef domain
= CFErrorGetDomain(error
);
1532 if (!CFEqual(domain
, kCFErrorDomainMach
) ||
1533 ((code
!= kIOReturnInvalid
) && (code
!= kIOReturnNotFound
))) {
1534 // if unexpected error
1535 SC_log(LOG_NOTICE
, "SecTaskCopyValueForEntitlement(,\"%@\",) failed, error = %@ : %@",
1538 sessionName(session
));
1545 SC_log(LOG_NOTICE
, "SecTaskCreateWithAuditToken() failed: %@",
1546 sessionName(session
));
1553 #if !TARGET_OS_IPHONE
1555 isSetChange(SCHelperSessionRef session
)
1557 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1559 if (sessionPrivate
->isSetChange
== UNKNOWN
) {
1560 CFBooleanRef bVal
= NULL
;
1561 CFStringRef prefsID
;
1562 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
1563 Boolean setFilter
= FALSE
;
1565 prefsID
= sessionPrefsID(session
);
1566 if (CFEqual(prefsID
, PREFS_DEFAULT_CONFIG
) &&
1567 isA_CFDictionary(prefsPrivate
->options
) &&
1568 CFDictionaryGetValueIfPresent(prefsPrivate
->options
,
1569 kSCPreferencesOptionChangeNetworkSet
,
1570 (const void **)&bVal
) &&
1571 isA_CFBoolean(bVal
) &&
1572 CFBooleanGetValue(bVal
)) {
1576 // establish network set (location) filter
1577 __SCHelperSessionSetNetworkSetFilter(session
, setFilter
);
1580 return (sessionPrivate
->isSetChange
== YES
) ? TRUE
: FALSE
;
1582 #endif // !TARGET_OS_IPHONE
1586 isVPNChange(SCHelperSessionRef session
)
1588 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1590 if (sessionPrivate
->isVPNChange
== UNKNOWN
) {
1591 CFArrayRef entitlement
;
1592 Boolean vpnChange
= FALSE
;
1593 CFArrayRef vpnTypes
= NULL
;
1595 entitlement
= copyEntitlement(session
, kSCVPNFilterEntitlementName
);
1596 if (entitlement
!= NULL
) {
1597 if (isA_CFArray(entitlement
)) {
1598 CFStringRef prefsID
;
1600 prefsID
= sessionPrefsID(session
);
1601 if (CFEqual(prefsID
, PREFS_DEFAULT_CONFIG
)) {
1602 // save the VPN type identifiers
1603 vpnTypes
= CFRetain(entitlement
);
1605 // grant an exception
1607 } else if (CFStringHasPrefix(prefsID
, CFSTR("VPN-")) &&
1608 CFStringHasSuffix(prefsID
, CFSTR(".plist"))) {
1612 range
.location
= sizeof("VPN-") - 1;
1613 range
.length
= CFStringGetLength(prefsID
)
1614 - (sizeof("VPN-") - 1) // trim VPN-
1615 - (sizeof(".plist") - 1); // trim .plist
1616 vpnID
= CFStringCreateWithSubstring(NULL
, prefsID
, range
);
1617 if (CFArrayContainsValue(entitlement
,
1618 CFRangeMake(0, CFArrayGetCount(entitlement
)),
1620 // grant an exception
1627 CFRelease(entitlement
);
1630 __SCHelperSessionSetVPNFilter(session
, vpnChange
, vpnTypes
);
1631 if (vpnTypes
!= NULL
) {
1632 CFRelease(vpnTypes
);
1636 return (sessionPrivate
->isVPNChange
== YES
) ? TRUE
: FALSE
;
1641 checkEntitlement(SCHelperSessionRef session
, CFStringRef prefsID
, CFStringRef entitlement_name
)
1643 CFArrayRef entitlement
;
1644 Boolean hasEntitlement
= FALSE
;
1646 entitlement
= copyEntitlement(session
, entitlement_name
);
1647 if (entitlement
!= NULL
) {
1648 if (isA_CFArray(entitlement
)) {
1649 if (CFArrayContainsValue(entitlement
,
1650 CFRangeMake(0, CFArrayGetCount(entitlement
)),
1652 // if client DOES have entitlement
1653 hasEntitlement
= TRUE
;
1656 SC_log(LOG_NOTICE
, "hasAuthorization() session=%@: entitlement=%@: not valid",
1657 sessionName(session
),
1661 CFRelease(entitlement
);
1664 #if TARGET_OS_IPHONE
1665 // make an exception for VPN configuration management
1666 if (!hasEntitlement
) {
1667 if (isVPNChange(session
)) {
1668 // grant a "filtered" exception
1669 hasEntitlement
= TRUE
;
1672 #endif // TARGET_OS_IPHONE
1674 return hasEntitlement
;
1679 hasAuthorization(SCHelperSessionRef session
, Boolean needWrite
)
1681 AuthorizationRef authorization
= __SCHelperSessionGetAuthorization(session
);
1682 CFStringRef prefsID
;
1683 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1685 if (authorization
== NULL
) {
1689 #if !TARGET_OS_IPHONE
1690 if (!__SCHelperSessionUseEntitlement(session
)) {
1691 AuthorizationFlags flags
;
1692 AuthorizationItem items
[1];
1693 AuthorizationRights rights
;
1696 if (isSetChange(session
)) {
1697 items
[0].name
= kSCPreferencesAuthorizationRight_network_set
;
1698 items
[0].value
= NULL
;
1699 items
[0].valueLength
= 0;
1701 } else if (isVPNChange(session
)) {
1702 items
[0].name
= kSCPreferencesAuthorizationRight_write
;
1703 items
[0].value
= NULL
;
1704 items
[0].valueLength
= 0;
1707 items
[0].name
= kSCPreferencesAuthorizationRight_write
;
1708 items
[0].value
= NULL
;
1709 items
[0].valueLength
= 0;
1713 rights
.count
= sizeof(items
) / sizeof(items
[0]);
1714 rights
.items
= items
;
1716 flags
= kAuthorizationFlagDefaults
;
1717 flags
|= kAuthorizationFlagInteractionAllowed
;
1718 flags
|= kAuthorizationFlagExtendRights
;
1719 // flags |= kAuthorizationFlagPartialRights;
1720 // flags |= kAuthorizationFlagPreAuthorize;
1722 status
= AuthorizationCopyRights(authorization
,
1724 kAuthorizationEmptyEnvironment
,
1727 if (status
!= errAuthorizationSuccess
) {
1728 SC_log(LOG_INFO
, "AuthorizationCopyRights() failed: status = %d",
1735 #endif // !TARGET_OS_IPHONE
1737 prefsID
= sessionPrefsID(session
);
1739 if (sessionPrivate
->callerWriteAccess
== UNKNOWN
) {
1740 if (checkEntitlement(session
, prefsID
, kSCWriteEntitlementName
)) {
1741 sessionPrivate
->callerWriteAccess
= YES
;
1742 sessionPrivate
->callerReadAccess
= YES
; // implied
1744 sessionPrivate
->callerWriteAccess
= NO
;
1749 if (sessionPrivate
->callerWriteAccess
== YES
) {
1752 SC_log(LOG_NOTICE
, "SCPreferences write access to \"%@\" denied, no entitlement for \"%@\"",
1754 sessionName(session
));
1759 if (sessionPrivate
->callerReadAccess
== UNKNOWN
) {
1760 if (checkEntitlement(session
, prefsID
, kSCReadEntitlementName
)) {
1761 sessionPrivate
->callerReadAccess
= YES
;
1763 sessionPrivate
->callerWriteAccess
= NO
;
1767 if (sessionPrivate
->callerReadAccess
== YES
) {
1771 SC_log(LOG_NOTICE
, "SCPreferences access to \"%@\" denied, no entitlement for \"%@\"",
1773 sessionName(session
));
1778 typedef Boolean (*helperFunction
) (SCHelperSessionRef session
,
1785 static const struct helper
{
1787 const char *commandName
;
1788 Boolean needsAuthorization
;
1790 helperFunction func
;
1793 { SCHELPER_MSG_AUTH
, "AUTH", FALSE
, FALSE
, do_Auth
, NULL
},
1795 { SCHELPER_MSG_PREFS_OPEN
, "PREFS open", FALSE
, FALSE
, do_prefs_Open
, NULL
},
1796 { SCHELPER_MSG_PREFS_ACCESS
, "PREFS access", TRUE
, FALSE
, do_prefs_Access
, NULL
},
1797 { SCHELPER_MSG_PREFS_LOCK
, "PREFS lock", TRUE
, TRUE
, do_prefs_Lock
, (void *)FALSE
},
1798 { SCHELPER_MSG_PREFS_LOCKWAIT
, "PREFS lock/wait", TRUE
, TRUE
, do_prefs_Lock
, (void *)TRUE
},
1799 { SCHELPER_MSG_PREFS_COMMIT
, "PREFS commit", TRUE
, TRUE
, do_prefs_Commit
, NULL
},
1800 { SCHELPER_MSG_PREFS_APPLY
, "PREFS apply", TRUE
, TRUE
, do_prefs_Apply
, NULL
},
1801 { SCHELPER_MSG_PREFS_UNLOCK
, "PREFS unlock", FALSE
, TRUE
, do_prefs_Unlock
, NULL
},
1802 { SCHELPER_MSG_PREFS_CLOSE
, "PREFS close", FALSE
, FALSE
, do_prefs_Close
, NULL
},
1803 { SCHELPER_MSG_PREFS_SYNCHRONIZE
, "PREFS synchronize", FALSE
, FALSE
, do_prefs_Synchronize
, NULL
},
1805 { SCHELPER_MSG_INTERFACE_REFRESH
, "INTERFACE refresh", TRUE
, TRUE
, do_interface_refresh
, NULL
},
1807 #if !TARGET_OS_IPHONE
1808 { SCHELPER_MSG_KEYCHAIN_COPY
, "KEYCHAIN copy", TRUE
, FALSE
, do_keychain_copy
, NULL
},
1809 { SCHELPER_MSG_KEYCHAIN_EXISTS
, "KEYCHAIN exists", TRUE
, FALSE
, do_keychain_exists
, NULL
},
1810 { SCHELPER_MSG_KEYCHAIN_REMOVE
, "KEYCHAIN remove", TRUE
, TRUE
, do_keychain_remove
, NULL
},
1811 { SCHELPER_MSG_KEYCHAIN_SET
, "KEYCHAIN set", TRUE
, TRUE
, do_keychain_set
, NULL
},
1812 #endif // !TARGET_OS_IPHONE
1814 { SCHELPER_MSG_EXIT
, "EXIT", FALSE
, FALSE
, do_Exit
, NULL
}
1816 #define nHELPERS (sizeof(helpers)/sizeof(struct helper))
1820 findCommand(int command
)
1822 for (int i
= 0; i
< (int)nHELPERS
; i
++) {
1823 if (helpers
[i
].command
== command
) {
1833 newHelper(void *arg
)
1836 CFRunLoopSourceRef rls
= NULL
;
1837 SCHelperSessionRef session
= (SCHelperSessionRef
)arg
;
1838 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1840 assert(session
!= NULL
);
1841 assert(sessionPrivate
->mp
!= NULL
);
1843 __SCHelperSessionSetThreadName(session
);
1845 ret
= pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED
, 0);
1847 SC_log(LOG_ERR
, "pthread_set_qos_class_self_np() failed: %s", strerror(errno
));
1850 rls
= CFMachPortCreateRunLoopSource(NULL
, sessionPrivate
->mp
, 0);
1851 CFRelease(sessionPrivate
->mp
);
1854 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
1857 SC_log(LOG_INFO
, "%p : start", session
);
1859 SC_log(LOG_INFO
, "%p : stop", session
);
1867 #pragma mark Main loop
1870 // MiG generated externals and functions
1871 extern struct mig_subsystem _helper_subsystem
;
1872 extern boolean_t
helper_server(mach_msg_header_t
*, mach_msg_header_t
*);
1877 notify_server(mach_msg_header_t
*request
, mach_msg_header_t
*reply
)
1879 mach_no_senders_notification_t
*Request
= (mach_no_senders_notification_t
*)request
;
1880 mig_reply_error_t
*Reply
= (mig_reply_error_t
*)reply
;
1882 reply
->msgh_bits
= MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request
->msgh_bits
), 0);
1883 reply
->msgh_remote_port
= request
->msgh_remote_port
;
1884 reply
->msgh_size
= sizeof(mig_reply_error_t
); /* Minimal size: update as needed */
1885 reply
->msgh_local_port
= MACH_PORT_NULL
;
1886 reply
->msgh_id
= request
->msgh_id
+ 100;
1888 if ((Request
->not_header
.msgh_id
> MACH_NOTIFY_LAST
) ||
1889 (Request
->not_header
.msgh_id
< MACH_NOTIFY_FIRST
)) {
1890 Reply
->NDR
= NDR_record
;
1891 Reply
->RetCode
= MIG_BAD_ID
;
1892 return FALSE
; /* if this is not a notification message */
1895 switch (Request
->not_header
.msgh_id
) {
1896 case MACH_NOTIFY_NO_SENDERS
: {
1897 SCHelperSessionRef session
;
1899 __MACH_PORT_DEBUG(TRUE
, "*** notify_server MACH_NOTIFY_NO_SENDERS", Request
->not_header
.msgh_local_port
);
1902 session
= __SCHelperSessionFindWithPort(Request
->not_header
.msgh_local_port
);
1903 if (session
!= NULL
) {
1904 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1906 // release CFMachPort *and* SCHelperSession
1907 CFMachPortInvalidate(sessionPrivate
->mp
);
1910 __MACH_PORT_DEBUG(TRUE
, "*** notify_server after invalidate", Request
->not_header
.msgh_local_port
);
1912 // and, lastly, remove our receive right.
1913 (void) mach_port_mod_refs(mach_task_self(),
1914 Request
->not_header
.msgh_local_port
,
1915 MACH_PORT_RIGHT_RECEIVE
, -1);
1917 Reply
->Head
.msgh_bits
= 0;
1918 Reply
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1919 Reply
->RetCode
= KERN_SUCCESS
;
1927 SC_log(LOG_NOTICE
, "HELP!, Received notification: port=%d, msgh_id=%d",
1928 Request
->not_header
.msgh_local_port
,
1929 Request
->not_header
.msgh_id
);
1931 Reply
->NDR
= NDR_record
;
1932 Reply
->RetCode
= MIG_BAD_ID
;
1933 return FALSE
; /* if this is not a notification we are handling */
1939 helper_demux(mach_msg_header_t
*request
, mach_msg_header_t
*reply
)
1941 Boolean processed
= FALSE
;
1944 * (attempt to) process SCHelper requests.
1946 processed
= helper_server(request
, reply
);
1952 * (attempt to) process (NO MORE SENDERS) notification messages.
1954 processed
= notify_server(request
, reply
);
1960 * unknown message ID, log and return an error.
1962 SC_log(LOG_NOTICE
, "helper_demux(): unknown message ID (%d) received", request
->msgh_id
);
1963 reply
->msgh_bits
= MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request
->msgh_bits
), 0);
1964 reply
->msgh_remote_port
= request
->msgh_remote_port
;
1965 reply
->msgh_size
= sizeof(mig_reply_error_t
); /* Minimal size */
1966 reply
->msgh_local_port
= MACH_PORT_NULL
;
1967 reply
->msgh_id
= request
->msgh_id
+ 100;
1968 ((mig_reply_error_t
*)reply
)->NDR
= NDR_record
;
1969 ((mig_reply_error_t
*)reply
)->RetCode
= MIG_BAD_ID
;
1975 #define MACH_MSG_BUFFER_SIZE 128
1979 helperCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1981 #pragma unused(port)
1982 #pragma unused(size)
1983 #pragma unused(info)
1984 mig_reply_error_t
* bufRequest
= msg
;
1985 uint32_t bufReply_q
[MACH_MSG_BUFFER_SIZE
/sizeof(uint32_t)];
1986 mig_reply_error_t
* bufReply
= (mig_reply_error_t
*)bufReply_q
;
1987 static size_t bufSize
= 0;
1988 mach_msg_return_t mr
;
1992 // get max size for MiG reply buffers
1993 bufSize
= _helper_subsystem
.maxsize
;
1995 // check if our on-the-stack reply buffer will be big enough
1996 if (bufSize
> sizeof(bufReply_q
)) {
1997 SC_log(LOG_NOTICE
, "buffer size should be increased > %d",
1998 _helper_subsystem
.maxsize
);
2002 if (bufSize
> sizeof(bufReply_q
)) {
2003 bufReply
= CFAllocatorAllocate(NULL
, _helper_subsystem
.maxsize
, 0);
2005 bufReply
->RetCode
= 0;
2007 /* we have a request message */
2008 (void) helper_demux(&bufRequest
->Head
, &bufReply
->Head
);
2010 if (!(bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)) {
2011 if (bufReply
->RetCode
== MIG_NO_REPLY
) {
2012 bufReply
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
2013 } else if ((bufReply
->RetCode
!= KERN_SUCCESS
) &&
2014 (bufRequest
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)) {
2016 * destroy the request - but not the reply port
2018 bufRequest
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
2019 mach_msg_destroy(&bufRequest
->Head
);
2023 if (bufReply
->Head
.msgh_remote_port
!= MACH_PORT_NULL
) {
2027 * We don't want to block indefinitely because the client
2028 * isn't receiving messages from the reply port.
2029 * If we have a send-once right for the reply port, then
2030 * this isn't a concern because the send won't block.
2031 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
2032 * To avoid falling off the kernel's fast RPC path unnecessarily,
2033 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
2036 options
= MACH_SEND_MSG
;
2037 if (MACH_MSGH_BITS_REMOTE(bufReply
->Head
.msgh_bits
) != MACH_MSG_TYPE_MOVE_SEND_ONCE
) {
2038 options
|= MACH_SEND_TIMEOUT
;
2040 mr
= mach_msg(&bufReply
->Head
, /* msg */
2041 options
, /* option */
2042 bufReply
->Head
.msgh_size
, /* send_size */
2044 MACH_PORT_NULL
, /* rcv_name */
2045 MACH_MSG_TIMEOUT_NONE
, /* timeout */
2046 MACH_PORT_NULL
); /* notify */
2048 /* Has a message error occurred? */
2050 case MACH_SEND_INVALID_DEST
:
2051 case MACH_SEND_TIMED_OUT
:
2054 /* Includes success case. */
2059 if (bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
2060 mach_msg_destroy(&bufReply
->Head
);
2065 if (bufReply
!= (mig_reply_error_t
*)bufReply_q
)
2066 CFAllocatorDeallocate(NULL
, bufReply
);
2073 initMPCopyDescription(const void *info
)
2075 #pragma unused(info)
2076 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<SCHelper MP>"));
2082 _helperinit(mach_port_t server
,
2083 mach_port_t
*newSession
,
2085 audit_token_t audit_token
)
2087 CFMachPortContext context
= { 0
2091 , initMPCopyDescription
2094 mach_port_t oldNotify
;
2095 SCHelperSessionRef session
;
2096 SCHelperSessionPrivateRef sessionPrivate
;
2097 pthread_attr_t tattr
;
2100 session
= __SCHelperSessionFindWithPort(server
);
2101 if (session
!= NULL
) {
2103 SC_log(LOG_DEBUG
, "session is already open");
2105 *status
= kSCStatusFailed
; /* you can't re-open an "open" session */
2106 return KERN_SUCCESS
;
2109 session
= __SCHelperSessionCreate(NULL
);
2110 assert(session
!= NULL
);
2111 sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
2113 // create per-session port
2114 kr
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, &sessionPrivate
->port
);
2115 if (kr
!= KERN_SUCCESS
) {
2116 SC_log(LOG_ERR
, "mach_port_allocate() failed: %s", mach_error_string(kr
));
2121 *newSession
= sessionPrivate
->port
;
2123 (void) mach_port_set_attributes(mach_task_self(),
2125 MACH_PORT_IMPORTANCE_RECEIVER
,
2130 // Note: we create the CFMachPort *before* we insert a send
2131 // right present to ensure that CF does not establish
2132 // its dead name notification.
2134 context
.info
= (void *)session
;
2135 sessionPrivate
->mp
= _SC_CFMachPortCreateWithPort("SCHelper/session",
2140 /* Request a notification when/if the client dies */
2141 kr
= mach_port_request_notification(mach_task_self(),
2143 MACH_NOTIFY_NO_SENDERS
,
2146 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
2148 if (kr
!= KERN_SUCCESS
) {
2149 SC_log(LOG_NOTICE
, "mach_port_request_notification() failed: %s", mach_error_string(kr
));
2151 // clean up CFMachPort, mach port rights
2152 CFMachPortInvalidate(sessionPrivate
->mp
);
2153 CFRelease(sessionPrivate
->mp
);
2154 sessionPrivate
->mp
= NULL
;
2155 (void) mach_port_mod_refs(mach_task_self(), *newSession
, MACH_PORT_RIGHT_RECEIVE
, -1);
2156 *newSession
= MACH_PORT_NULL
;
2157 *status
= kSCStatusFailed
;
2161 if (oldNotify
!= MACH_PORT_NULL
) {
2162 SC_log(LOG_NOTICE
, "oldNotify != MACH_PORT_NULL");
2165 // add send right (that will be passed back to the client)
2166 (void) mach_port_insert_right(mach_task_self(),
2169 MACH_MSG_TYPE_MAKE_SEND
);
2172 sessionPrivate
->auditToken
= audit_token
;
2175 // Note: at this time we should be holding ONE send right and
2176 // ONE receive right to the server. The send right is
2177 // moved to the caller.
2180 // start per-session thread
2181 pthread_attr_init(&tattr
);
2182 pthread_attr_setscope(&tattr
, PTHREAD_SCOPE_SYSTEM
);
2183 pthread_attr_setdetachstate(&tattr
, PTHREAD_CREATE_DETACHED
);
2184 // pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
2185 pthread_create(&tid
, &tattr
, newHelper
, (void *)session
);
2186 pthread_attr_destroy(&tattr
);
2188 *status
= kSCStatusOK
;
2193 return KERN_SUCCESS
;
2199 _helperexec(mach_port_t server
,
2201 xmlData_t dataRef
, /* raw XML bytes */
2202 mach_msg_type_number_t dataLen
,
2203 xmlData_t traceRef
, /* raw XML bytes */
2204 mach_msg_type_number_t traceLen
,
2206 xmlDataOut_t
*replyRef
, /* raw XML bytes */
2207 mach_msg_type_number_t
*replyLen
)
2209 CFStringRef backtrace
= NULL
;
2210 CFDataRef data
= NULL
;
2212 CFDataRef reply
= NULL
;
2213 SCHelperSessionRef session
;
2215 *status
= kSCStatusOK
;
2219 if ((dataRef
!= NULL
) && (dataLen
> 0)) {
2220 if (!_SCUnserializeData(&data
, (void *)dataRef
, dataLen
)) {
2221 *status
= SCError();
2225 if ((traceRef
!= NULL
) && (traceLen
> 0)) {
2226 if (!_SCUnserializeString(&backtrace
, NULL
, (void *)traceRef
, traceLen
)) {
2227 *status
= SCError();
2231 if (*status
!= kSCStatusOK
) {
2235 session
= __SCHelperSessionFindWithPort(server
);
2236 if (session
== NULL
) {
2237 *status
= kSCStatusFailed
; /* you must have an open session to play */
2241 i
= findCommand(msgID
);
2243 SC_log(LOG_NOTICE
, "received unknown command : %u", msgID
);
2244 *status
= kSCStatusInvalidArgument
;
2248 SC_log(LOG_INFO
, "%p : processing command \"%s\"%s",
2250 helpers
[i
].commandName
,
2251 (data
!= NULL
) ? " w/data" : "");
2253 if (helpers
[i
].needsAuthorization
&&
2254 !hasAuthorization(session
, helpers
[i
].needsWrite
)) {
2255 SC_log(LOG_INFO
, "%p : command \"%s\" : not authorized",
2257 helpers
[i
].commandName
);
2258 *status
= kSCStatusAccessError
;
2261 if (*status
== kSCStatusOK
) {
2262 if (backtrace
!= NULL
) {
2263 __SCHelperSessionAddBacktrace(session
, backtrace
, helpers
[i
].commandName
);
2265 (*helpers
[i
].func
)(session
, helpers
[i
].info
, data
, status
, &reply
);
2268 if ((*status
!= HELPER_STATUS_NO_REPLY
) || (reply
!= NULL
)) {
2271 SC_log(LOG_INFO
, "%p : sending status %u%s",
2274 (reply
!= NULL
) ? " w/reply" : "");
2276 /* serialize the data */
2277 if (reply
!= NULL
) {
2280 ok
= _SCSerializeData(reply
, (void **)replyRef
, &len
);
2281 *replyLen
= (mach_msg_type_number_t
)len
;
2283 *status
= SCError();
2291 if (data
!= NULL
) CFRelease(data
);
2292 if (backtrace
!= NULL
) CFRelease(backtrace
);
2293 if (reply
!= NULL
) CFRelease(reply
);
2294 return KERN_SUCCESS
;
2299 helperMPCopyDescription(const void *info
)
2301 #pragma unused(info)
2302 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<main SCHelper MP>"));
2307 init_MiG(const char *service_name
, int *n_listeners
)
2309 CFMachPortContext context
= { 0
2313 , helperMPCopyDescription
2317 CFRunLoopSourceRef rls
;
2318 mach_port_t service_port
= MACH_PORT_NULL
;
2320 kr
= bootstrap_check_in(bootstrap_port
, service_name
, &service_port
);
2321 if (kr
!= BOOTSTRAP_SUCCESS
) {
2322 SC_log(LOG_NOTICE
, "bootstrap_check_in() failed: %s",
2323 bootstrap_strerror(kr
));
2327 // add a run loop source to listen for new requests
2328 mp
= _SC_CFMachPortCreateWithPort("SCHelper/server",
2332 rls
= CFMachPortCreateRunLoopSource(NULL
, mp
, 0);
2334 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
2337 *n_listeners
= *n_listeners
+ 1;
2347 static const struct option longopts
[] = {
2348 { "debug", no_argument
, 0, 'd' },
2354 main(int argc
, char **argv
)
2356 Boolean done
= FALSE
;
2358 int gen_reported
= 0;
2360 int n_listeners
= 0;
2361 // extern int optind;
2366 openlog("SCHelper", LOG_CONS
|LOG_PID
, LOG_DAEMON
);
2368 // process any arguments
2369 while ((opt
= getopt_long(argc
, argv
, "d", longopts
, &opti
)) != -1) {
2375 // if (strcmp(longopts[opti].name, "debug") == 1) {
2380 SC_log(LOG_NOTICE
, "ignoring unknown or ambiguous command line option");
2387 if (geteuid() != 0) {
2388 SC_log(LOG_NOTICE
, "%s", strerror(EACCES
));
2392 main_runLoop
= CFRunLoopGetCurrent();
2394 err
= init_MiG("com.apple.SystemConfiguration.helper", &n_listeners
);
2395 if ((err
!= 0) || (n_listeners
== 0)) {
2399 pthread_setname_np("SCHelper main thread");
2401 ret
= pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED
, 0);
2403 SC_log(LOG_ERR
, "pthread_set_qos_class_self_np() failed: %s", strerror(errno
));
2410 rlStatus
= CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 15.0, TRUE
);
2412 pthread_mutex_lock(&sessions_lock
);
2414 if (sessions
!= NULL
) {
2415 if (rlStatus
== kCFRunLoopRunTimedOut
) {
2418 if ((CFSetGetCount(sessions
) == 0) && (sessions_closed
== 0)) {
2419 // if we don't have any open sessions and no
2420 // sessions have recently been closed
2427 gen_current
= sessions_generation
;
2428 sessions_closed
= 0;
2430 if (!done
&& (idle
>= (2 * 60 / 15))) {
2431 if (gen_reported
!= gen_current
) {
2432 FILE *logFile
= NULL
;
2434 SC_log(LOG_INFO
, "active (but IDLE) sessions");
2435 CFSetApplyFunction(sessions
, __SCHelperSessionLog
, (void *)&logFile
);
2436 gen_reported
= gen_current
;
2438 if (logFile
!= NULL
) {
2439 (void) fclose(logFile
);
2445 pthread_mutex_unlock(&sessions_lock
);