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 char *api_trace
= "api_trace";
51 const CFStringRef kStringNegate
= CFSTR("-");
52 const CFStringRef kStringAll
= CFSTR("all");
54 const CFStringRef kAPIScope
= CFSTR("api");
56 static CFMutableArrayRef sLogSettings
= NULL
; /* Either sets or dictionaries of level => set. */
58 static dispatch_queue_t
GetDispatchControlQueue(void) {
59 static dispatch_queue_t sLoggingScopeControlQueue
;
60 static dispatch_once_t onceToken
;
61 dispatch_once(&onceToken
, ^{
62 sLoggingScopeControlQueue
= dispatch_queue_create("security scope control", DISPATCH_QUEUE_CONCURRENT
);
64 return sLoggingScopeControlQueue
;
67 static void with_scopes_read(dispatch_block_t action
) {
68 dispatch_sync(GetDispatchControlQueue(), action
);
71 static void with_scopes_write(dispatch_block_t action
) {
72 dispatch_barrier_sync(GetDispatchControlQueue(), action
);
75 bool IsScopeActive(int level
, CFStringRef scope
)
80 CFNumberRef level_number
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &level
);
82 __block
bool isActive
= false;
85 CFArrayForEach(sLogSettings
, ^(const void *value
) {
86 CFSetRef setToCheck
= NULL
;
89 setToCheck
= (CFSetRef
) value
;
90 } else if (isDictionary(value
)) {
91 CFDictionaryRef levels
= (CFDictionaryRef
) value
;
93 setToCheck
= CFDictionaryGetValue(levels
, level_number
);
95 if (!isSet(setToCheck
))
99 if (setToCheck
!= NULL
&& !isActive
) {
100 bool negated
= CFSetContainsValue(setToCheck
, kStringNegate
);
101 bool inSet
= CFSetContainsValue(setToCheck
, scope
);
103 isActive
= negated
^ inSet
;
109 CFReleaseNull(level_number
);
114 bool IsScopeActiveC(int level
, const char *scope
)
116 CFStringRef scopeString
= CFStringCreateWithBytes(kCFAllocatorDefault
, (const uint8_t*)scope
, strlen(scope
), kCFStringEncodingUTF8
, false);
117 bool isActive
= IsScopeActive(level
, scopeString
);
118 CFReleaseNull(scopeString
);
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 SECLOG_LEVEL_EMERG
;
199 else if (CFEqual(string
, CFSTR(ASL_STRING_ALERT
)))
200 return SECLOG_LEVEL_ALERT
;
201 else if (CFEqual(string
, CFSTR(ASL_STRING_CRIT
)))
202 return SECLOG_LEVEL_CRIT
;
203 else if (CFEqual(string
, CFSTR(ASL_STRING_ERR
)))
204 return SECLOG_LEVEL_ERR
;
205 else if (CFEqual(string
, CFSTR(ASL_STRING_WARNING
)))
206 return SECLOG_LEVEL_WARNING
;
207 else if (CFEqual(string
, CFSTR(ASL_STRING_NOTICE
)))
208 return SECLOG_LEVEL_NOTICE
;
209 else if (CFEqual(string
, CFSTR(ASL_STRING_INFO
)))
210 return SECLOG_LEVEL_INFO
;
211 else if (CFEqual(string
, CFSTR(ASL_STRING_DEBUG
)))
212 return SECLOG_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
319 * Instead of using CFPropertyListReadFromFile we use a
320 * CFPropertyListCreateWithStream directly
321 * here. CFPropertyListReadFromFile() uses
322 * CFURLCopyResourcePropertyForKey() andCF pulls in CoreServices for
323 * CFURLCopyResourcePropertyForKey() and that doesn't work in install
327 static CFPropertyListRef
328 CopyPlistFromFile(CFURLRef url
)
330 CFDictionaryRef d
= NULL
;
331 CFReadStreamRef s
= CFReadStreamCreateWithFile(kCFAllocatorDefault
, url
);
332 if (s
&& CFReadStreamOpen(s
)) {
333 d
= (CFDictionaryRef
)CFPropertyListCreateWithStream(kCFAllocatorDefault
, s
, 0, kCFPropertyListImmutable
, NULL
, NULL
);
340 static void ApplyScopeByTypeForID(CFPropertyListRef scopes
, SecDebugScopeID whichID
) {
341 if (isDictionary(scopes
)) {
342 ApplyScopeDictionaryForID(scopes
, whichID
);
343 } else if (isString(scopes
)) {
344 ApplyScopeListForID(scopes
, whichID
);
348 static void setup_config_settings() {
349 CFStringRef logFileName
;
351 logFileName
= CFSTR(".GlobalPreferences.plist");
353 logFileName
= CFSTR("com.apple.security.logging.plist");
355 CFURLRef prefURL
= SecCopyURLForFileInManagedPreferencesDirectory(logFileName
);
357 CFPropertyListRef plist
= CopyPlistFromFile(prefURL
);
359 ApplyScopeByTypeForID(CFDictionaryGetValue(plist
, CFSTR("SecLogging")), kScopeIDConfig
);
361 CFReleaseSafe(plist
);
363 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_circle_defaults_settings() {
375 CFPropertyListRef scopes_value
= CFPreferencesCopyValue(CFSTR("Circle-Logging"), CFSTR("com.apple.security"), kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
377 ApplyScopeByTypeForID(scopes_value
, kScopeIDDefaults
);
379 CFReleaseSafe(scopes_value
);
382 static void setup_environment_scopes() {
383 const char *cur_scope
= getenv("DEBUGSCOPE");
384 if (cur_scope
== NULL
)
387 ApplyScopeListForIDC(cur_scope
, kScopeIDEnvironment
);
391 void __security_debug_init(void) {
392 static dispatch_once_t sdOnceToken
;
394 dispatch_once(&sdOnceToken
, ^{
395 setup_environment_scopes();
396 setup_config_settings();
397 setup_defaults_settings();
398 setup_circle_defaults_settings();
405 static char *copyScopeStr(CFStringRef scope
, char *alternative
) {
406 char *scopeStr
= NULL
;
408 scopeStr
= CFStringToCString(scope
);
410 scopeStr
= strdup("noScope");
415 static os_log_t
logObjForCFScope(CFStringRef scope
) {
416 static dispatch_once_t onceToken
= 0;
417 __block os_log_t retval
= OS_LOG_DISABLED
;
418 static dispatch_queue_t logObjectQueue
= NULL
;
419 static CFMutableDictionaryRef scopeMap
= NULL
;
421 if(scope
== NULL
) scope
= CFSTR("logging");
423 dispatch_once(&onceToken
, ^{
424 logObjectQueue
= dispatch_queue_create("logObjectQueue", DISPATCH_QUEUE_SERIAL
);
425 scopeMap
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFCopyStringDictionaryKeyCallBacks
, NULL
);
428 dispatch_sync(logObjectQueue
, ^{
429 retval
= (os_log_t
) CFDictionaryGetValue(scopeMap
, scope
);
432 CFStringPerformWithCString(scope
, ^(const char *scopeStr
) {
433 CFDictionaryAddValue(scopeMap
, scope
, os_log_create("com.apple.securityd", scopeStr
));
435 retval
= (os_log_t
) CFDictionaryGetValue(scopeMap
, scope
);
441 static bool loggingEnabled
= true;
443 bool secLogEnabled(void) {
444 return loggingEnabled
;
446 void secLogDisable(void) {
447 loggingEnabled
= false;
450 void secLogEnable(void) {
451 loggingEnabled
= true;
455 os_log_t
logObjForScope(const char *scope
) {
457 return OS_LOG_DISABLED
;
458 CFStringRef cfscope
= NULL
;
459 if(scope
) cfscope
= CFStringCreateWithCString(kCFAllocatorDefault
, scope
, kCFStringEncodingASCII
);
460 os_log_t retval
= logObjForCFScope(cfscope
);
461 CFReleaseNull(cfscope
);
467 CFStringRef
SecLogAPICreate(bool apiIn
, const char *api
, CFStringRef format
, ... ) {
468 CFMutableStringRef outStr
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
470 char *direction
= apiIn
? "ENTER" : "RETURN";
472 va_start(args
, format
);
474 CFStringAppend(outStr
, CFSTR("SecAPITrace "));
475 CFStringAppendCString(outStr
, api
, kCFStringEncodingASCII
);
476 CFStringAppendCString(outStr
, direction
, kCFStringEncodingASCII
);
479 CFStringRef message
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, NULL
, format
, args
);
480 CFStringAppend(outStr
, message
);
481 CFReleaseSafe(message
);
485 char caller_info
[80];
486 snprintf(caller_info
, sizeof(caller_info
), "C%p F%p", __builtin_return_address(1), __builtin_frame_address(2));
487 CFStringAppend(outStr
, CFSTR("CALLER "));
488 CFStringAppendCString(outStr
, caller_info
, kCFStringEncodingASCII
);
497 // Functions for weak-linking os_log functions
500 #define weak_log_f(fname, newname, rettype, fallthrough) \
501 rettype newname(log_args) { \
502 static dispatch_once_t onceToken = 0; \
503 static rettype (*newname)(log_args) = NULL; \
505 dispatch_once(&onceToken, ^{ \
506 void* libtrace = dlopen("/usr/lib/system/libsystem_trace.dylib", RTLD_LAZY | RTLD_LOCAL); \
508 newname = (rettype(*)(log_args)) dlsym(libtrace, #fname); \
513 return newname(log_argnames); \
518 #define log_args void *dso, os_log_t log, os_log_type_t type, const char *format, uint8_t *buf, unsigned int size
519 #define log_argnames dso, log, type, format, buf, size
520 weak_log_f(_os_log_impl
, weak_os_log_impl
, void, return);
524 #define log_args const char *subsystem, const char *category
525 #define log_argnames subsystem, category
526 weak_log_f(os_log_create
, weak_os_log_create
, os_log_t
, return NULL
);
530 #define log_args os_log_t oslog, os_log_type_t type
531 #define log_argnames oslog, type
532 weak_log_f(os_log_type_enabled
, weak_os_log_type_enabled
, bool, return false);
539 #endif // TARGET_OS_OSX