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 void CFSetAppendValues(CFSetRef set
, CFMutableArrayRef appendTo
)
220 CFSetForEach(set
, ^(const void *value
) {
221 CFArrayAppendValue(appendTo
, value
);
225 static CFMutableArrayRef
CFSetOfCFObjectsCopyValues(CFSetRef setOfCFs
)
227 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
229 CFSetForEach(setOfCFs
, ^(const void *value
) {
230 CFArrayAppendValue(result
, value
);
236 CFPropertyListRef
CopyCurrentScopePlist(void)
238 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
240 CFArrayForEach(sLogSettings
, ^(const void *value
) {
242 CFArrayRef values
= CFSetOfCFObjectsCopyValues((CFSetRef
) value
);
243 CFArrayAppendValue(result
, values
);
244 CFReleaseNull(values
);
245 } else if (isDictionary(value
)) {
246 CFMutableDictionaryRef levels
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
248 CFDictionaryForEach((CFDictionaryRef
) value
, ^(const void *key
, const void *value
) {
250 CFArrayRef values
= CFSetOfCFObjectsCopyValues((CFSetRef
) value
);
251 CFDictionaryAddValue(levels
, key
, values
);
252 CFReleaseNull(values
);
256 CFArrayAppendValue(result
, levels
);
258 CFArrayAppendValue(result
, kCFNull
);
265 void ApplyScopeDictionaryForID(CFDictionaryRef scopeDictionary
, SecDebugScopeID whichID
)
267 CFMutableDictionaryRef dictionary_for_id
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
269 CFDictionaryForEach(scopeDictionary
, ^(const void *key
, const void *value
) {
270 CFSetRef scope_set
= NULL
;
271 CFNumberRef key_number
= NULL
;
273 int level
= string_to_log_level((CFStringRef
) key
);
276 key_number
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &level
);
277 } else if (isNumber(key
)) {
278 key_number
= CFRetainSafe(key
);
281 if (isString(value
)) {
282 scope_set
= CopyScopesFromScopeList(value
);
285 if (key_number
&& scope_set
)
286 CFDictionaryAddValue(dictionary_for_id
, key_number
, scope_set
);
288 CFReleaseNull(key_number
);
289 CFReleaseNull(scope_set
);
292 if (CFDictionaryGetCount(dictionary_for_id
) > 0) {
293 SetNthScopeSet(whichID
, dictionary_for_id
);
296 CFReleaseNull(dictionary_for_id
);
299 void ApplyScopeListForID(CFStringRef scopeList
, SecDebugScopeID whichID
)
301 CFMutableSetRef scopesToUse
= CopyScopesFromScopeList(scopeList
);
303 SetNthScopeSet(whichID
, scopesToUse
);
305 CFReleaseNull(scopesToUse
);
308 void ApplyScopeListForIDC(const char *scopeList
, SecDebugScopeID whichID
) {
309 CFStringRef scope_string
= CFStringCreateWithCStringNoCopy(kCFAllocatorDefault
, scopeList
, kCFStringEncodingUTF8
, kCFAllocatorNull
);
311 ApplyScopeListForID(scope_string
, whichID
);
313 CFReleaseNull(scope_string
);
316 #pragma mark - Log Handlers to catch log information
320 * Instead of using CFPropertyListReadFromFile we use a
321 * CFPropertyListCreateWithStream directly
322 * here. CFPropertyListReadFromFile() uses
323 * CFURLCopyResourcePropertyForKey() andCF pulls in CoreServices for
324 * CFURLCopyResourcePropertyForKey() and that doesn't work in install
328 static CFPropertyListRef
329 CopyPlistFromFile(CFURLRef url
)
331 CFDictionaryRef d
= NULL
;
332 CFReadStreamRef s
= CFReadStreamCreateWithFile(kCFAllocatorDefault
, url
);
333 if (s
&& CFReadStreamOpen(s
)) {
334 d
= (CFDictionaryRef
)CFPropertyListCreateWithStream(kCFAllocatorDefault
, s
, 0, kCFPropertyListImmutable
, NULL
, NULL
);
341 static void ApplyScopeByTypeForID(CFPropertyListRef scopes
, SecDebugScopeID whichID
) {
342 if (isDictionary(scopes
)) {
343 ApplyScopeDictionaryForID(scopes
, whichID
);
344 } else if (isString(scopes
)) {
345 ApplyScopeListForID(scopes
, whichID
);
349 static void setup_config_settings() {
350 CFStringRef logFileName
;
352 logFileName
= CFSTR(".GlobalPreferences.plist");
354 logFileName
= CFSTR("com.apple.security.logging.plist");
356 CFURLRef prefURL
= SecCopyURLForFileInManagedPreferencesDirectory(logFileName
);
358 CFPropertyListRef plist
= CopyPlistFromFile(prefURL
);
360 ApplyScopeByTypeForID(CFDictionaryGetValue(plist
, CFSTR("SecLogging")), kScopeIDConfig
);
362 CFReleaseSafe(plist
);
364 CFReleaseSafe(prefURL
);
367 static void setup_defaults_settings() {
368 CFPropertyListRef scopes_value
= CFPreferencesCopyValue(CFSTR("Logging"), CFSTR("com.apple.security"), kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
370 ApplyScopeByTypeForID(scopes_value
, kScopeIDDefaults
);
372 CFReleaseSafe(scopes_value
);
375 static void setup_circle_defaults_settings() {
376 CFPropertyListRef scopes_value
= CFPreferencesCopyValue(CFSTR("Circle-Logging"), CFSTR("com.apple.security"), kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
378 ApplyScopeByTypeForID(scopes_value
, kScopeIDDefaults
);
380 CFReleaseSafe(scopes_value
);
383 static void setup_environment_scopes() {
384 const char *cur_scope
= getenv("DEBUGSCOPE");
385 if (cur_scope
== NULL
)
388 ApplyScopeListForIDC(cur_scope
, kScopeIDEnvironment
);
392 void __security_debug_init(void) {
393 static dispatch_once_t sdOnceToken
;
395 dispatch_once(&sdOnceToken
, ^{
396 setup_environment_scopes();
397 setup_config_settings();
398 setup_defaults_settings();
399 setup_circle_defaults_settings();
406 static char *copyScopeStr(CFStringRef scope
, char *alternative
) {
407 char *scopeStr
= NULL
;
409 scopeStr
= CFStringToCString(scope
);
411 scopeStr
= strdup("noScope");
417 secLogObjForCFScope(CFStringRef scope
)
419 os_log_t retval
= OS_LOG_DISABLED
;
420 static os_unfair_lock lock
= OS_UNFAIR_LOCK_INIT
;
421 static CFMutableDictionaryRef scopeMap
= NULL
;
424 scope
= CFSTR("logging");
427 os_unfair_lock_lock_with_options(&lock
, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION
);
429 if (scopeMap
== NULL
) {
430 scopeMap
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFCopyStringDictionaryKeyCallBacks
, NULL
);
433 retval
= (os_log_t
)CFDictionaryGetValue(scopeMap
, scope
);
434 if (retval
== NULL
) {
435 CFStringPerformWithCString(scope
, ^(const char *scopeStr
) {
436 CFDictionaryAddValue(scopeMap
, scope
, os_log_create("com.apple.securityd", scopeStr
));
438 retval
= (os_log_t
)CFDictionaryGetValue(scopeMap
, scope
);
441 os_unfair_lock_unlock(&lock
);
446 static bool loggingEnabled
= true;
447 static pthread_mutex_t loggingMutex
= PTHREAD_MUTEX_INITIALIZER
;
449 bool secLogEnabled(void) {
451 pthread_mutex_lock(&loggingMutex
);
453 pthread_mutex_unlock(&loggingMutex
);
456 void secLogDisable(void) {
457 pthread_mutex_lock(&loggingMutex
);
458 loggingEnabled
= false;
459 pthread_mutex_unlock(&loggingMutex
);
462 void secLogEnable(void) {
463 pthread_mutex_lock(&loggingMutex
);
464 loggingEnabled
= true;
465 pthread_mutex_unlock(&loggingMutex
);
468 os_log_t
secLogObjForScope(const char *scope
) {
469 if (!secLogEnabled())
470 return OS_LOG_DISABLED
;
471 CFStringRef cfscope
= NULL
;
472 if(scope
) cfscope
= CFStringCreateWithCString(kCFAllocatorDefault
, scope
, kCFStringEncodingASCII
);
473 os_log_t retval
= secLogObjForCFScope(cfscope
);
474 CFReleaseNull(cfscope
);
480 CFStringRef
SecLogAPICreate(bool apiIn
, const char *api
, CFStringRef format
, ... ) {
481 CFMutableStringRef outStr
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
483 char *direction
= apiIn
? "ENTER" : "RETURN";
485 va_start(args
, format
);
487 CFStringAppend(outStr
, CFSTR("SecAPITrace "));
488 CFStringAppendCString(outStr
, api
, kCFStringEncodingASCII
);
489 CFStringAppendCString(outStr
, direction
, kCFStringEncodingASCII
);
492 CFStringRef message
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, NULL
, format
, args
);
493 CFStringAppend(outStr
, message
);
494 CFReleaseSafe(message
);
498 char caller_info
[80];
499 snprintf(caller_info
, sizeof(caller_info
), "C%p F%p", __builtin_return_address(1), __builtin_frame_address(2));
500 CFStringAppend(outStr
, CFSTR("CALLER "));
501 CFStringAppendCString(outStr
, caller_info
, kCFStringEncodingASCII
);
509 // Functions for weak-linking os_log functions
512 #define weak_log_f(fname, newname, rettype, fallthrough) \
513 rettype newname(log_args) { \
514 static dispatch_once_t onceToken = 0; \
515 static rettype (*newname)(log_args) = NULL; \
517 dispatch_once(&onceToken, ^{ \
518 void* libtrace = dlopen("/usr/lib/system/libsystem_trace.dylib", RTLD_LAZY | RTLD_LOCAL); \
520 newname = (rettype(*)(log_args)) dlsym(libtrace, #fname); \
525 return newname(log_argnames); \
530 #define log_args void *dso, os_log_t log, os_log_type_t type, const char *format, uint8_t *buf, unsigned int size
531 #define log_argnames dso, log, type, format, buf, size
532 weak_log_f(_os_log_impl
, weak_os_log_impl
, void, return);
536 #define log_args const char *subsystem, const char *category
537 #define log_argnames subsystem, category
538 weak_log_f(os_log_create
, weak_os_log_create
, os_log_t
, return NULL
);
542 #define log_args os_log_t oslog, os_log_type_t type
543 #define log_argnames oslog, type
544 weak_log_f(os_log_type_enabled
, weak_os_log_type_enabled
, bool, return false);
550 #endif // TARGET_OS_OSX