2 * Copyright (c) 2006-2010,2012-2014 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@
26 * debugging.c - non-trivial debug support
28 #include "utilities/debugging.h"
29 #include "utilities/debugging_test.h"
30 #include "utilities/SecCFWrappers.h"
31 #include "utilities/SecFileLocations.h"
32 #include <CoreFoundation/CFSet.h>
33 #include <CoreFoundation/CFString.h>
34 #include <CoreFoundation/CFPreferences.h>
36 #include <dispatch/dispatch.h>
45 #include <os/log_private.h>
47 #include <os/lock_private.h>
49 const char *api_trace
= "api_trace";
52 const CFStringRef kStringNegate
= CFSTR("-");
53 const CFStringRef kStringAll
= CFSTR("all");
55 const CFStringRef kAPIScope
= CFSTR("api");
57 static CFMutableArrayRef sLogSettings
= NULL
; /* Either sets or dictionaries of level => set. */
59 static dispatch_queue_t
GetDispatchControlQueue(void) {
60 static dispatch_queue_t sLoggingScopeControlQueue
;
61 static dispatch_once_t onceToken
;
62 dispatch_once(&onceToken
, ^{
63 sLoggingScopeControlQueue
= dispatch_queue_create("security scope control", DISPATCH_QUEUE_CONCURRENT
);
65 return sLoggingScopeControlQueue
;
68 static void with_scopes_read(dispatch_block_t action
) {
69 dispatch_sync(GetDispatchControlQueue(), action
);
72 static void with_scopes_write(dispatch_block_t action
) {
73 dispatch_barrier_sync(GetDispatchControlQueue(), action
);
76 bool IsScopeActive(int level
, CFStringRef scope
)
81 CFNumberRef level_number
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &level
);
83 __block
bool isActive
= false;
86 CFArrayForEach(sLogSettings
, ^(const void *value
) {
87 CFSetRef setToCheck
= NULL
;
90 setToCheck
= (CFSetRef
) value
;
91 } else if (isDictionary(value
)) {
92 CFDictionaryRef levels
= (CFDictionaryRef
) value
;
94 setToCheck
= CFDictionaryGetValue(levels
, level_number
);
96 if (!isSet(setToCheck
))
100 if (setToCheck
!= NULL
&& !isActive
) {
101 bool negated
= CFSetContainsValue(setToCheck
, kStringNegate
);
102 bool inSet
= CFSetContainsValue(setToCheck
, scope
);
104 isActive
= negated
^ inSet
;
110 CFReleaseNull(level_number
);
115 bool IsScopeActiveC(int level
, const char *scope
)
117 CFStringRef scopeString
= CFStringCreateWithBytes(kCFAllocatorDefault
, (const uint8_t*)scope
, strlen(scope
), kCFStringEncodingUTF8
, false);
118 bool isActive
= IsScopeActive(level
, scopeString
);
119 CFReleaseNull(scopeString
);
125 static CFMutableSetRef
CopyScopesFromScopeList(CFStringRef scopes
) {
126 CFMutableSetRef resultSet
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
128 CFStringRef allocated_scope_list
= NULL
;
129 CFStringRef clean_scope_list
= scopes
;
130 bool add_negate
= false;
132 if (CFStringHasPrefix(scopes
, kStringNegate
)) {
133 allocated_scope_list
= CFStringCreateWithSubstring(kCFAllocatorDefault
, scopes
, CFRangeMake(CFStringGetLength(kStringNegate
), CFStringGetLength(scopes
) - 1));
134 clean_scope_list
= allocated_scope_list
;
138 CFArrayRef commaArray
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault
, clean_scope_list
, CFSTR(","));
141 CFArrayForEach(commaArray
, ^(const void *value
) {
142 if (isString(value
)) {
143 CFMutableStringRef copy
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, (CFStringRef
) value
);
144 CFStringTrimWhitespace(copy
);
145 CFSetSetValue(resultSet
, copy
);
151 CFSetRemoveValue(resultSet
, CFSTR("none"));
152 CFSetRemoveValue(resultSet
, CFSTR(""));
154 if (CFSetContainsValue(resultSet
, CFSTR("all"))) {
155 CFSetRemoveAllValues(resultSet
);
156 add_negate
= !add_negate
;
160 CFSetSetValue(resultSet
, kStringNegate
);
162 CFReleaseNull(commaArray
);
163 CFReleaseNull(allocated_scope_list
);
168 static CFMutableArrayRef
CFArrayCreateMutableForCFTypesFilledWithCFNull(CFAllocatorRef allocator
, CFIndex capacity
) {
169 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypesWithCapacity(kCFAllocatorDefault
, kScopeIDMax
);
171 for(int count
= 0; count
<= capacity
; ++count
)
172 CFArrayAppendValue(result
, kCFNull
);
177 static bool CFArrayIsAll(CFArrayRef array
, const void *value
)
179 return CFArrayGetCountOfValue(array
, CFRangeMake(0, CFArrayGetCount(array
)), value
) == CFArrayGetCount(array
);
182 static void SetNthScopeSet(int nth
, CFTypeRef collection
)
185 if (sLogSettings
== NULL
) {
186 sLogSettings
= CFArrayCreateMutableForCFTypesFilledWithCFNull(kCFAllocatorDefault
, kScopeIDMax
);
189 CFArraySetValueAtIndex(sLogSettings
, nth
, collection
);
191 if (CFArrayIsAll(sLogSettings
, kCFNull
)) {
192 CFReleaseNull(sLogSettings
);
197 static int string_to_log_level(CFStringRef string
) {
198 if (CFEqual(string
, CFSTR(ASL_STRING_EMERG
)))
199 return SECLOG_LEVEL_EMERG
;
200 else if (CFEqual(string
, CFSTR(ASL_STRING_ALERT
)))
201 return SECLOG_LEVEL_ALERT
;
202 else if (CFEqual(string
, CFSTR(ASL_STRING_CRIT
)))
203 return SECLOG_LEVEL_CRIT
;
204 else if (CFEqual(string
, CFSTR(ASL_STRING_ERR
)))
205 return SECLOG_LEVEL_ERR
;
206 else if (CFEqual(string
, CFSTR(ASL_STRING_WARNING
)))
207 return SECLOG_LEVEL_WARNING
;
208 else if (CFEqual(string
, CFSTR(ASL_STRING_NOTICE
)))
209 return SECLOG_LEVEL_NOTICE
;
210 else if (CFEqual(string
, CFSTR(ASL_STRING_INFO
)))
211 return SECLOG_LEVEL_INFO
;
212 else if (CFEqual(string
, CFSTR(ASL_STRING_DEBUG
)))
213 return SECLOG_LEVEL_DEBUG
;
218 static CFMutableArrayRef
CFSetOfCFObjectsCopyValues(CFSetRef setOfCFs
)
220 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
222 CFSetForEach(setOfCFs
, ^(const void *value
) {
223 CFArrayAppendValue(result
, value
);
229 CFPropertyListRef
CopyCurrentScopePlist(void)
231 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
233 CFArrayForEach(sLogSettings
, ^(const void *value
) {
235 CFArrayRef values
= CFSetOfCFObjectsCopyValues((CFSetRef
) value
);
236 CFArrayAppendValue(result
, values
);
237 CFReleaseNull(values
);
238 } else if (isDictionary(value
)) {
239 CFMutableDictionaryRef levels
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
241 CFDictionaryForEach((CFDictionaryRef
) value
, ^(const void *key
, const void *value
) {
243 CFArrayRef values
= CFSetOfCFObjectsCopyValues((CFSetRef
) value
);
244 CFDictionaryAddValue(levels
, key
, values
);
245 CFReleaseNull(values
);
249 CFArrayAppendValue(result
, levels
);
251 CFArrayAppendValue(result
, kCFNull
);
258 void ApplyScopeDictionaryForID(CFDictionaryRef scopeDictionary
, SecDebugScopeID whichID
)
260 CFMutableDictionaryRef dictionary_for_id
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
262 CFDictionaryForEach(scopeDictionary
, ^(const void *key
, const void *value
) {
263 CFSetRef scope_set
= NULL
;
264 CFNumberRef key_number
= NULL
;
266 int level
= string_to_log_level((CFStringRef
) key
);
269 key_number
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &level
);
270 } else if (isNumber(key
)) {
271 key_number
= CFRetainSafe(key
);
274 if (isString(value
)) {
275 scope_set
= CopyScopesFromScopeList(value
);
278 if (key_number
&& scope_set
)
279 CFDictionaryAddValue(dictionary_for_id
, key_number
, scope_set
);
281 CFReleaseNull(key_number
);
282 CFReleaseNull(scope_set
);
285 if (CFDictionaryGetCount(dictionary_for_id
) > 0) {
286 SetNthScopeSet(whichID
, dictionary_for_id
);
289 CFReleaseNull(dictionary_for_id
);
292 void ApplyScopeListForID(CFStringRef scopeList
, SecDebugScopeID whichID
)
294 CFMutableSetRef scopesToUse
= CopyScopesFromScopeList(scopeList
);
296 SetNthScopeSet(whichID
, scopesToUse
);
298 CFReleaseNull(scopesToUse
);
301 void ApplyScopeListForIDC(const char *scopeList
, SecDebugScopeID whichID
) {
302 CFStringRef scope_string
= CFStringCreateWithCStringNoCopy(kCFAllocatorDefault
, scopeList
, kCFStringEncodingUTF8
, kCFAllocatorNull
);
304 ApplyScopeListForID(scope_string
, whichID
);
306 CFReleaseNull(scope_string
);
309 #pragma mark - Log Handlers to catch log information
313 * Instead of using CFPropertyListReadFromFile we use a
314 * CFPropertyListCreateWithStream directly
315 * here. CFPropertyListReadFromFile() uses
316 * CFURLCopyResourcePropertyForKey() andCF pulls in CoreServices for
317 * CFURLCopyResourcePropertyForKey() and that doesn't work in install
321 static CFPropertyListRef
322 CopyPlistFromFile(CFURLRef url
)
324 CFDictionaryRef d
= NULL
;
325 CFReadStreamRef s
= CFReadStreamCreateWithFile(kCFAllocatorDefault
, url
);
326 if (s
&& CFReadStreamOpen(s
)) {
327 d
= (CFDictionaryRef
)CFPropertyListCreateWithStream(kCFAllocatorDefault
, s
, 0, kCFPropertyListImmutable
, NULL
, NULL
);
334 static void ApplyScopeByTypeForID(CFPropertyListRef scopes
, SecDebugScopeID whichID
) {
335 if (isDictionary(scopes
)) {
336 ApplyScopeDictionaryForID(scopes
, whichID
);
337 } else if (isString(scopes
)) {
338 ApplyScopeListForID(scopes
, whichID
);
342 static void setup_config_settings() {
343 CFStringRef logFileName
;
345 logFileName
= CFSTR(".GlobalPreferences.plist");
347 logFileName
= CFSTR("com.apple.security.logging.plist");
349 CFURLRef prefURL
= SecCopyURLForFileInManagedPreferencesDirectory(logFileName
);
351 CFPropertyListRef plist
= CopyPlistFromFile(prefURL
);
353 ApplyScopeByTypeForID(CFDictionaryGetValue(plist
, CFSTR("SecLogging")), kScopeIDConfig
);
355 CFReleaseSafe(plist
);
357 CFReleaseSafe(prefURL
);
360 static void setup_defaults_settings() {
361 CFPropertyListRef scopes_value
= CFPreferencesCopyValue(CFSTR("Logging"), CFSTR("com.apple.security"), kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
363 ApplyScopeByTypeForID(scopes_value
, kScopeIDDefaults
);
365 CFReleaseSafe(scopes_value
);
368 static void setup_circle_defaults_settings() {
369 CFPropertyListRef scopes_value
= CFPreferencesCopyValue(CFSTR("Circle-Logging"), CFSTR("com.apple.security"), kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
371 ApplyScopeByTypeForID(scopes_value
, kScopeIDDefaults
);
373 CFReleaseSafe(scopes_value
);
376 static void setup_environment_scopes() {
377 const char *cur_scope
= getenv("DEBUGSCOPE");
378 if (cur_scope
== NULL
)
381 ApplyScopeListForIDC(cur_scope
, kScopeIDEnvironment
);
385 void __security_debug_init(void) {
386 static dispatch_once_t sdOnceToken
;
388 dispatch_once(&sdOnceToken
, ^{
389 setup_environment_scopes();
390 setup_config_settings();
391 setup_defaults_settings();
392 setup_circle_defaults_settings();
398 secLogObjForCFScope(CFStringRef scope
)
400 os_log_t retval
= OS_LOG_DISABLED
;
401 static os_unfair_lock lock
= OS_UNFAIR_LOCK_INIT
;
402 static CFMutableDictionaryRef scopeMap
= NULL
;
405 scope
= CFSTR("logging");
408 os_unfair_lock_lock_with_options(&lock
, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION
);
410 if (scopeMap
== NULL
) {
411 scopeMap
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFCopyStringDictionaryKeyCallBacks
, NULL
);
414 retval
= (os_log_t
)CFDictionaryGetValue(scopeMap
, scope
);
415 if (retval
== NULL
) {
416 CFStringPerformWithCString(scope
, ^(const char *scopeStr
) {
417 CFDictionaryAddValue(scopeMap
, scope
, os_log_create("com.apple.securityd", scopeStr
));
419 retval
= (os_log_t
)CFDictionaryGetValue(scopeMap
, scope
);
422 os_unfair_lock_unlock(&lock
);
427 static bool loggingEnabled
= true;
428 static pthread_mutex_t loggingMutex
= PTHREAD_MUTEX_INITIALIZER
;
430 bool secLogEnabled(void) {
432 pthread_mutex_lock(&loggingMutex
);
434 pthread_mutex_unlock(&loggingMutex
);
437 void secLogDisable(void) {
438 pthread_mutex_lock(&loggingMutex
);
439 loggingEnabled
= false;
440 pthread_mutex_unlock(&loggingMutex
);
443 void secLogEnable(void) {
444 pthread_mutex_lock(&loggingMutex
);
445 loggingEnabled
= true;
446 pthread_mutex_unlock(&loggingMutex
);
449 os_log_t
secLogObjForScope(const char *scope
) {
450 if (!secLogEnabled())
451 return OS_LOG_DISABLED
;
452 CFStringRef cfscope
= NULL
;
453 if(scope
) cfscope
= CFStringCreateWithCString(kCFAllocatorDefault
, scope
, kCFStringEncodingASCII
);
454 os_log_t retval
= secLogObjForCFScope(cfscope
);
455 CFReleaseNull(cfscope
);
461 CFStringRef
SecLogAPICreate(bool apiIn
, const char *api
, CFStringRef format
, ... ) {
462 CFMutableStringRef outStr
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
464 char *direction
= apiIn
? "ENTER" : "RETURN";
466 va_start(args
, format
);
468 CFStringAppend(outStr
, CFSTR("SecAPITrace "));
469 CFStringAppendCString(outStr
, api
, kCFStringEncodingASCII
);
470 CFStringAppendCString(outStr
, direction
, kCFStringEncodingASCII
);
473 CFStringRef message
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, NULL
, format
, args
);
474 CFStringAppend(outStr
, message
);
475 CFReleaseSafe(message
);
479 char caller_info
[80];
480 snprintf(caller_info
, sizeof(caller_info
), "C%p F%p", __builtin_return_address(1), __builtin_frame_address(2));
481 CFStringAppend(outStr
, CFSTR("CALLER "));
482 CFStringAppendCString(outStr
, caller_info
, kCFStringEncodingASCII
);