2 * Copyright (c) 2005-2013 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
30 #include <bsm/libbsm.h>
31 #include <sys/types.h>
34 //#define DEBUG_MACH_PORT_ALLOCATIONS
36 #include <CoreFoundation/CoreFoundation.h>
37 #include <CoreFoundation/CFRuntime.h>
38 #include <Security/Security.h>
39 #include <Security/SecTask.h>
40 #include <SystemConfiguration/SystemConfiguration.h>
41 #include <SystemConfiguration/SCPrivate.h>
42 #include <SystemConfiguration/SCValidation.h>
44 #include "SCPreferencesInternal.h"
45 #include "SCHelper_client.h"
46 #include "helper_types.h"
48 #include <libproc_internal.h>
52 #pragma mark SCHelper session managment
56 // entitlement used to control write access to a given "prefsID"
58 #define kSCReadEntitlementName CFSTR("com.apple.SystemConfiguration.SCPreferences-read-access")
59 #define kSCWriteEntitlementName CFSTR("com.apple.SystemConfiguration.SCPreferences-write-access")
62 // entitlement used to allow limited [VPN configuration] write access to the "preferences.plist"
64 #define kSCVPNFilterEntitlementName CFSTR("com.apple.networking.vpn.configuration")
66 typedef enum { NO
= 0, YES
, UNKNOWN
} lazyBoolean
;
68 typedef const struct __SCHelperSession
* SCHelperSessionRef
;
72 // base CFType information
79 AuthorizationRef authorization
;
80 Boolean use_entitlement
;
86 // Mach security audit trailer for evaluating credentials
87 audit_token_t auditToken
;
89 // write access entitlement associated with this session
90 lazyBoolean callerReadAccess
;
91 lazyBoolean callerWriteAccess
;
93 // configuration filtering
94 lazyBoolean isSetChange
; // only network "set" changes
95 lazyBoolean isVPNChange
; // only VPN configuration changes
99 SCPreferencesRef prefs
;
102 CFMutableSetRef backtraces
;
104 } SCHelperSessionPrivate
, *SCHelperSessionPrivateRef
;
107 static CFStringRef
__SCHelperSessionCopyDescription (CFTypeRef cf
);
108 static void __SCHelperSessionDeallocate (CFTypeRef cf
);
111 static void __SCHelperSessionLogBacktrace (const void *value
, void *context
);
114 static CFTypeID __kSCHelperSessionTypeID
= _kCFRuntimeNotATypeID
;
115 static Boolean debug
= FALSE
;
116 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
117 static CFRunLoopRef main_runLoop
= NULL
;
118 static CFMutableSetRef sessions
= NULL
;
119 static int sessions_closed
= 0; // count of sessions recently closed
120 static int sessions_generation
= 0;
121 static pthread_mutex_t sessions_lock
= PTHREAD_MUTEX_INITIALIZER
;
125 #pragma mark Helper session management
128 #if !TARGET_OS_IPHONE
130 __SCHelperSessionUseEntitlement(SCHelperSessionRef session
)
132 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
134 return sessionPrivate
->use_entitlement
;
136 #endif //!TARGET_OS_IPHONE
139 static AuthorizationRef
140 __SCHelperSessionGetAuthorization(SCHelperSessionRef session
)
142 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
144 return sessionPrivate
->authorization
;
149 __SCHelperSessionSetAuthorization(SCHelperSessionRef session
, CFTypeRef authorizationData
)
152 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
155 * On OSX, the authorizationData can either be a CFData-wrapped/externalized
156 * "authorization" or a CFString indicating that we should check entitlements.
158 * On iOS, the authorizationData is a CFString indicating that we should
159 * check entitlements.
161 pthread_mutex_lock(&sessionPrivate
->lock
);
163 if (sessionPrivate
->authorization
!= NULL
) {
164 #if !TARGET_OS_IPHONE
165 if (!__SCHelperSessionUseEntitlement(session
)) {
166 AuthorizationFree(sessionPrivate
->authorization
, kAuthorizationFlagDefaults
);
167 // AuthorizationFree(sessionPrivate->authorization, kAuthorizationFlagDestroyRights);
169 CFRelease(sessionPrivate
->authorization
);
171 #else //!TARGET_OS_IPHONE
172 CFRelease(sessionPrivate
->authorization
);
173 #endif //!TARGET_OS_IPHONE
174 sessionPrivate
->authorization
= NULL
;
175 sessionPrivate
->use_entitlement
= FALSE
;
178 #if !TARGET_OS_IPHONE
179 if (isA_CFData(authorizationData
)) {
180 AuthorizationExternalForm extForm
;
182 if (CFDataGetLength(authorizationData
) == sizeof(extForm
.bytes
)) {
185 bcopy(CFDataGetBytePtr(authorizationData
), extForm
.bytes
, sizeof(extForm
.bytes
));
186 err
= AuthorizationCreateFromExternalForm(&extForm
,
187 &sessionPrivate
->authorization
);
188 if (err
!= errAuthorizationSuccess
) {
190 CFSTR("AuthorizationCreateFromExternalForm() failed: status = %d"),
192 sessionPrivate
->authorization
= NULL
;
196 } else if (isA_CFString(authorizationData
)) {
197 sessionPrivate
->authorization
= (void *)CFRetain(authorizationData
);
198 sessionPrivate
->use_entitlement
= TRUE
;
200 #else //!TARGET_OS_IPHONE
201 if (isA_CFString(authorizationData
)) {
202 sessionPrivate
->authorization
= (void *)CFRetain(authorizationData
);
203 sessionPrivate
->use_entitlement
= TRUE
;
205 #endif //!TARGET_OS_IPHONE
207 pthread_mutex_unlock(&sessionPrivate
->lock
);
213 static SCPreferencesRef
214 __SCHelperSessionGetPreferences(SCHelperSessionRef session
)
216 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
218 return sessionPrivate
->prefs
;
223 __SCHelperSessionSetThreadName(SCHelperSessionRef session
)
229 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
232 if (sessionPrivate
->mp
== NULL
) {
236 if (sessionPrivate
->prefs
!= NULL
) {
237 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
239 if (prefsPrivate
->name
!= NULL
) {
240 caller
= _SC_cfstring_to_cstring(prefsPrivate
->name
,
243 kCFStringEncodingUTF8
);
246 path
= (prefsPrivate
->newPath
!= NULL
) ? prefsPrivate
->newPath
: prefsPrivate
->path
;
248 path_s
= strrchr(path
, '/');
249 if (path_s
!= NULL
) {
255 if (caller
!= NULL
) {
256 snprintf(name
, sizeof(name
), "SESSION|%p|%s|%s%s",
257 (void *)(uintptr_t)CFMachPortGetPort(sessionPrivate
->mp
),
258 (caller
!= NULL
) ? caller
: "?",
259 (path_s
!= NULL
) ? "*/" : "",
260 (path
!= NULL
) ? path
: "?");
261 CFAllocatorDeallocate(NULL
, caller
);
263 snprintf(name
, sizeof(name
), "SESSION|%p",
264 (void *)(uintptr_t)CFMachPortGetPort(sessionPrivate
->mp
));
267 pthread_setname_np(name
);
274 __SCHelperSessionSetPreferences(SCHelperSessionRef session
, SCPreferencesRef prefs
)
276 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
278 pthread_mutex_lock(&sessionPrivate
->lock
);
283 if (sessionPrivate
->prefs
!= NULL
) {
284 SCLog(debug
, LOG_DEBUG
,
287 CFRelease(sessionPrivate
->prefs
);
290 SCLog(debug
, LOG_DEBUG
,
291 CFSTR("%p : open, prefs = %@"),
295 sessionPrivate
->prefs
= prefs
;
297 __SCHelperSessionSetThreadName(session
);
299 pthread_mutex_unlock(&sessionPrivate
->lock
);
306 __SCHelperSessionSetNetworkSetFilter(SCHelperSessionRef session
, Boolean setChange
)
308 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
310 pthread_mutex_lock(&sessionPrivate
->lock
);
312 sessionPrivate
->isSetChange
= setChange
? YES
: NO
;
314 pthread_mutex_unlock(&sessionPrivate
->lock
);
321 __SCHelperSessionUseNetworkSetFilter(SCHelperSessionRef session
)
323 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
325 return (sessionPrivate
->isSetChange
== YES
) ? TRUE
: FALSE
;
330 __SCHelperSessionSetVPNFilter(SCHelperSessionRef session
, Boolean vpnChange
, CFArrayRef vpnTypes
)
332 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
334 pthread_mutex_lock(&sessionPrivate
->lock
);
336 sessionPrivate
->isVPNChange
= vpnChange
? YES
: NO
;
338 if (vpnTypes
!= NULL
) {
341 if (sessionPrivate
->vpnTypes
!= NULL
) {
342 CFRelease(sessionPrivate
->vpnTypes
);
344 sessionPrivate
->vpnTypes
= vpnTypes
;
346 pthread_mutex_unlock(&sessionPrivate
->lock
);
353 __SCHelperSessionUseVPNFilter(SCHelperSessionRef session
, CFArrayRef
*vpnTypes
)
355 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
357 *vpnTypes
= sessionPrivate
->vpnTypes
;
358 return (sessionPrivate
->vpnTypes
!= NULL
) ? TRUE
: FALSE
;
363 __SCHelperSessionLog(const void *value
, void *context
)
365 SCHelperSessionRef session
= (SCHelperSessionRef
)value
;
366 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
367 FILE **logFile
= (FILE **)context
;
369 pthread_mutex_lock(&sessionPrivate
->lock
);
371 if ((sessionPrivate
->mp
!= NULL
) && (sessionPrivate
->prefs
!= NULL
)) {
372 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
374 SCLog(TRUE
, LOG_NOTICE
,
375 CFSTR(" %p {port = %p, caller = %@, path = %s%s}"),
377 CFMachPortGetPort(sessionPrivate
->mp
),
379 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
,
380 prefsPrivate
->locked
? ", locked" : "");
382 if ((sessionPrivate
->backtraces
!= NULL
) &&
383 (CFSetGetCount(sessionPrivate
->backtraces
) > 0)) {
384 // log/report all collected backtraces
385 CFSetApplyFunction(sessionPrivate
->backtraces
,
386 __SCHelperSessionLogBacktrace
,
389 // to ensure that we don't log the same backtraces multiple
390 // times we remove any reported traces
391 CFRelease(sessionPrivate
->backtraces
);
392 sessionPrivate
->backtraces
= NULL
;
396 pthread_mutex_unlock(&sessionPrivate
->lock
);
405 static const CFRuntimeClass __SCHelperSessionClass
= {
407 "SCHelperSession", // className
410 __SCHelperSessionDeallocate
, // dealloc
413 NULL
, // copyFormattingDesc
414 __SCHelperSessionCopyDescription
// copyDebugDesc
419 __SCHelperSessionCopyDescription(CFTypeRef cf
)
421 CFAllocatorRef allocator
= CFGetAllocator(cf
);
422 CFMutableStringRef result
;
423 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)cf
;
425 pthread_mutex_lock(&sessionPrivate
->lock
);
427 result
= CFStringCreateMutable(allocator
, 0);
428 CFStringAppendFormat(result
, NULL
, CFSTR("<SCHelperSession %p [%p]> {"), cf
, allocator
);
429 CFStringAppendFormat(result
, NULL
, CFSTR("authorization = %p"), sessionPrivate
->authorization
);
430 if (sessionPrivate
->mp
!= NULL
) {
431 CFStringAppendFormat(result
, NULL
,
432 CFSTR(", mp = %p (port = 0x%x)"),
434 CFMachPortGetPort(sessionPrivate
->mp
));
436 if (sessionPrivate
->prefs
!= NULL
) {
437 CFStringAppendFormat(result
, NULL
, CFSTR(", prefs = %@"), sessionPrivate
->prefs
);
439 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
441 pthread_mutex_unlock(&sessionPrivate
->lock
);
448 __SCHelperSessionDeallocate(CFTypeRef cf
)
450 SCHelperSessionRef session
= (SCHelperSessionRef
)cf
;
451 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
453 // we're releasing "a" session so take the global lock...
454 pthread_mutex_lock(&sessions_lock
);
457 __SCHelperSessionSetAuthorization(session
, NULL
);
458 __SCHelperSessionSetPreferences(session
, NULL
);
459 __SCHelperSessionSetNetworkSetFilter(session
, FALSE
);
460 __SCHelperSessionSetVPNFilter(session
, FALSE
, NULL
);
461 pthread_mutex_destroy(&sessionPrivate
->lock
);
462 if (sessionPrivate
->backtraces
!= NULL
) {
463 CFRelease(sessionPrivate
->backtraces
);
466 // we no longer need/want to track this session
467 CFSetRemoveValue(sessions
, sessionPrivate
);
468 sessions_generation
++;
471 // release the global lock, wake up the main runloop, all done
472 pthread_mutex_unlock(&sessions_lock
);
473 CFRunLoopWakeUp(main_runLoop
);
480 __SCHelperSessionInitialize(void)
482 __kSCHelperSessionTypeID
= _CFRuntimeRegisterClass(&__SCHelperSessionClass
);
487 static SCHelperSessionRef
488 __SCHelperSessionCreate(CFAllocatorRef allocator
)
490 SCHelperSessionPrivateRef sessionPrivate
;
493 // initialize runtime
494 pthread_once(&initialized
, __SCHelperSessionInitialize
);
497 size
= sizeof(SCHelperSessionPrivate
) - sizeof(CFRuntimeBase
);
498 sessionPrivate
= (SCHelperSessionPrivateRef
)_CFRuntimeCreateInstance(allocator
,
499 __kSCHelperSessionTypeID
,
502 if (sessionPrivate
== NULL
) {
506 if (pthread_mutex_init(&sessionPrivate
->lock
, NULL
) != 0) {
507 SCLog(TRUE
, LOG_ERR
, CFSTR("pthread_mutex_init(): failure to initialize per session lock"));
508 CFRelease(sessionPrivate
);
511 sessionPrivate
->authorization
= NULL
;
512 sessionPrivate
->use_entitlement
= FALSE
;
513 sessionPrivate
->port
= MACH_PORT_NULL
;
514 sessionPrivate
->mp
= NULL
;
515 sessionPrivate
->callerReadAccess
= UNKNOWN
;
516 sessionPrivate
->callerWriteAccess
= UNKNOWN
;
517 sessionPrivate
->isSetChange
= UNKNOWN
;
518 sessionPrivate
->isVPNChange
= UNKNOWN
;
519 sessionPrivate
->vpnTypes
= NULL
;
520 sessionPrivate
->prefs
= NULL
;
521 sessionPrivate
->backtraces
= NULL
;
523 // keep track this session
524 pthread_mutex_lock(&sessions_lock
);
525 if (sessions
== NULL
) {
526 const CFSetCallBacks mySetCallBacks
= { 0, NULL
, NULL
, CFCopyDescription
, CFEqual
, CFHash
};
528 // create a non-retaining set
529 sessions
= CFSetCreateMutable(NULL
, 0, &mySetCallBacks
);
531 CFSetAddValue(sessions
, sessionPrivate
);
532 sessions_generation
++;
533 pthread_mutex_unlock(&sessions_lock
);
535 return (SCHelperSessionRef
)sessionPrivate
;
542 static SCHelperSessionRef
543 __SCHelperSessionFindWithPort(mach_port_t port
)
545 SCHelperSessionRef session
= NULL
;
547 // keep track this session
548 pthread_mutex_lock(&sessions_lock
);
549 if (sessions
!= NULL
) {
551 CFIndex n
= CFSetGetCount(sessions
);
552 const void * vals_q
[16];
553 const void ** vals
= vals_q
;
555 if (n
> (CFIndex
)(sizeof(vals_q
) / sizeof(SCHelperSessionRef
)))
556 vals
= CFAllocatorAllocate(NULL
, n
* sizeof(CFStringRef
), 0);
557 CFSetGetValues(sessions
, vals
);
558 for (i
= 0; i
< n
; i
++) {
559 SCHelperSessionPrivateRef sessionPrivate
;
561 sessionPrivate
= (SCHelperSessionPrivateRef
)vals
[i
];
562 if (sessionPrivate
->port
== port
) {
563 session
= (SCHelperSessionRef
)sessionPrivate
;
568 CFAllocatorDeallocate(NULL
, vals
);
570 pthread_mutex_unlock(&sessions_lock
);
577 #pragma mark Session backtrace logging
581 __SCHelperSessionAddBacktrace(SCHelperSessionRef session
, CFStringRef backtrace
, const char * command
)
583 CFStringRef logEntry
;
584 SCPreferencesRef prefs
;
585 SCPreferencesPrivateRef prefsPrivate
;
586 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
588 prefs
= __SCHelperSessionGetPreferences((SCHelperSessionRef
)sessionPrivate
);
593 prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
595 logEntry
= CFStringCreateWithFormat(NULL
, NULL
,
596 CFSTR("%@ [%s]: %s\n\n%@"),
598 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
,
602 pthread_mutex_lock(&sessionPrivate
->lock
);
604 if (sessionPrivate
->backtraces
== NULL
) {
605 sessionPrivate
->backtraces
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
607 CFSetSetValue(sessionPrivate
->backtraces
, logEntry
);
609 pthread_mutex_unlock(&sessionPrivate
->lock
);
617 __SCHelperSessionLogBacktrace(const void *value
, void *context
)
619 CFSetRef backtrace
= (CFSetRef
)value
;
620 FILE **logFile
= (FILE **)context
;
622 if (*logFile
== NULL
) {
625 struct timeval tv_now
;
627 (void)gettimeofday(&tv_now
, NULL
);
628 (void)localtime_r(&tv_now
.tv_sec
, &tm_now
);
632 "/Library/Logs/CrashReporter/SCHelper-%4d-%02d-%02d-%02d%02d%02d.log",
633 tm_now
.tm_year
+ 1900,
640 *logFile
= fopen(path
, "a");
641 if (*logFile
== NULL
) {
642 // if log file could not be created
646 SCLog(TRUE
, LOG_INFO
, CFSTR("created backtrace log: %s"), path
);
649 SCPrint(TRUE
, *logFile
, CFSTR("%@\n"), backtrace
);
661 * (out) status = SCError()
665 do_Exit(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
674 * (in) data = authorizationDict (in 2 flavors)
675 * kSCHelperAuthAuthorization - use provided AuthorizationExternalForm
676 * kSCHelperAuthCallerInfo - use entitlement
677 * (out) status = OSStatus
681 do_Auth(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
683 CFDictionaryRef authorizationDict
;
684 #if !TARGET_OS_IPHONE
685 CFDataRef authorizationData
= NULL
;
689 if (_SCUnserialize((CFPropertyListRef
*)&authorizationDict
, data
, NULL
, 0) == FALSE
) {
693 if (authorizationDict
== NULL
) {
697 if (!isA_CFDictionary(authorizationDict
)) {
698 CFRelease(authorizationDict
);
702 #if !TARGET_OS_IPHONE
703 authorizationData
= CFDictionaryGetValue(authorizationDict
, kSCHelperAuthAuthorization
);
704 if (authorizationData
!= NULL
&& isA_CFData(authorizationData
)) {
705 ok
= __SCHelperSessionSetAuthorization(session
, authorizationData
);
709 CFStringRef authorizationInfo
;
711 authorizationInfo
= CFDictionaryGetValue(authorizationDict
, kSCHelperAuthCallerInfo
);
712 if (authorizationInfo
!= NULL
&& isA_CFString(authorizationInfo
)) {
713 ok
= __SCHelperSessionSetAuthorization(session
, authorizationInfo
);
717 CFRelease(authorizationDict
);
718 *status
= ok
? 0 : 1;
723 #if !TARGET_OS_IPHONE
727 * SCHELPER_MSG_KEYCHAIN_COPY
728 * (in) data = unique_id
729 * (out) status = SCError()
730 * (out) reply = password
733 do_keychain_copy(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
736 SCPreferencesRef prefs
;
737 CFStringRef unique_id
= NULL
;
739 if ((data
!= NULL
) && !_SCUnserializeString(&unique_id
, data
, NULL
, 0)) {
743 if (unique_id
!= NULL
) {
744 if (isA_CFString(unique_id
)) {
745 prefs
= __SCHelperSessionGetPreferences(session
);
746 *reply
= _SCPreferencesSystemKeychainPasswordItemCopy(prefs
, unique_id
);
747 if (*reply
== NULL
) {
753 CFRelease(unique_id
);
761 * SCHELPER_MSG_KEYCHAIN_EXISTS
762 * (in) data = unique_id
763 * (out) status = SCError()
767 do_keychain_exists(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
770 SCPreferencesRef prefs
;
771 CFStringRef unique_id
= NULL
;
773 if ((data
!= NULL
) && !_SCUnserializeString(&unique_id
, data
, NULL
, 0)) {
777 if (unique_id
!= NULL
) {
778 if (isA_CFString(unique_id
)) {
779 prefs
= __SCHelperSessionGetPreferences(session
);
780 ok
= _SCPreferencesSystemKeychainPasswordItemExists(prefs
, unique_id
);
786 CFRelease(unique_id
);
794 * SCHELPER_MSG_KEYCHAIN_REMOVE
795 * (in) data = unique_id
796 * (out) status = SCError()
800 do_keychain_remove(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
803 SCPreferencesRef prefs
;
804 CFStringRef unique_id
= NULL
;
806 if ((data
!= NULL
) && !_SCUnserializeString(&unique_id
, data
, NULL
, 0)) {
810 if (unique_id
!= NULL
) {
811 if (isA_CFString(unique_id
)) {
812 prefs
= __SCHelperSessionGetPreferences(session
);
813 ok
= _SCPreferencesSystemKeychainPasswordItemRemove(prefs
, unique_id
);
819 CFRelease(unique_id
);
827 * SCHELPER_MSG_KEYCHAIN_SET
828 * (in) data = options dictionary
829 * (out) status = SCError()
833 do_keychain_set(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
836 CFStringRef description
;
837 CFArrayRef executablePaths
= NULL
;
840 CFDictionaryRef options
= NULL
;
842 SCPreferencesRef prefs
;
843 CFStringRef unique_id
;
845 if ((data
!= NULL
) && !_SCUnserialize((CFPropertyListRef
*)&options
, data
, NULL
, 0)) {
849 if (options
!= NULL
) {
850 if (!isA_CFDictionary(options
)) {
858 if (CFDictionaryGetValueIfPresent(options
,
859 kSCKeychainOptionsAllowedExecutables
,
860 (const void **)&executablePaths
)) {
861 CFMutableArrayRef executableURLs
;
864 CFMutableDictionaryRef newOptions
;
866 executableURLs
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
867 n
= CFArrayGetCount(executablePaths
);
868 for (i
= 0; i
< n
; i
++) {
872 path
= CFArrayGetValueAtIndex(executablePaths
, i
);
873 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
874 CFDataGetBytePtr(path
),
875 CFDataGetLength(path
),
878 CFArrayAppendValue(executableURLs
, url
);
883 newOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, options
);
884 CFDictionarySetValue(newOptions
, kSCKeychainOptionsAllowedExecutables
, executableURLs
);
885 CFRelease(executableURLs
);
888 options
= newOptions
;
891 unique_id
= CFDictionaryGetValue(options
, kSCKeychainOptionsUniqueID
);
892 label
= CFDictionaryGetValue(options
, kSCKeychainOptionsLabel
);
893 description
= CFDictionaryGetValue(options
, kSCKeychainOptionsDescription
);
894 account
= CFDictionaryGetValue(options
, kSCKeychainOptionsAccount
);
895 password
= CFDictionaryGetValue(options
, kSCKeychainOptionsPassword
);
897 prefs
= __SCHelperSessionGetPreferences(session
);
898 ok
= _SCPreferencesSystemKeychainPasswordItemSet(prefs
,
914 #endif // !TARGET_OS_IPHONE
918 * SCHELPER_MSG_INTERFACE_REFRESH
920 * (out) status = SCError()
924 do_interface_refresh(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
926 CFStringRef ifName
= NULL
;
929 if ((data
!= NULL
) && !_SCUnserializeString(&ifName
, data
, NULL
, 0)) {
930 *status
= kSCStatusInvalidArgument
;
931 SCLog(TRUE
, LOG_ERR
, CFSTR("interface name not valid"));
935 if (ifName
== NULL
) {
936 *status
= kSCStatusInvalidArgument
;
937 SCLog(TRUE
, LOG_ERR
, CFSTR("interface name not valid"));
941 if (isA_CFString(ifName
)) {
942 ok
= _SCNetworkInterfaceForceConfigurationRefresh(ifName
);
946 CFSTR("interface \"%@\" not refreshed: %s"),
948 SCErrorString(*status
));
951 *status
= kSCStatusInvalidArgument
;
952 SCLog(TRUE
, LOG_ERR
, CFSTR("interface name not valid"));
963 * (in) data = prefsID
964 * (out) status = SCError()
968 do_prefs_Open(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
971 CFDictionaryRef options
;
973 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
974 CFDictionaryRef prefsInfo
= NULL
;
976 CFStringRef prefsName
;
977 CFStringRef proc_name
;
983 if ((data
!= NULL
) && !_SCUnserialize((CFPropertyListRef
*)&prefsInfo
, data
, NULL
, 0)) {
984 SCLog(TRUE
, LOG_ERR
, CFSTR("data not valid, %@"), data
);
988 if ((prefsInfo
== NULL
) || !isA_CFDictionary(prefsInfo
)) {
989 SCLog(TRUE
, LOG_ERR
, CFSTR("info not valid"));
990 if (prefsInfo
!= NULL
) CFRelease(prefsInfo
);
994 // get [optional] prefsID
995 prefsID
= CFDictionaryGetValue(prefsInfo
, CFSTR("prefsID"));
996 prefsID
= isA_CFString(prefsID
);
997 if (prefsID
!= NULL
) {
998 if (CFStringHasPrefix(prefsID
, CFSTR("/")) ||
999 CFStringHasPrefix(prefsID
, CFSTR("../")) ||
1000 CFStringHasSuffix(prefsID
, CFSTR("/..")) ||
1001 (CFStringFind(prefsID
, CFSTR("/../"), 0).location
!= kCFNotFound
)) {
1002 // if we're trying to escape from the preferences directory
1003 SCLog(TRUE
, LOG_ERR
, CFSTR("prefsID (%@) not valid"), prefsID
);
1004 CFRelease(prefsInfo
);
1005 *status
= kSCStatusInvalidArgument
;
1010 // get [optional] options
1011 options
= CFDictionaryGetValue(prefsInfo
, CFSTR("options"));
1012 options
= isA_CFDictionary(options
);
1014 // get preferences session "name"
1015 name
= CFDictionaryGetValue(prefsInfo
, CFSTR("name"));
1016 if (!isA_CFString(name
)) {
1017 SCLog(TRUE
, LOG_ERR
, CFSTR("session \"name\" not valid"));
1018 CFRelease(prefsInfo
);
1022 // get PID of caller
1023 pid
= CFDictionaryGetValue(prefsInfo
, CFSTR("PID"));
1024 if (!isA_CFNumber(pid
)) {
1025 SCLog(TRUE
, LOG_ERR
, CFSTR("PID not valid"));
1026 CFRelease(prefsInfo
);
1030 // get process name of caller
1031 proc_name
= CFDictionaryGetValue(prefsInfo
, CFSTR("PROC_NAME"));
1032 if (!isA_CFString(proc_name
)) {
1033 SCLog(TRUE
, LOG_ERR
, CFSTR("process name not valid"));
1034 CFRelease(prefsInfo
);
1038 // build [helper] preferences "name" (used for debugging) and estabish
1039 // a preferences session.
1040 prefsName
= CFStringCreateWithFormat(NULL
, NULL
,
1046 prefs
= SCPreferencesCreateWithOptions(NULL
, prefsName
, prefsID
, NULL
, options
);
1047 CFRelease(prefsName
);
1048 CFRelease(prefsInfo
);
1050 __SCHelperSessionSetPreferences(session
, prefs
);
1052 if (prefs
!= NULL
) {
1055 CFRunLoopRef rl
= CFRunLoopGetCurrent();
1057 // [temporarily] schedule notifications to ensure that we can use
1058 // the SCDynamicStore to track helper sessions
1059 ok
= SCPreferencesScheduleWithRunLoop(prefs
, rl
, kCFRunLoopDefaultMode
);
1061 (void)SCPreferencesUnscheduleFromRunLoop(prefs
, rl
, kCFRunLoopDefaultMode
);
1065 // the session now holds a references to the SCPreferencesRef
1068 *status
= SCError();
1078 * (out) status = SCError()
1079 * (out) reply = current signature + current preferences
1082 do_prefs_Access(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1085 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1086 CFDataRef signature
;
1088 if (prefs
== NULL
) {
1092 signature
= SCPreferencesGetSignature(prefs
);
1093 if (signature
!= NULL
) {
1094 const void * dictKeys
[2];
1095 const void * dictVals
[2];
1096 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1097 CFDictionaryRef replyDict
;
1099 dictKeys
[0] = CFSTR("signature");
1100 dictVals
[0] = signature
;
1102 dictKeys
[1] = CFSTR("preferences");
1103 dictVals
[1] = prefsPrivate
->prefs
;
1105 replyDict
= CFDictionaryCreate(NULL
,
1106 (const void **)&dictKeys
,
1107 (const void **)&dictVals
,
1108 sizeof(dictKeys
)/sizeof(dictKeys
[0]),
1109 &kCFTypeDictionaryKeyCallBacks
,
1110 &kCFTypeDictionaryValueCallBacks
);
1112 ok
= _SCSerialize(replyDict
, reply
, NULL
, NULL
);
1113 CFRelease(replyDict
);
1118 *status
= SCError();
1127 * (in) data = client prefs signature (NULL if check not needed)
1128 * (out) status = SCError()
1132 do_prefs_Lock(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1134 CFDataRef clientSignature
= (CFDataRef
)data
;
1136 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1137 Boolean wait
= (info
== (void *)FALSE
) ? FALSE
: TRUE
;
1139 if (prefs
== NULL
) {
1143 ok
= SCPreferencesLock(prefs
, wait
);
1145 *status
= SCError();
1149 if (clientSignature
!= NULL
) {
1150 CFDataRef serverSignature
;
1152 serverSignature
= SCPreferencesGetSignature(prefs
);
1153 if (!CFEqual(clientSignature
, serverSignature
)) {
1154 (void)SCPreferencesUnlock(prefs
);
1155 *status
= kSCStatusStale
;
1165 * (in) data = new preferences (NULL if commit w/no changes)
1166 * (out) status = SCError()
1167 * (out) reply = new signature
1170 do_prefs_Commit(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1173 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1174 CFPropertyListRef prefsData
= NULL
;
1175 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1176 Boolean useSetFilter
;
1177 Boolean useVPNFilter
;
1178 CFArrayRef vpnTypes
= NULL
;
1180 if (prefs
== NULL
) {
1185 // if commit with no changes
1189 ok
= _SCUnserialize(&prefsData
, data
, NULL
, 0);
1194 if (!isA_CFDictionary(prefsData
)) {
1195 *status
= kSCStatusFailed
;
1200 useSetFilter
= __SCHelperSessionUseNetworkSetFilter(session
);
1201 useVPNFilter
= __SCHelperSessionUseVPNFilter(session
, &vpnTypes
);
1202 if (useSetFilter
|| useVPNFilter
) {
1205 if (prefsPrivate
->prefs
!= NULL
) {
1207 CFMutableDictionaryRef prefsNew
= NULL
;
1208 CFMutableDictionaryRef prefsOld
= NULL
;
1209 CFMutableDictionaryRef prefsSave
= prefsPrivate
->prefs
;
1211 for (c
= 0; c
< 2; c
++) {
1215 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, prefsSave
);
1218 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, prefsData
);
1223 // filter out current network set selection
1224 (void) SCPreferencesRemoveValue(prefs
, kSCPrefCurrentSet
);
1225 } else if (useVPNFilter
) {
1226 CFRange range
= CFRangeMake(0, CFArrayGetCount(vpnTypes
));
1227 CFArrayRef services
;
1229 // filter out VPN services of the specified type
1230 services
= SCNetworkServiceCopyAll(prefs
);
1231 if (services
!= NULL
) {
1233 CFIndex n
= CFArrayGetCount(services
);
1235 for (i
= 0; i
< n
; i
++) {
1236 SCNetworkServiceRef service
;
1238 service
= CFArrayGetValueAtIndex(services
, i
);
1239 if (_SCNetworkServiceIsVPN(service
)) {
1240 SCNetworkInterfaceRef child
;
1241 CFStringRef childType
= NULL
;
1242 SCNetworkInterfaceRef interface
;
1243 CFStringRef interfaceType
;
1245 interface
= SCNetworkServiceGetInterface(service
);
1246 interfaceType
= SCNetworkInterfaceGetInterfaceType(interface
);
1247 child
= SCNetworkInterfaceGetInterface(interface
);
1248 if (child
!= NULL
) {
1249 childType
= SCNetworkInterfaceGetInterfaceType(child
);
1251 if (CFEqual(interfaceType
, kSCNetworkInterfaceTypeVPN
) &&
1252 (childType
!= NULL
) &&
1253 CFArrayContainsValue(vpnTypes
, range
, childType
)) {
1254 // filter out VPN service
1255 (void) SCNetworkServiceRemove(service
);
1257 // mark all other VPN services "enabled"
1258 (void) SCNetworkServiceSetEnabled(service
, TRUE
);
1263 CFRelease(services
);
1269 prefsOld
= prefsPrivate
->prefs
;
1272 prefsNew
= prefsPrivate
->prefs
;
1277 // compare the filtered configurations
1278 ok
= _SC_CFEqual(prefsOld
, prefsNew
);
1281 if (prefsOld
!= NULL
) CFRelease(prefsOld
);
1282 if (prefsNew
!= NULL
) CFRelease(prefsNew
);
1283 prefsPrivate
->prefs
= prefsSave
;
1287 *status
= kSCStatusAccessError
;
1292 if (prefsPrivate
->prefs
!= NULL
) {
1293 CFRelease(prefsPrivate
->prefs
);
1295 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, prefsData
);
1296 prefsPrivate
->accessed
= TRUE
;
1297 prefsPrivate
->changed
= TRUE
;
1301 ok
= SCPreferencesCommitChanges(prefs
);
1303 *reply
= SCPreferencesGetSignature(prefs
);
1306 *status
= SCError();
1311 if (prefsData
!= NULL
) CFRelease(prefsData
);
1319 * (out) status = SCError()
1323 do_prefs_Apply(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1326 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1328 if (prefs
== NULL
) {
1332 ok
= SCPreferencesApplyChanges(prefs
);
1334 *status
= SCError();
1344 * (out) status = SCError()
1348 do_prefs_Unlock(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1351 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1353 if (prefs
== NULL
) {
1357 ok
= SCPreferencesUnlock(prefs
);
1359 *status
= SCError();
1369 * (out) status = SCError()
1373 do_prefs_Close(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1375 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1377 if (prefs
== NULL
) {
1381 __SCHelperSessionSetPreferences(session
, NULL
);
1390 * (out) status = kSCStatusOK
1394 do_prefs_Synchronize(SCHelperSessionRef session
, void *info
, CFDataRef data
, uint32_t *status
, CFDataRef
*reply
)
1396 SCPreferencesRef prefs
= __SCHelperSessionGetPreferences(session
);
1398 if (prefs
== NULL
) {
1402 SCPreferencesSynchronize(prefs
);
1403 *status
= kSCStatusOK
;
1409 #pragma mark Process commands
1413 sessionName(SCHelperSessionRef session
)
1415 CFStringRef name
= NULL
;
1416 SCPreferencesRef prefs
;
1418 prefs
= __SCHelperSessionGetPreferences(session
);
1419 if (prefs
!= NULL
) {
1420 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
1422 name
= prefsPrivate
->name
;
1425 return (name
!= NULL
) ? name
: CFSTR("???");
1430 sessionPrefsID(SCHelperSessionRef session
)
1432 CFStringRef prefsID
;
1433 SCPreferencesPrivateRef prefsPrivate
;
1434 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1436 prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
1437 if ((prefsPrivate
!= NULL
) && (prefsPrivate
->prefsID
!= NULL
)) {
1438 prefsID
= prefsPrivate
->prefsID
;
1440 prefsID
= PREFS_DEFAULT_CONFIG
;
1448 copyEntitlement(SCHelperSessionRef session
, CFStringRef entitlement
)
1450 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1452 CFTypeRef value
= NULL
;
1454 // Create the security task from the audit token
1455 task
= SecTaskCreateWithAuditToken(NULL
, sessionPrivate
->auditToken
);
1457 CFErrorRef error
= NULL
;
1459 // Get the value for the entitlement
1460 value
= SecTaskCopyValueForEntitlement(task
, entitlement
, &error
);
1461 if ((value
== NULL
) && (error
!= NULL
)) {
1462 CFIndex code
= CFErrorGetCode(error
);
1463 CFStringRef domain
= CFErrorGetDomain(error
);
1465 if (!CFEqual(domain
, kCFErrorDomainMach
) ||
1466 ((code
!= kIOReturnInvalid
) && (code
!= kIOReturnNotFound
))) {
1467 // if unexpected error
1468 SCLog(TRUE
, LOG_ERR
,
1469 CFSTR("SecTaskCopyValueForEntitlement(,\"%@\",) failed, error = %@ : %@"),
1472 sessionName(session
));
1479 SCLog(TRUE
, LOG_ERR
,
1480 CFSTR("SecTaskCreateWithAuditToken() failed: %@"),
1481 sessionName(session
));
1488 #if !TARGET_OS_IPHONE
1490 isSetChange(SCHelperSessionRef session
)
1492 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1494 if (sessionPrivate
->isSetChange
== UNKNOWN
) {
1495 CFBooleanRef bVal
= NULL
;
1496 CFStringRef prefsID
;
1497 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)sessionPrivate
->prefs
;
1498 Boolean setFilter
= FALSE
;
1500 prefsID
= sessionPrefsID(session
);
1501 if (CFEqual(prefsID
, PREFS_DEFAULT_CONFIG
) &&
1502 isA_CFDictionary(prefsPrivate
->options
) &&
1503 CFDictionaryGetValueIfPresent(prefsPrivate
->options
,
1504 kSCPreferencesOptionChangeNetworkSet
,
1505 (const void **)&bVal
) &&
1506 isA_CFBoolean(bVal
) &&
1507 CFBooleanGetValue(bVal
)) {
1511 // establish network set (location) filter
1512 __SCHelperSessionSetNetworkSetFilter(session
, setFilter
);
1515 return (sessionPrivate
->isSetChange
== YES
) ? TRUE
: FALSE
;
1517 #endif // !TARGET_OS_IPHONE
1521 isVPNChange(SCHelperSessionRef session
)
1523 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1525 if (sessionPrivate
->isVPNChange
== UNKNOWN
) {
1526 CFArrayRef entitlement
;
1527 Boolean vpnChange
= FALSE
;
1528 CFArrayRef vpnTypes
= NULL
;
1530 entitlement
= copyEntitlement(session
, kSCVPNFilterEntitlementName
);
1531 if (entitlement
!= NULL
) {
1532 if (isA_CFArray(entitlement
)) {
1533 CFStringRef prefsID
;
1535 prefsID
= sessionPrefsID(session
);
1536 if (CFEqual(prefsID
, PREFS_DEFAULT_CONFIG
)) {
1537 // save the VPN type identifiers
1538 vpnTypes
= CFRetain(entitlement
);
1540 // grant an exception
1542 } else if (CFStringHasPrefix(prefsID
, CFSTR("VPN-")) &&
1543 CFStringHasSuffix(prefsID
, CFSTR(".plist"))) {
1547 range
.location
= sizeof("VPN-") - 1;
1548 range
.length
= CFStringGetLength(prefsID
)
1549 - (sizeof("VPN-") - 1) // trim VPN-
1550 - (sizeof(".plist") - 1); // trim .plist
1551 vpnID
= CFStringCreateWithSubstring(NULL
, prefsID
, range
);
1552 if (CFArrayContainsValue(entitlement
,
1553 CFRangeMake(0, CFArrayGetCount(entitlement
)),
1555 // grant an exception
1562 CFRelease(entitlement
);
1565 __SCHelperSessionSetVPNFilter(session
, vpnChange
, vpnTypes
);
1566 if (vpnTypes
!= NULL
) {
1567 CFRelease(vpnTypes
);
1571 return (sessionPrivate
->isVPNChange
== YES
) ? TRUE
: FALSE
;
1576 checkEntitlement(SCHelperSessionRef session
, CFStringRef prefsID
, CFStringRef entitlement_name
)
1578 CFArrayRef entitlement
;
1579 Boolean hasEntitlement
= FALSE
;
1581 entitlement
= copyEntitlement(session
, entitlement_name
);
1582 if (entitlement
!= NULL
) {
1583 if (isA_CFArray(entitlement
)) {
1584 if (CFArrayContainsValue(entitlement
,
1585 CFRangeMake(0, CFArrayGetCount(entitlement
)),
1587 // if client DOES have entitlement
1588 hasEntitlement
= TRUE
;
1591 SCLog(TRUE
, LOG_ERR
,
1592 CFSTR("hasAuthorization() entitlement=%@: not valid"),
1594 sessionName(session
));
1597 CFRelease(entitlement
);
1600 #if TARGET_OS_IPHONE
1601 // make an exception for VPN configuration management
1602 if (!hasEntitlement
) {
1603 if (isVPNChange(session
)) {
1604 // grant a "filtered" exception
1605 hasEntitlement
= TRUE
;
1608 #endif // TARGET_OS_IPHONE
1610 return hasEntitlement
;
1615 hasAuthorization(SCHelperSessionRef session
, Boolean needWrite
)
1617 AuthorizationRef authorization
= __SCHelperSessionGetAuthorization(session
);
1618 CFStringRef prefsID
;
1619 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1621 if (authorization
== NULL
) {
1625 #if !TARGET_OS_IPHONE
1626 if (!__SCHelperSessionUseEntitlement(session
)) {
1627 AuthorizationFlags flags
;
1628 AuthorizationItem items
[1];
1629 AuthorizationRights rights
;
1632 if (isSetChange(session
)) {
1633 items
[0].name
= kSCPreferencesAuthorizationRight_network_set
;
1634 items
[0].value
= NULL
;
1635 items
[0].valueLength
= 0;
1637 } else if (isVPNChange(session
)) {
1638 items
[0].name
= kSCPreferencesAuthorizationRight_write
;
1639 items
[0].value
= NULL
;
1640 items
[0].valueLength
= 0;
1643 items
[0].name
= kSCPreferencesAuthorizationRight_write
;
1644 items
[0].value
= NULL
;
1645 items
[0].valueLength
= 0;
1649 rights
.count
= sizeof(items
) / sizeof(items
[0]);
1650 rights
.items
= items
;
1652 flags
= kAuthorizationFlagDefaults
;
1653 flags
|= kAuthorizationFlagInteractionAllowed
;
1654 flags
|= kAuthorizationFlagExtendRights
;
1655 // flags |= kAuthorizationFlagPartialRights;
1656 // flags |= kAuthorizationFlagPreAuthorize;
1658 status
= AuthorizationCopyRights(authorization
,
1660 kAuthorizationEmptyEnvironment
,
1663 if (status
!= errAuthorizationSuccess
) {
1669 #endif // !TARGET_OS_IPHONE
1671 prefsID
= sessionPrefsID(session
);
1673 if (sessionPrivate
->callerWriteAccess
== UNKNOWN
) {
1674 if (checkEntitlement(session
, prefsID
, kSCWriteEntitlementName
)) {
1675 sessionPrivate
->callerWriteAccess
= YES
;
1676 sessionPrivate
->callerReadAccess
= YES
; // implied
1678 sessionPrivate
->callerWriteAccess
= NO
;
1683 if (sessionPrivate
->callerWriteAccess
== YES
) {
1686 SCLog(TRUE
, LOG_ERR
,
1687 CFSTR("SCPreferences write access to \"%@\" denied, no entitlement for \"%@\""),
1689 sessionName(session
));
1694 if (sessionPrivate
->callerReadAccess
== UNKNOWN
) {
1695 if (checkEntitlement(session
, prefsID
, kSCReadEntitlementName
)) {
1696 sessionPrivate
->callerReadAccess
= YES
;
1698 sessionPrivate
->callerWriteAccess
= NO
;
1702 if (sessionPrivate
->callerReadAccess
== YES
) {
1706 SCLog(TRUE
, LOG_ERR
,
1707 CFSTR("SCPreferences access to \"%@\" denied, no entitlement for \"%@\""),
1709 sessionName(session
));
1714 typedef Boolean (*helperFunction
) (SCHelperSessionRef session
,
1721 static const struct helper
{
1723 const char *commandName
;
1724 Boolean needsAuthorization
;
1726 helperFunction func
;
1729 { SCHELPER_MSG_AUTH
, "AUTH", FALSE
, FALSE
, do_Auth
, NULL
},
1731 { SCHELPER_MSG_PREFS_OPEN
, "PREFS open", FALSE
, FALSE
, do_prefs_Open
, NULL
},
1732 { SCHELPER_MSG_PREFS_ACCESS
, "PREFS access", TRUE
, FALSE
, do_prefs_Access
, NULL
},
1733 { SCHELPER_MSG_PREFS_LOCK
, "PREFS lock", TRUE
, TRUE
, do_prefs_Lock
, (void *)FALSE
},
1734 { SCHELPER_MSG_PREFS_LOCKWAIT
, "PREFS lock/wait", TRUE
, TRUE
, do_prefs_Lock
, (void *)TRUE
},
1735 { SCHELPER_MSG_PREFS_COMMIT
, "PREFS commit", TRUE
, TRUE
, do_prefs_Commit
, NULL
},
1736 { SCHELPER_MSG_PREFS_APPLY
, "PREFS apply", TRUE
, TRUE
, do_prefs_Apply
, NULL
},
1737 { SCHELPER_MSG_PREFS_UNLOCK
, "PREFS unlock", FALSE
, TRUE
, do_prefs_Unlock
, NULL
},
1738 { SCHELPER_MSG_PREFS_CLOSE
, "PREFS close", FALSE
, FALSE
, do_prefs_Close
, NULL
},
1739 { SCHELPER_MSG_PREFS_SYNCHRONIZE
, "PREFS synchronize", FALSE
, FALSE
, do_prefs_Synchronize
, NULL
},
1741 { SCHELPER_MSG_INTERFACE_REFRESH
, "INTERFACE refresh", TRUE
, TRUE
, do_interface_refresh
, NULL
},
1743 #if !TARGET_OS_IPHONE
1744 { SCHELPER_MSG_KEYCHAIN_COPY
, "KEYCHAIN copy", TRUE
, FALSE
, do_keychain_copy
, NULL
},
1745 { SCHELPER_MSG_KEYCHAIN_EXISTS
, "KEYCHAIN exists", TRUE
, FALSE
, do_keychain_exists
, NULL
},
1746 { SCHELPER_MSG_KEYCHAIN_REMOVE
, "KEYCHAIN remove", TRUE
, TRUE
, do_keychain_remove
, NULL
},
1747 { SCHELPER_MSG_KEYCHAIN_SET
, "KEYCHAIN set", TRUE
, TRUE
, do_keychain_set
, NULL
},
1748 #endif // !TARGET_OS_IPHONE
1750 { SCHELPER_MSG_EXIT
, "EXIT", FALSE
, FALSE
, do_Exit
, NULL
}
1752 #define nHELPERS (sizeof(helpers)/sizeof(struct helper))
1756 findCommand(uint32_t command
)
1760 for (i
= 0; i
< (int)nHELPERS
; i
++) {
1761 if (helpers
[i
].command
== command
) {
1771 newHelper(void *arg
)
1773 CFRunLoopSourceRef rls
= NULL
;
1774 SCHelperSessionRef session
= (SCHelperSessionRef
)arg
;
1775 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1777 assert(session
!= NULL
);
1778 assert(sessionPrivate
->mp
!= NULL
);
1780 __SCHelperSessionSetThreadName(session
);
1782 rls
= CFMachPortCreateRunLoopSource(NULL
, sessionPrivate
->mp
, 0);
1783 CFRelease(sessionPrivate
->mp
);
1786 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
1789 SCLog(debug
, LOG_DEBUG
, CFSTR("%p : start"), session
);
1791 SCLog(debug
, LOG_DEBUG
, CFSTR("%p : stop"), session
);
1799 #pragma mark Main loop
1802 // MiG generated externals and functions
1803 extern struct mig_subsystem _helper_subsystem
;
1804 extern boolean_t
helper_server(mach_msg_header_t
*, mach_msg_header_t
*);
1809 notify_server(mach_msg_header_t
*request
, mach_msg_header_t
*reply
)
1811 mach_no_senders_notification_t
*Request
= (mach_no_senders_notification_t
*)request
;
1812 mig_reply_error_t
*Reply
= (mig_reply_error_t
*)reply
;
1814 reply
->msgh_bits
= MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request
->msgh_bits
), 0);
1815 reply
->msgh_remote_port
= request
->msgh_remote_port
;
1816 reply
->msgh_size
= sizeof(mig_reply_error_t
); /* Minimal size: update as needed */
1817 reply
->msgh_local_port
= MACH_PORT_NULL
;
1818 reply
->msgh_id
= request
->msgh_id
+ 100;
1820 if ((Request
->not_header
.msgh_id
> MACH_NOTIFY_LAST
) ||
1821 (Request
->not_header
.msgh_id
< MACH_NOTIFY_FIRST
)) {
1822 Reply
->NDR
= NDR_record
;
1823 Reply
->RetCode
= MIG_BAD_ID
;
1824 return FALSE
; /* if this is not a notification message */
1827 switch (Request
->not_header
.msgh_id
) {
1828 case MACH_NOTIFY_NO_SENDERS
: {
1829 SCHelperSessionRef session
;
1831 __MACH_PORT_DEBUG(TRUE
, "*** notify_server MACH_NOTIFY_NO_SENDERS", Request
->not_header
.msgh_local_port
);
1834 session
= __SCHelperSessionFindWithPort(Request
->not_header
.msgh_local_port
);
1835 if (session
!= NULL
) {
1836 SCHelperSessionPrivateRef sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
1838 // release CFMachPort *and* SCHelperSession
1839 CFMachPortInvalidate(sessionPrivate
->mp
);
1842 __MACH_PORT_DEBUG(TRUE
, "*** notify_server after invalidate", Request
->not_header
.msgh_local_port
);
1844 // and, lastly, remove our receive right.
1845 (void) mach_port_mod_refs(mach_task_self(),
1846 Request
->not_header
.msgh_local_port
,
1847 MACH_PORT_RIGHT_RECEIVE
, -1);
1849 Reply
->Head
.msgh_bits
= 0;
1850 Reply
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1851 Reply
->RetCode
= KERN_SUCCESS
;
1859 SCLog(TRUE
, LOG_ERR
, CFSTR("HELP!, Received notification: port=%d, msgh_id=%d"),
1860 Request
->not_header
.msgh_local_port
,
1861 Request
->not_header
.msgh_id
);
1863 Reply
->NDR
= NDR_record
;
1864 Reply
->RetCode
= MIG_BAD_ID
;
1865 return FALSE
; /* if this is not a notification we are handling */
1871 helper_demux(mach_msg_header_t
*request
, mach_msg_header_t
*reply
)
1873 Boolean processed
= FALSE
;
1876 * (attempt to) process SCHelper requests.
1878 processed
= helper_server(request
, reply
);
1884 * (attempt to) process (NO MORE SENDERS) notification messages.
1886 processed
= notify_server(request
, reply
);
1892 * unknown message ID, log and return an error.
1894 SCLog(TRUE
, LOG_ERR
, CFSTR("helper_demux(): unknown message ID (%d) received"), request
->msgh_id
);
1895 reply
->msgh_bits
= MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request
->msgh_bits
), 0);
1896 reply
->msgh_remote_port
= request
->msgh_remote_port
;
1897 reply
->msgh_size
= sizeof(mig_reply_error_t
); /* Minimal size */
1898 reply
->msgh_local_port
= MACH_PORT_NULL
;
1899 reply
->msgh_id
= request
->msgh_id
+ 100;
1900 ((mig_reply_error_t
*)reply
)->NDR
= NDR_record
;
1901 ((mig_reply_error_t
*)reply
)->RetCode
= MIG_BAD_ID
;
1907 #define MACH_MSG_BUFFER_SIZE 128
1911 helperCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1913 mig_reply_error_t
* bufRequest
= msg
;
1914 uint32_t bufReply_q
[MACH_MSG_BUFFER_SIZE
/sizeof(uint32_t)];
1915 mig_reply_error_t
* bufReply
= (mig_reply_error_t
*)bufReply_q
;
1916 static CFIndex bufSize
= 0;
1917 mach_msg_return_t mr
;
1922 ret
= proc_importance_assertion_begin_with_msg(&bufRequest
->Head
, NULL
, &token
);
1925 // get max size for MiG reply buffers
1926 bufSize
= _helper_subsystem
.maxsize
;
1928 // check if our on-the-stack reply buffer will be big enough
1929 if (bufSize
> sizeof(bufReply_q
)) {
1930 SCLog(TRUE
, LOG_NOTICE
,
1931 CFSTR("helperCallback(): buffer size should be increased > %d"),
1932 _helper_subsystem
.maxsize
);
1936 if (bufSize
> sizeof(bufReply_q
)) {
1937 bufReply
= CFAllocatorAllocate(NULL
, _helper_subsystem
.maxsize
, 0);
1939 bufReply
->RetCode
= 0;
1941 /* we have a request message */
1942 (void) helper_demux(&bufRequest
->Head
, &bufReply
->Head
);
1944 if (!(bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)) {
1945 if (bufReply
->RetCode
== MIG_NO_REPLY
) {
1946 bufReply
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1947 } else if ((bufReply
->RetCode
!= KERN_SUCCESS
) &&
1948 (bufRequest
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)) {
1950 * destroy the request - but not the reply port
1952 bufRequest
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1953 mach_msg_destroy(&bufRequest
->Head
);
1957 if (bufReply
->Head
.msgh_remote_port
!= MACH_PORT_NULL
) {
1961 * We don't want to block indefinitely because the client
1962 * isn't receiving messages from the reply port.
1963 * If we have a send-once right for the reply port, then
1964 * this isn't a concern because the send won't block.
1965 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
1966 * To avoid falling off the kernel's fast RPC path unnecessarily,
1967 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
1970 options
= MACH_SEND_MSG
;
1971 if (MACH_MSGH_BITS_REMOTE(bufReply
->Head
.msgh_bits
) != MACH_MSG_TYPE_MOVE_SEND_ONCE
) {
1972 options
|= MACH_SEND_TIMEOUT
;
1974 mr
= mach_msg(&bufReply
->Head
, /* msg */
1975 options
, /* option */
1976 bufReply
->Head
.msgh_size
, /* send_size */
1978 MACH_PORT_NULL
, /* rcv_name */
1979 MACH_MSG_TIMEOUT_NONE
, /* timeout */
1980 MACH_PORT_NULL
); /* notify */
1982 /* Has a message error occurred? */
1984 case MACH_SEND_INVALID_DEST
:
1985 case MACH_SEND_TIMED_OUT
:
1988 /* Includes success case. */
1993 if (bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
1994 mach_msg_destroy(&bufReply
->Head
);
2000 proc_importance_assertion_complete(token
);
2003 if (bufReply
!= (mig_reply_error_t
*)bufReply_q
)
2004 CFAllocatorDeallocate(NULL
, bufReply
);
2010 initMPCopyDescription(const void *info
)
2012 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<SCHelper MP>"));
2018 _helperinit(mach_port_t server
,
2019 mach_port_t
*newSession
,
2021 audit_token_t audit_token
)
2023 CFMachPortContext context
= { 0
2027 , initMPCopyDescription
2030 mach_port_t oldNotify
;
2031 SCHelperSessionRef session
;
2032 SCHelperSessionPrivateRef sessionPrivate
;
2033 pthread_attr_t tattr
;
2036 session
= __SCHelperSessionFindWithPort(server
);
2037 if (session
!= NULL
) {
2039 SCLog(TRUE
, LOG_DEBUG
, CFSTR("_helperinit(): session is already open."));
2041 *status
= kSCStatusFailed
; /* you can't re-open an "open" session */
2042 return KERN_SUCCESS
;
2045 session
= __SCHelperSessionCreate(NULL
);
2046 assert(session
!= NULL
);
2047 sessionPrivate
= (SCHelperSessionPrivateRef
)session
;
2049 // create per-session port
2050 (void) mach_port_allocate(mach_task_self(),
2051 MACH_PORT_RIGHT_RECEIVE
,
2052 &sessionPrivate
->port
);
2053 *newSession
= sessionPrivate
->port
;
2055 (void) mach_port_set_attributes(mach_task_self(),
2057 MACH_PORT_IMPORTANCE_RECEIVER
,
2062 // Note: we create the CFMachPort *before* we insert a send
2063 // right present to ensure that CF does not establish
2064 // its dead name notification.
2066 context
.info
= (void *)session
;
2067 sessionPrivate
->mp
= _SC_CFMachPortCreateWithPort("SCHelper/session",
2072 /* Request a notification when/if the client dies */
2073 kr
= mach_port_request_notification(mach_task_self(),
2075 MACH_NOTIFY_NO_SENDERS
,
2078 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
2080 if (kr
!= KERN_SUCCESS
) {
2081 SCLog(TRUE
, LOG_ERR
, CFSTR("_helperinit() mach_port_request_notification() failed: %s"), mach_error_string(kr
));
2083 // clean up CFMachPort, mach port rights
2084 CFMachPortInvalidate(sessionPrivate
->mp
);
2085 CFRelease(sessionPrivate
->mp
);
2086 sessionPrivate
->mp
= NULL
;
2087 (void) mach_port_mod_refs(mach_task_self(), *newSession
, MACH_PORT_RIGHT_RECEIVE
, -1);
2088 *newSession
= MACH_PORT_NULL
;
2089 *status
= kSCStatusFailed
;
2093 if (oldNotify
!= MACH_PORT_NULL
) {
2094 SCLog(TRUE
, LOG_ERR
, CFSTR("_helperinit(): oldNotify != MACH_PORT_NULL"));
2097 // add send right (that will be passed back to the client)
2098 (void) mach_port_insert_right(mach_task_self(),
2101 MACH_MSG_TYPE_MAKE_SEND
);
2104 sessionPrivate
->auditToken
= audit_token
;
2107 // Note: at this time we should be holding ONE send right and
2108 // ONE receive right to the server. The send right is
2109 // moved to the caller.
2112 // start per-session thread
2113 pthread_attr_init(&tattr
);
2114 pthread_attr_setscope(&tattr
, PTHREAD_SCOPE_SYSTEM
);
2115 pthread_attr_setdetachstate(&tattr
, PTHREAD_CREATE_DETACHED
);
2116 pthread_attr_setstacksize(&tattr
, 96 * 1024); // each thread gets a 96K stack
2117 pthread_create(&tid
, &tattr
, newHelper
, (void *)session
);
2118 pthread_attr_destroy(&tattr
);
2120 *status
= kSCStatusOK
;
2125 return KERN_SUCCESS
;
2131 _helperexec(mach_port_t server
,
2133 xmlData_t dataRef
, /* raw XML bytes */
2134 mach_msg_type_number_t dataLen
,
2135 xmlData_t traceRef
, /* raw XML bytes */
2136 mach_msg_type_number_t traceLen
,
2138 xmlDataOut_t
*replyRef
, /* raw XML bytes */
2139 mach_msg_type_number_t
*replyLen
)
2141 CFStringRef backtrace
= NULL
;
2142 CFDataRef data
= NULL
;
2144 CFDataRef reply
= NULL
;
2145 SCHelperSessionRef session
;
2147 *status
= kSCStatusOK
;
2151 if ((dataRef
!= NULL
) && (dataLen
> 0)) {
2152 if (!_SCUnserializeData(&data
, (void *)dataRef
, dataLen
)) {
2153 *status
= SCError();
2157 if ((traceRef
!= NULL
) && (traceLen
> 0)) {
2158 if (!_SCUnserializeString(&backtrace
, NULL
, (void *)traceRef
, traceLen
)) {
2159 *status
= SCError();
2163 if (*status
!= kSCStatusOK
) {
2167 session
= __SCHelperSessionFindWithPort(server
);
2168 if (session
== NULL
) {
2169 *status
= kSCStatusFailed
; /* you must have an open session to play */
2173 i
= findCommand(msgID
);
2175 SCLog(TRUE
, LOG_ERR
, CFSTR("received unknown command : %u"), msgID
);
2176 *status
= kSCStatusInvalidArgument
;
2180 SCLog(debug
, LOG_DEBUG
,
2181 CFSTR("%p : processing command \"%s\"%s"),
2183 helpers
[i
].commandName
,
2184 (data
!= NULL
) ? " w/data" : "");
2186 if (helpers
[i
].needsAuthorization
&&
2187 !hasAuthorization(session
, helpers
[i
].needsWrite
)) {
2188 SCLog(debug
, LOG_DEBUG
,
2189 CFSTR("%p : command \"%s\" : not authorized"),
2191 helpers
[i
].commandName
);
2192 *status
= kSCStatusAccessError
;
2195 if (*status
== kSCStatusOK
) {
2196 if (backtrace
!= NULL
) {
2197 __SCHelperSessionAddBacktrace(session
, backtrace
, helpers
[i
].commandName
);
2199 (*helpers
[i
].func
)(session
, helpers
[i
].info
, data
, status
, &reply
);
2202 if ((*status
!= -1) || (reply
!= NULL
)) {
2205 SCLog(debug
, LOG_DEBUG
,
2206 CFSTR("%p : sending status %u%s"),
2209 (reply
!= NULL
) ? " w/reply" : "");
2211 /* serialize the data */
2212 if (reply
!= NULL
) {
2215 ok
= _SCSerializeData(reply
, (void **)replyRef
, &len
);
2220 *status
= SCError();
2228 if (data
!= NULL
) CFRelease(data
);
2229 if (backtrace
!= NULL
) CFRelease(backtrace
);
2230 if (reply
!= NULL
) CFRelease(reply
);
2231 return KERN_SUCCESS
;
2236 helperMPCopyDescription(const void *info
)
2238 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<main SCHelper MP>"));
2243 init_MiG_1(const launch_data_t l_obj
, const char *name
, void *info
)
2245 CFMachPortContext context
= { 0
2249 , helperMPCopyDescription
2251 launch_data_type_t l_type
;
2253 int *n_listeners
= (int *)info
;
2254 CFRunLoopSourceRef rls
;
2255 mach_port_t service_port
;
2257 // get the mach port
2258 l_type
= (l_obj
!= NULL
) ? launch_data_get_type(l_obj
) : 0;
2259 if (l_type
!= LAUNCH_DATA_MACHPORT
) {
2260 SCLog(TRUE
, LOG_ERR
,
2261 CFSTR("SCHelper: error w/MachServices \"%s\" port (%p, %d)"),
2262 (name
!= NULL
) ? name
: "?",
2267 service_port
= launch_data_get_machport(l_obj
);
2269 // add a run loop source to listen for new requests
2270 mp
= _SC_CFMachPortCreateWithPort("SCHelper/server",
2274 rls
= CFMachPortCreateRunLoopSource(NULL
, mp
, 0);
2276 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
2279 *n_listeners
= *n_listeners
+ 1;
2286 init_MiG(launch_data_t l_reply
, int *n_listeners
)
2288 launch_data_t l_machservices
;
2289 launch_data_type_t l_type
;
2291 l_machservices
= launch_data_dict_lookup(l_reply
, LAUNCH_JOBKEY_MACHSERVICES
);
2292 l_type
= (l_machservices
!= NULL
) ? launch_data_get_type(l_machservices
) : 0;
2293 if (l_type
!= LAUNCH_DATA_DICTIONARY
) {
2294 SCLog(TRUE
, LOG_ERR
,
2295 CFSTR("SCHelper: error w/" LAUNCH_JOBKEY_MACHSERVICES
" (%p, %d)"),
2301 launch_data_dict_iterate(l_machservices
, init_MiG_1
, (void *)n_listeners
);
2310 static const struct option longopts
[] = {
2311 { "debug", no_argument
, 0, 'd' },
2317 main(int argc
, char **argv
)
2319 Boolean done
= FALSE
;
2321 int gen_reported
= 0;
2323 launch_data_t l_msg
;
2324 launch_data_t l_reply
;
2325 launch_data_type_t l_type
;
2326 int n_listeners
= 0;
2327 // extern int optind;
2331 openlog("SCHelper", LOG_CONS
|LOG_PID
, LOG_DAEMON
);
2333 // process any arguments
2334 while ((opt
= getopt_long(argc
, argv
, "d", longopts
, &opti
)) != -1) {
2340 // if (strcmp(longopts[opti].name, "debug") == 1) {
2345 SCLog(TRUE
, LOG_ERR
,
2346 CFSTR("ignoring unknown or ambiguous command line option"));
2353 if (geteuid() != 0) {
2354 SCLog(TRUE
, LOG_ERR
, CFSTR("%s"), strerror(EACCES
));
2358 main_runLoop
= CFRunLoopGetCurrent();
2360 l_msg
= launch_data_new_string(LAUNCH_KEY_CHECKIN
);
2361 l_reply
= launch_msg(l_msg
);
2362 launch_data_free(l_msg
);
2363 l_type
= (l_reply
!= NULL
) ? launch_data_get_type(l_reply
) : 0;
2364 if (l_type
!= LAUNCH_DATA_DICTIONARY
) {
2365 SCLog(TRUE
, LOG_ERR
,
2366 CFSTR("SCHelper: error w/launchd " LAUNCH_KEY_CHECKIN
" dictionary (%p, %d)"),
2373 err
= init_MiG(l_reply
, &n_listeners
);
2380 if (l_reply
!= NULL
) launch_data_free(l_reply
);
2382 if ((err
!= 0) || (n_listeners
== 0)) {
2386 pthread_setname_np("SCHelper main thread");
2392 rlStatus
= CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 15.0, TRUE
);
2394 pthread_mutex_lock(&sessions_lock
);
2396 if (sessions
!= NULL
) {
2397 if (rlStatus
== kCFRunLoopRunTimedOut
) {
2400 if ((CFSetGetCount(sessions
) == 0) && (sessions_closed
== 0)) {
2401 // if we don't have any open sessions and no
2402 // sessions have recently been closed
2409 gen_current
= sessions_generation
;
2410 sessions_closed
= 0;
2412 if (!done
&& (idle
>= (2 * 60 / 15))) {
2413 if (gen_reported
!= gen_current
) {
2414 FILE *logFile
= NULL
;
2416 SCLog(TRUE
, LOG_NOTICE
, CFSTR("active (but IDLE) sessions"));
2417 CFSetApplyFunction(sessions
, __SCHelperSessionLog
, (void *)&logFile
);
2418 gen_reported
= gen_current
;
2420 if (logFile
!= NULL
) {
2421 (void) fclose(logFile
);
2427 pthread_mutex_unlock(&sessions_lock
);