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 <CoreFoundation/CFSet.h>
32 #include <CoreFoundation/CFString.h>
33 #include <CoreFoundation/CFPreferences.h>
35 #include <dispatch/dispatch.h>
45 const CFStringRef kStringNegate
= CFSTR("-");
46 const CFStringRef kStringAll
= CFSTR("all");
48 const CFStringRef kAPIScope
= CFSTR("api");
50 static CFMutableArrayRef sLogSettings
= NULL
; /* Either sets or dictionaries of level => set. */
52 static dispatch_queue_t
GetDispatchControlQueue(void) {
53 static dispatch_queue_t sLoggingScopeControlQueue
;
54 static dispatch_once_t onceToken
;
55 dispatch_once(&onceToken
, ^{
56 sLoggingScopeControlQueue
= dispatch_queue_create("security scope control", DISPATCH_QUEUE_CONCURRENT
);
58 return sLoggingScopeControlQueue
;
61 static void with_scopes_read(dispatch_block_t action
) {
62 dispatch_sync(GetDispatchControlQueue(), action
);
65 static void with_scopes_write(dispatch_block_t action
) {
66 dispatch_barrier_sync(GetDispatchControlQueue(), action
);
69 bool IsScopeActive(int level
, CFStringRef scope
)
74 CFNumberRef level_number
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &level
);
76 __block
bool isActive
= false;
79 CFArrayForEach(sLogSettings
, ^(const void *value
) {
80 CFSetRef setToCheck
= NULL
;
83 setToCheck
= (CFSetRef
) value
;
84 } else if (isDictionary(value
)) {
85 CFDictionaryRef levels
= (CFDictionaryRef
) value
;
87 setToCheck
= CFDictionaryGetValue(levels
, level_number
);
89 if (!isSet(setToCheck
))
93 if (setToCheck
!= NULL
&& !isActive
) {
94 bool negated
= CFSetContainsValue(setToCheck
, kStringNegate
);
95 bool inSet
= CFSetContainsValue(setToCheck
, scope
);
97 isActive
= negated
^ inSet
;
103 CFReleaseNull(level_number
);
108 bool IsScopeActiveC(int level
, const char *scope
)
110 CFStringRef scopeString
= CFStringCreateWithBytes(kCFAllocatorDefault
, (const uint8_t*)scope
, strlen(scope
), kCFStringEncodingUTF8
, false);
111 bool isActive
= IsScopeActive(level
, scopeString
);
112 CFReleaseNull(scopeString
);
119 static CFStringRef
copyScopeName(const char *scope
, CFIndex scopeLen
) {
120 return CFStringCreateWithBytes(kCFAllocatorDefault
, (const UInt8
*)scope
,
121 scopeLen
, kCFStringEncodingUTF8
, false);
124 static CFMutableSetRef
CopyScopesFromScopeList(CFStringRef scopes
) {
125 CFMutableSetRef resultSet
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
127 CFStringRef allocated_scope_list
= NULL
;
128 CFStringRef clean_scope_list
= scopes
;
129 bool add_negate
= false;
131 if (CFStringHasPrefix(scopes
, kStringNegate
)) {
132 allocated_scope_list
= CFStringCreateWithSubstring(kCFAllocatorDefault
, scopes
, CFRangeMake(CFStringGetLength(kStringNegate
), CFStringGetLength(scopes
) - 1));
133 clean_scope_list
= allocated_scope_list
;
137 CFArrayRef commaArray
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault
, clean_scope_list
, CFSTR(","));
140 CFArrayForEach(commaArray
, ^(const void *value
) {
141 if (isString(value
)) {
142 CFMutableStringRef copy
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, (CFStringRef
) value
);
143 CFStringTrimWhitespace(copy
);
144 CFSetSetValue(resultSet
, copy
);
150 CFSetRemoveValue(resultSet
, CFSTR("none"));
151 CFSetRemoveValue(resultSet
, CFSTR(""));
153 if (CFSetContainsValue(resultSet
, CFSTR("all"))) {
154 CFSetRemoveAllValues(resultSet
);
155 add_negate
= !add_negate
;
159 CFSetSetValue(resultSet
, kStringNegate
);
161 CFReleaseNull(commaArray
);
162 CFReleaseNull(allocated_scope_list
);
167 static CFMutableArrayRef
CFArrayCreateMutableForCFTypesFilledWithCFNull(CFAllocatorRef allocator
, CFIndex capacity
) {
168 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypesWithCapacity(kCFAllocatorDefault
, kScopeIDMax
);
170 for(int count
= 0; count
<= capacity
; ++count
)
171 CFArrayAppendValue(result
, kCFNull
);
176 static bool CFArrayIsAll(CFArrayRef array
, const void *value
)
178 return CFArrayGetCountOfValue(array
, CFRangeMake(0, CFArrayGetCount(array
)), value
) == CFArrayGetCount(array
);
181 static void SetNthScopeSet(int nth
, CFTypeRef collection
)
184 if (sLogSettings
== NULL
) {
185 sLogSettings
= CFArrayCreateMutableForCFTypesFilledWithCFNull(kCFAllocatorDefault
, kScopeIDMax
);
188 CFArraySetValueAtIndex(sLogSettings
, nth
, collection
);
190 if (CFArrayIsAll(sLogSettings
, kCFNull
)) {
191 CFReleaseNull(sLogSettings
);
196 static int string_to_log_level(CFStringRef string
) {
197 if (CFEqual(string
, CFSTR(ASL_STRING_EMERG
)))
198 return ASL_LEVEL_EMERG
;
199 else if (CFEqual(string
, CFSTR(ASL_STRING_ALERT
)))
200 return ASL_LEVEL_ALERT
;
201 else if (CFEqual(string
, CFSTR(ASL_STRING_CRIT
)))
202 return ASL_LEVEL_CRIT
;
203 else if (CFEqual(string
, CFSTR(ASL_STRING_ERR
)))
204 return ASL_LEVEL_ERR
;
205 else if (CFEqual(string
, CFSTR(ASL_STRING_WARNING
)))
206 return ASL_LEVEL_WARNING
;
207 else if (CFEqual(string
, CFSTR(ASL_STRING_NOTICE
)))
208 return ASL_LEVEL_NOTICE
;
209 else if (CFEqual(string
, CFSTR(ASL_STRING_INFO
)))
210 return ASL_LEVEL_INFO
;
211 else if (CFEqual(string
, CFSTR(ASL_STRING_DEBUG
)))
212 return ASL_LEVEL_DEBUG
;
217 static void CFSetAppendValues(CFSetRef set
, CFMutableArrayRef appendTo
)
219 CFSetForEach(set
, ^(const void *value
) {
220 CFArrayAppendValue(appendTo
, value
);
224 static CFMutableArrayRef
CFSetOfCFObjectsCopyValues(CFSetRef setOfCFs
)
226 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
228 CFSetForEach(setOfCFs
, ^(const void *value
) {
229 CFArrayAppendValue(result
, value
);
235 CFPropertyListRef
CopyCurrentScopePlist(void)
237 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
239 CFArrayForEach(sLogSettings
, ^(const void *value
) {
241 CFArrayRef values
= CFSetOfCFObjectsCopyValues((CFSetRef
) value
);
242 CFArrayAppendValue(result
, values
);
243 CFReleaseNull(values
);
244 } else if (isDictionary(value
)) {
245 CFMutableDictionaryRef levels
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
247 CFDictionaryForEach((CFDictionaryRef
) value
, ^(const void *key
, const void *value
) {
249 CFArrayRef values
= CFSetOfCFObjectsCopyValues((CFSetRef
) value
);
250 CFDictionaryAddValue(levels
, key
, values
);
251 CFReleaseNull(values
);
255 CFArrayAppendValue(result
, levels
);
257 CFArrayAppendValue(result
, kCFNull
);
264 void ApplyScopeDictionaryForID(CFDictionaryRef scopeDictionary
, SecDebugScopeID whichID
)
266 CFMutableDictionaryRef dictionary_for_id
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
268 CFDictionaryForEach(scopeDictionary
, ^(const void *key
, const void *value
) {
269 CFSetRef scope_set
= NULL
;
270 CFNumberRef key_number
= NULL
;
272 int level
= string_to_log_level((CFStringRef
) key
);
275 key_number
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &level
);
276 } else if (isNumber(key
)) {
277 key_number
= CFRetainSafe(key
);
280 if (isString(value
)) {
281 scope_set
= CopyScopesFromScopeList(value
);
284 if (key_number
&& scope_set
)
285 CFDictionaryAddValue(dictionary_for_id
, key_number
, scope_set
);
287 CFReleaseNull(key_number
);
288 CFReleaseNull(scope_set
);
291 if (CFDictionaryGetCount(dictionary_for_id
) > 0) {
292 SetNthScopeSet(whichID
, dictionary_for_id
);
295 CFReleaseNull(dictionary_for_id
);
298 void ApplyScopeListForID(CFStringRef scopeList
, SecDebugScopeID whichID
)
300 CFMutableSetRef scopesToUse
= CopyScopesFromScopeList(scopeList
);
302 SetNthScopeSet(whichID
, scopesToUse
);
304 CFReleaseNull(scopesToUse
);
307 void ApplyScopeListForIDC(const char *scopeList
, SecDebugScopeID whichID
) {
308 CFStringRef scope_string
= CFStringCreateWithCStringNoCopy(kCFAllocatorDefault
, scopeList
, kCFStringEncodingUTF8
, kCFAllocatorNull
);
310 ApplyScopeListForID(scope_string
, whichID
);
312 CFReleaseNull(scope_string
);
315 #pragma mark - Log Handlers to catch log information
317 static CFMutableArrayRef sSecurityLogHandlers
;
322 * Instead of using CFPropertyListReadFromFile we use a
323 * CFPropertyListCreateWithStream directly
324 * here. CFPropertyListReadFromFile() uses
325 * CFURLCopyResourcePropertyForKey() andCF pulls in CoreServices for
326 * CFURLCopyResourcePropertyForKey() and that doesn't work in install
330 static CFPropertyListRef
331 CopyPlistFromFile(CFURLRef url
)
333 CFDictionaryRef d
= NULL
;
334 CFReadStreamRef s
= CFReadStreamCreateWithFile(kCFAllocatorDefault
, url
);
335 if (s
&& CFReadStreamOpen(s
)) {
336 d
= (CFDictionaryRef
)CFPropertyListCreateWithStream(kCFAllocatorDefault
, s
, 0, kCFPropertyListImmutable
, NULL
, NULL
);
344 static void ApplyScopeByTypeForID(CFPropertyListRef scopes
, SecDebugScopeID whichID
) {
345 if (isDictionary(scopes
)) {
346 ApplyScopeDictionaryForID(scopes
, whichID
);
347 } else if (isString(scopes
)) {
348 ApplyScopeListForID(scopes
, whichID
);
352 static void setup_config_settings() {
354 CFURLRef prefURL
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
, CFSTR("/Library/Managed Preferences/mobile/.GlobalPreferences.plist"), kCFURLPOSIXPathStyle
, false);
356 CFPropertyListRef plist
= CopyPlistFromFile(prefURL
);
358 ApplyScopeByTypeForID(CFDictionaryGetValue(plist
, CFSTR("SecLogging")), kScopeIDConfig
);
360 CFReleaseSafe(plist
);
362 CFReleaseSafe(prefURL
);
366 static void setup_defaults_settings() {
367 CFPropertyListRef scopes_value
= CFPreferencesCopyValue(CFSTR("Logging"), CFSTR("com.apple.security"), kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
369 ApplyScopeByTypeForID(scopes_value
, kScopeIDDefaults
);
371 CFReleaseSafe(scopes_value
);
374 static void setup_environment_scopes() {
375 const char *cur_scope
= getenv("DEBUGSCOPE");
376 if (cur_scope
== NULL
)
379 ApplyScopeListForIDC(cur_scope
, kScopeIDEnvironment
);
382 void __security_debug_init(void) {
383 static dispatch_once_t sdOnceToken
;
385 dispatch_once(&sdOnceToken
, ^{
386 setup_environment_scopes();
387 setup_config_settings();
388 setup_defaults_settings();
393 // MARK: Log handler recording (e.g. grabbing security logging and sending it to test results).
394 static void clean_aslclient(void *client
)
399 static aslclient
get_aslclient()
401 static dispatch_once_t once
;
402 static pthread_key_t asl_client_key
;
403 dispatch_once(&once
, ^{
404 pthread_key_create(&asl_client_key
, clean_aslclient
);
406 aslclient client
= pthread_getspecific(asl_client_key
);
408 client
= asl_open(NULL
, "SecLogging", 0);
409 asl_set_filter(client
, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG
));
410 pthread_setspecific(asl_client_key
, client
);
416 static CFMutableArrayRef
get_log_handlers()
418 static dispatch_once_t handlers_once
;
420 dispatch_once(&handlers_once
, ^{
421 sSecurityLogHandlers
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
423 CFArrayAppendValue(sSecurityLogHandlers
, ^(int level
, CFStringRef scope
, const char *function
,
424 const char *file
, int line
, CFStringRef message
){
425 CFStringRef logStr
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@ %s %@\n"), scope
? scope
: CFSTR(""), function
, message
);
426 CFStringPerformWithCString(logStr
, ^(const char *logMsg
) {
427 aslmsg msg
= asl_new(ASL_TYPE_MSG
);
429 CFStringPerformWithCString(scope
, ^(const char *scopeStr
) {
430 asl_set(msg
, ASL_KEY_FACILITY
, scopeStr
);
433 asl_log(get_aslclient(), msg
, level
, "%s", logMsg
);
436 CFReleaseSafe(logStr
);
440 return sSecurityLogHandlers
;
443 static void log_api_trace_v(const char *api
, const char *caller_info
, CFStringRef format
, va_list args
)
445 aslmsg msg
= asl_new(ASL_TYPE_MSG
);
446 asl_set(msg
, ASL_KEY_LEVEL
, ASL_STRING_DEBUG
);
447 CFStringPerformWithCString(kAPIScope
, ^(const char *scopeStr
) {
448 asl_set(msg
, ASL_KEY_FACILITY
, scopeStr
);
450 asl_set(msg
, "SecAPITrace", api
);
451 asl_set(msg
, caller_info
? "ENTER" : "RETURN", "");
454 CFStringRef message
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, NULL
, format
, args
);
455 CFStringPerformWithCString(message
, ^(const char *utf8Str
) {
456 asl_set(msg
, ASL_KEY_MSG
, utf8Str
);
458 CFReleaseSafe(message
);
462 asl_set(msg
, "CALLER", caller_info
);
465 asl_send(get_aslclient(), msg
);
469 void __security_trace_enter_api(const char *api
, CFStringRef format
, ...)
471 if (!IsScopeActive(ASL_LEVEL_DEBUG
, kAPIScope
))
475 va_start(args
, format
);
480 snprintf(stack_info
, sizeof(stack_info
), "C%p F%p", __builtin_return_address(1), __builtin_frame_address(2));
482 log_api_trace_v(api
, stack_info
, format
, args
);
488 void __security_trace_return_api(const char *api
, CFStringRef format
, ...)
490 if (!IsScopeActive(ASL_LEVEL_DEBUG
, kAPIScope
))
494 va_start(args
, format
);
496 log_api_trace_v(api
, NULL
, format
, args
);
502 void add_security_log_handler(security_log_handler handler
)
504 CFArrayAppendValue(get_log_handlers(), handler
);
507 void remove_security_log_handler(security_log_handler handler
)
509 CFArrayRemoveAllValue(get_log_handlers(), handler
);
512 static void __security_post_msg(int level
, CFStringRef scope
, const char *function
,
513 const char *file
, int line
, CFStringRef message
)
515 CFArrayForEach(get_log_handlers(), ^(const void *value
) {
516 security_log_handler handler
= (security_log_handler
) value
;
518 handler(level
, scope
, function
, file
, line
, message
);
522 static void __security_log_msg_v(int level
, CFStringRef scope
, const char *function
,
523 const char *file
, int line
, CFStringRef format
, va_list args
)
525 __security_debug_init();
527 if (!IsScopeActive(level
, scope
))
530 CFStringRef message
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, NULL
, format
, args
);
531 __security_post_msg(level
, scope
, function
, file
, line
, message
);
536 void __security_debug(CFStringRef scope
, const char *function
,
537 const char *file
, int line
, CFStringRef format
, ...)
540 va_start(args
, format
);
542 __security_log_msg_v(ASL_LEVEL_DEBUG
, scope
, function
, file
, line
, format
, args
);
547 void __security_log(int level
, CFStringRef scope
, const char *function
,
548 const char *file
, int line
, CFStringRef format
, ...)
551 va_start(args
, format
);
553 __security_log_msg_v(level
, scope
, function
, file
, line
, format
, args
);