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>
48 const uint8_t _os_trace_type_map
[8] = {
49 OS_TRACE_TYPE_FAULT
, // ASL_LEVEL_EMERG
50 OS_TRACE_TYPE_FAULT
, // ASL_LEVEL_ALERT
51 OS_TRACE_TYPE_FAULT
, // ASL_LEVEL_CRIT
52 OS_TRACE_TYPE_ERROR
, // ASL_LEVEL_ERR
53 OS_TRACE_TYPE_RELEASE
, // ASL_LEVEL_WARNING
54 OS_TRACE_TYPE_RELEASE
, // ASL_LEVEL_NOTICE
55 OS_TRACE_TYPE_RELEASE
, // ASL_LEVEL_INFO
56 OS_TRACE_TYPE_DEBUG
// ASL_LEVEL_DEBUG
59 const char *_asl_string_map
[8] = {
60 ASL_STRING_EMERG
, // ASL_LEVEL_EMERG
61 ASL_STRING_ALERT
, // ASL_LEVEL_ALERT
62 ASL_STRING_CRIT
, // ASL_LEVEL_CRIT
63 ASL_STRING_ERR
, // ASL_LEVEL_ERR
64 ASL_STRING_WARNING
, // ASL_LEVEL_WARNING
65 ASL_STRING_NOTICE
, // ASL_LEVEL_NOTICE
66 ASL_STRING_INFO
, // ASL_LEVEL_INFO
67 ASL_STRING_DEBUG
// ASL_LEVEL_DEBUG
70 const CFStringRef kStringNegate
= CFSTR("-");
71 const CFStringRef kStringAll
= CFSTR("all");
73 const CFStringRef kAPIScope
= CFSTR("api");
75 static CFMutableArrayRef sLogSettings
= NULL
; /* Either sets or dictionaries of level => set. */
77 static dispatch_queue_t
GetDispatchControlQueue(void) {
78 static dispatch_queue_t sLoggingScopeControlQueue
;
79 static dispatch_once_t onceToken
;
80 dispatch_once(&onceToken
, ^{
81 sLoggingScopeControlQueue
= dispatch_queue_create("security scope control", DISPATCH_QUEUE_CONCURRENT
);
83 return sLoggingScopeControlQueue
;
86 static void with_scopes_read(dispatch_block_t action
) {
87 dispatch_sync(GetDispatchControlQueue(), action
);
90 static void with_scopes_write(dispatch_block_t action
) {
91 dispatch_barrier_sync(GetDispatchControlQueue(), action
);
94 bool IsScopeActive(int level
, CFStringRef scope
)
99 CFNumberRef level_number
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &level
);
101 __block
bool isActive
= false;
104 CFArrayForEach(sLogSettings
, ^(const void *value
) {
105 CFSetRef setToCheck
= NULL
;
108 setToCheck
= (CFSetRef
) value
;
109 } else if (isDictionary(value
)) {
110 CFDictionaryRef levels
= (CFDictionaryRef
) value
;
112 setToCheck
= CFDictionaryGetValue(levels
, level_number
);
114 if (!isSet(setToCheck
))
118 if (setToCheck
!= NULL
&& !isActive
) {
119 bool negated
= CFSetContainsValue(setToCheck
, kStringNegate
);
120 bool inSet
= CFSetContainsValue(setToCheck
, scope
);
122 isActive
= negated
^ inSet
;
128 CFReleaseNull(level_number
);
133 bool IsScopeActiveC(int level
, const char *scope
)
135 CFStringRef scopeString
= CFStringCreateWithBytes(kCFAllocatorDefault
, (const uint8_t*)scope
, strlen(scope
), kCFStringEncodingUTF8
, false);
136 bool isActive
= IsScopeActive(level
, scopeString
);
137 CFReleaseNull(scopeString
);
144 static CFStringRef
copyScopeName(const char *scope
, CFIndex scopeLen
) {
145 return CFStringCreateWithBytes(kCFAllocatorDefault
, (const UInt8
*)scope
,
146 scopeLen
, kCFStringEncodingUTF8
, false);
149 static CFMutableSetRef
CopyScopesFromScopeList(CFStringRef scopes
) {
150 CFMutableSetRef resultSet
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
152 CFStringRef allocated_scope_list
= NULL
;
153 CFStringRef clean_scope_list
= scopes
;
154 bool add_negate
= false;
156 if (CFStringHasPrefix(scopes
, kStringNegate
)) {
157 allocated_scope_list
= CFStringCreateWithSubstring(kCFAllocatorDefault
, scopes
, CFRangeMake(CFStringGetLength(kStringNegate
), CFStringGetLength(scopes
) - 1));
158 clean_scope_list
= allocated_scope_list
;
162 CFArrayRef commaArray
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault
, clean_scope_list
, CFSTR(","));
165 CFArrayForEach(commaArray
, ^(const void *value
) {
166 if (isString(value
)) {
167 CFMutableStringRef copy
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, (CFStringRef
) value
);
168 CFStringTrimWhitespace(copy
);
169 CFSetSetValue(resultSet
, copy
);
175 CFSetRemoveValue(resultSet
, CFSTR("none"));
176 CFSetRemoveValue(resultSet
, CFSTR(""));
178 if (CFSetContainsValue(resultSet
, CFSTR("all"))) {
179 CFSetRemoveAllValues(resultSet
);
180 add_negate
= !add_negate
;
184 CFSetSetValue(resultSet
, kStringNegate
);
186 CFReleaseNull(commaArray
);
187 CFReleaseNull(allocated_scope_list
);
192 static CFMutableArrayRef
CFArrayCreateMutableForCFTypesFilledWithCFNull(CFAllocatorRef allocator
, CFIndex capacity
) {
193 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypesWithCapacity(kCFAllocatorDefault
, kScopeIDMax
);
195 for(int count
= 0; count
<= capacity
; ++count
)
196 CFArrayAppendValue(result
, kCFNull
);
201 static bool CFArrayIsAll(CFArrayRef array
, const void *value
)
203 return CFArrayGetCountOfValue(array
, CFRangeMake(0, CFArrayGetCount(array
)), value
) == CFArrayGetCount(array
);
206 static void SetNthScopeSet(int nth
, CFTypeRef collection
)
209 if (sLogSettings
== NULL
) {
210 sLogSettings
= CFArrayCreateMutableForCFTypesFilledWithCFNull(kCFAllocatorDefault
, kScopeIDMax
);
213 CFArraySetValueAtIndex(sLogSettings
, nth
, collection
);
215 if (CFArrayIsAll(sLogSettings
, kCFNull
)) {
216 CFReleaseNull(sLogSettings
);
221 static int string_to_log_level(CFStringRef string
) {
222 if (CFEqual(string
, CFSTR(ASL_STRING_EMERG
)))
223 return ASL_LEVEL_EMERG
;
224 else if (CFEqual(string
, CFSTR(ASL_STRING_ALERT
)))
225 return ASL_LEVEL_ALERT
;
226 else if (CFEqual(string
, CFSTR(ASL_STRING_CRIT
)))
227 return ASL_LEVEL_CRIT
;
228 else if (CFEqual(string
, CFSTR(ASL_STRING_ERR
)))
229 return ASL_LEVEL_ERR
;
230 else if (CFEqual(string
, CFSTR(ASL_STRING_WARNING
)))
231 return ASL_LEVEL_WARNING
;
232 else if (CFEqual(string
, CFSTR(ASL_STRING_NOTICE
)))
233 return ASL_LEVEL_NOTICE
;
234 else if (CFEqual(string
, CFSTR(ASL_STRING_INFO
)))
235 return ASL_LEVEL_INFO
;
236 else if (CFEqual(string
, CFSTR(ASL_STRING_DEBUG
)))
237 return ASL_LEVEL_DEBUG
;
242 static void CFSetAppendValues(CFSetRef set
, CFMutableArrayRef appendTo
)
244 CFSetForEach(set
, ^(const void *value
) {
245 CFArrayAppendValue(appendTo
, value
);
249 static CFMutableArrayRef
CFSetOfCFObjectsCopyValues(CFSetRef setOfCFs
)
251 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
253 CFSetForEach(setOfCFs
, ^(const void *value
) {
254 CFArrayAppendValue(result
, value
);
260 CFPropertyListRef
CopyCurrentScopePlist(void)
262 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
264 CFArrayForEach(sLogSettings
, ^(const void *value
) {
266 CFArrayRef values
= CFSetOfCFObjectsCopyValues((CFSetRef
) value
);
267 CFArrayAppendValue(result
, values
);
268 CFReleaseNull(values
);
269 } else if (isDictionary(value
)) {
270 CFMutableDictionaryRef levels
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
272 CFDictionaryForEach((CFDictionaryRef
) value
, ^(const void *key
, const void *value
) {
274 CFArrayRef values
= CFSetOfCFObjectsCopyValues((CFSetRef
) value
);
275 CFDictionaryAddValue(levels
, key
, values
);
276 CFReleaseNull(values
);
280 CFArrayAppendValue(result
, levels
);
282 CFArrayAppendValue(result
, kCFNull
);
289 void ApplyScopeDictionaryForID(CFDictionaryRef scopeDictionary
, SecDebugScopeID whichID
)
291 CFMutableDictionaryRef dictionary_for_id
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
293 CFDictionaryForEach(scopeDictionary
, ^(const void *key
, const void *value
) {
294 CFSetRef scope_set
= NULL
;
295 CFNumberRef key_number
= NULL
;
297 int level
= string_to_log_level((CFStringRef
) key
);
300 key_number
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &level
);
301 } else if (isNumber(key
)) {
302 key_number
= CFRetainSafe(key
);
305 if (isString(value
)) {
306 scope_set
= CopyScopesFromScopeList(value
);
309 if (key_number
&& scope_set
)
310 CFDictionaryAddValue(dictionary_for_id
, key_number
, scope_set
);
312 CFReleaseNull(key_number
);
313 CFReleaseNull(scope_set
);
316 if (CFDictionaryGetCount(dictionary_for_id
) > 0) {
317 SetNthScopeSet(whichID
, dictionary_for_id
);
320 CFReleaseNull(dictionary_for_id
);
323 void ApplyScopeListForID(CFStringRef scopeList
, SecDebugScopeID whichID
)
325 CFMutableSetRef scopesToUse
= CopyScopesFromScopeList(scopeList
);
327 SetNthScopeSet(whichID
, scopesToUse
);
329 CFReleaseNull(scopesToUse
);
332 void ApplyScopeListForIDC(const char *scopeList
, SecDebugScopeID whichID
) {
333 CFStringRef scope_string
= CFStringCreateWithCStringNoCopy(kCFAllocatorDefault
, scopeList
, kCFStringEncodingUTF8
, kCFAllocatorNull
);
335 ApplyScopeListForID(scope_string
, whichID
);
337 CFReleaseNull(scope_string
);
340 #pragma mark - Log Handlers to catch log information
342 static CFMutableArrayRef sSecurityLogHandlers
;
346 * Instead of using CFPropertyListReadFromFile we use a
347 * CFPropertyListCreateWithStream directly
348 * here. CFPropertyListReadFromFile() uses
349 * CFURLCopyResourcePropertyForKey() andCF pulls in CoreServices for
350 * CFURLCopyResourcePropertyForKey() and that doesn't work in install
354 static CFPropertyListRef
355 CopyPlistFromFile(CFURLRef url
)
357 CFDictionaryRef d
= NULL
;
358 CFReadStreamRef s
= CFReadStreamCreateWithFile(kCFAllocatorDefault
, url
);
359 if (s
&& CFReadStreamOpen(s
)) {
360 d
= (CFDictionaryRef
)CFPropertyListCreateWithStream(kCFAllocatorDefault
, s
, 0, kCFPropertyListImmutable
, NULL
, NULL
);
367 static void ApplyScopeByTypeForID(CFPropertyListRef scopes
, SecDebugScopeID whichID
) {
368 if (isDictionary(scopes
)) {
369 ApplyScopeDictionaryForID(scopes
, whichID
);
370 } else if (isString(scopes
)) {
371 ApplyScopeListForID(scopes
, whichID
);
375 static void setup_config_settings() {
376 CFStringRef logFileName
;
378 logFileName
= CFSTR(".GlobalPreferences.plist");
380 logFileName
= CFSTR("com.apple.security.logging.plist");
382 CFURLRef prefURL
= SecCopyURLForFileInManagedPreferencesDirectory(logFileName
);
384 CFPropertyListRef plist
= CopyPlistFromFile(prefURL
);
386 ApplyScopeByTypeForID(CFDictionaryGetValue(plist
, CFSTR("SecLogging")), kScopeIDConfig
);
388 CFReleaseSafe(plist
);
390 CFReleaseSafe(prefURL
);
393 static void setup_defaults_settings() {
394 CFPropertyListRef scopes_value
= CFPreferencesCopyValue(CFSTR("Logging"), CFSTR("com.apple.security"), kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
396 ApplyScopeByTypeForID(scopes_value
, kScopeIDDefaults
);
398 CFReleaseSafe(scopes_value
);
401 static void setup_circle_defaults_settings() {
402 CFPropertyListRef scopes_value
= CFPreferencesCopyValue(CFSTR("Circle-Logging"), CFSTR("com.apple.security"), kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
404 ApplyScopeByTypeForID(scopes_value
, kScopeIDDefaults
);
406 CFReleaseSafe(scopes_value
);
409 static void setup_environment_scopes() {
410 const char *cur_scope
= getenv("DEBUGSCOPE");
411 if (cur_scope
== NULL
)
414 ApplyScopeListForIDC(cur_scope
, kScopeIDEnvironment
);
417 #define XPCSCOPESTRWANT "api,account,accountChange,circle,circleChange,circleCreat,flush,fresh,keygen,signing,talkwithkvs"
418 #define XPCSCOPESTRDONTWANT "-event,http,item,keytrace,lockassertions,otr_keysetup,securityd,server,serverxpc,session,sync,titc,transport,trust,updates,xpc"
419 static void setup_xpcdefault_scopes() {
421 CFDictionaryRef noticeLogging
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
422 CFSTR(ASL_STRING_NOTICE
), CFSTR(XPCSCOPESTRDONTWANT
), NULL
);
424 ApplyScopeDictionaryForID(noticeLogging
, kScopeIDXPC
);
426 CFReleaseNull(noticeLogging
);
429 void __security_debug_init(void) {
430 static dispatch_once_t sdOnceToken
;
432 dispatch_once(&sdOnceToken
, ^{
433 setup_environment_scopes();
434 setup_config_settings();
435 setup_defaults_settings();
436 //setup_xpcdefault_scopes();
437 setup_circle_defaults_settings();
441 // MARK: Log handler recording (e.g. grabbing security logging and sending it to test results).
442 static void clean_aslclient(void *client
)
447 static aslclient
get_aslclient()
449 static dispatch_once_t once
;
450 static pthread_key_t asl_client_key
;
451 dispatch_once(&once
, ^{
452 pthread_key_create(&asl_client_key
, clean_aslclient
);
454 aslclient client
= pthread_getspecific(asl_client_key
);
456 client
= asl_open(NULL
, "SecLogging", 0);
457 asl_set_filter(client
, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG
));
458 pthread_setspecific(asl_client_key
, client
);
464 static CFMutableArrayRef
get_log_handlers()
466 static dispatch_once_t handlers_once
;
468 dispatch_once(&handlers_once
, ^{
469 sSecurityLogHandlers
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
471 CFArrayAppendValue(sSecurityLogHandlers
, ^(int level
, CFStringRef scope
, const char *function
,
472 const char *file
, int line
, CFStringRef message
){
473 CFStringRef logStr
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@ %s %@\n"), scope
? scope
: CFSTR(""), function
, message
);
474 CFStringPerformWithCString(logStr
, ^(const char *logMsg
) {
475 aslmsg msg
= asl_new(ASL_TYPE_MSG
);
477 CFStringPerformWithCString(scope
, ^(const char *scopeStr
) {
478 asl_set(msg
, ASL_KEY_FACILITY
, scopeStr
);
481 asl_log(get_aslclient(), msg
, level
, "%s", logMsg
);
484 CFReleaseSafe(logStr
);
488 return sSecurityLogHandlers
;
491 static void log_api_trace_v(const char *api
, const char *caller_info
, CFStringRef format
, va_list args
)
493 aslmsg msg
= asl_new(ASL_TYPE_MSG
);
494 asl_set(msg
, ASL_KEY_LEVEL
, ASL_STRING_DEBUG
);
495 CFStringPerformWithCString(kAPIScope
, ^(const char *scopeStr
) {
496 asl_set(msg
, ASL_KEY_FACILITY
, scopeStr
);
498 asl_set(msg
, "SecAPITrace", api
);
499 asl_set(msg
, caller_info
? "ENTER" : "RETURN", "");
502 CFStringRef message
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, NULL
, format
, args
);
503 CFStringPerformWithCString(message
, ^(const char *utf8Str
) {
504 asl_set(msg
, ASL_KEY_MSG
, utf8Str
);
506 CFReleaseSafe(message
);
510 asl_set(msg
, "CALLER", caller_info
);
513 asl_send(get_aslclient(), msg
);
517 void __security_trace_enter_api(const char *api
, CFStringRef format
, ...)
519 if (!IsScopeActive(ASL_LEVEL_DEBUG
, kAPIScope
))
523 va_start(args
, format
);
528 snprintf(stack_info
, sizeof(stack_info
), "C%p F%p", __builtin_return_address(1), __builtin_frame_address(2));
530 log_api_trace_v(api
, stack_info
, format
, args
);
536 void __security_trace_return_api(const char *api
, CFStringRef format
, ...)
538 if (!IsScopeActive(ASL_LEVEL_DEBUG
, kAPIScope
))
542 va_start(args
, format
);
544 log_api_trace_v(api
, NULL
, format
, args
);
550 void add_security_log_handler(security_log_handler handler
)
552 CFArrayAppendValue(get_log_handlers(), handler
);
555 void remove_security_log_handler(security_log_handler handler
)
557 CFArrayRemoveAllValue(get_log_handlers(), handler
);
560 static void __security_post_msg(int level
, CFStringRef scope
, const char *function
,
561 const char *file
, int line
, CFStringRef message
)
563 CFArrayForEach(get_log_handlers(), ^(const void *value
) {
564 security_log_handler handler
= (security_log_handler
) value
;
566 handler(level
, scope
, function
, file
, line
, message
);
571 static void __security_log_msg_v(int level
, CFStringRef scope
, const char *function
,
572 const char *file
, int line
, CFStringRef format
, va_list args
)
574 __security_debug_init();
576 if (!IsScopeActive(level
, scope
))
579 CFStringRef message
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, NULL
, format
, args
);
580 __security_post_msg(level
, scope
, function
, file
, line
, message
);
585 void __security_debug(CFStringRef scope
, const char *function
,
586 const char *file
, int line
, CFStringRef format
, ...)
589 va_start(args
, format
);
591 __security_log_msg_v(ASL_LEVEL_DEBUG
, scope
, function
, file
, line
, format
, args
);
596 static void __os_log_shim(void *addr
, int32_t level
, CFStringRef format
, va_list in_args
) {
597 if ((level
& 0x7) == level
) {
599 va_copy(args
, in_args
);
600 os_log_shim_with_CFString(addr
, OS_LOG_DEFAULT
, _os_trace_type_map
[level
], format
, args
, NULL
);
605 void __security_log(int level
, CFStringRef scope
, const char *function
,
606 const char *file
, int line
, CFStringRef format
, ...)
609 va_start(args
, format
);
611 __os_log_shim(__builtin_return_address(0), level
, format
, args
);
613 if (os_log_shim_legacy_logging_enabled()) {
614 __security_log_msg_v(level
, scope
, function
, file
, line
, format
, args
);