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; 
 442 static pthread_mutex_t loggingMutex 
= PTHREAD_MUTEX_INITIALIZER
; 
 444 bool secLogEnabled(void) { 
 446     pthread_mutex_lock(&loggingMutex
); 
 448     pthread_mutex_unlock(&loggingMutex
); 
 451 void secLogDisable(void) { 
 452     pthread_mutex_lock(&loggingMutex
); 
 453     loggingEnabled 
= false; 
 454     pthread_mutex_unlock(&loggingMutex
); 
 457 void secLogEnable(void) { 
 458     pthread_mutex_lock(&loggingMutex
); 
 459     loggingEnabled 
= true; 
 460     pthread_mutex_unlock(&loggingMutex
); 
 463 os_log_t 
logObjForScope(const char *scope
) 
 465     return secLogObjForScope(scope
); 
 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 
= logObjForCFScope(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
); 
 510 // Functions for weak-linking os_log functions 
 513 #define weak_log_f(fname, newname, rettype, fallthrough) \ 
 514   rettype newname(log_args) { \ 
 515     static dispatch_once_t onceToken = 0; \ 
 516     static rettype (*newname)(log_args) = NULL; \ 
 518     dispatch_once(&onceToken, ^{ \ 
 519         void* libtrace = dlopen("/usr/lib/system/libsystem_trace.dylib", RTLD_LAZY | RTLD_LOCAL); \ 
 521             newname = (rettype(*)(log_args)) dlsym(libtrace, #fname); \ 
 526         return newname(log_argnames); \ 
 531 #define log_args void *dso, os_log_t log, os_log_type_t type, const char *format, uint8_t *buf, unsigned int size 
 532 #define log_argnames dso, log, type, format, buf, size 
 533 weak_log_f(_os_log_impl
, weak_os_log_impl
, void, return); 
 537 #define log_args const char *subsystem, const char *category 
 538 #define log_argnames subsystem, category 
 539 weak_log_f(os_log_create
, weak_os_log_create
, os_log_t
, return NULL
); 
 543 #define log_args os_log_t oslog, os_log_type_t type 
 544 #define log_argnames oslog, type 
 545 weak_log_f(os_log_type_enabled
, weak_os_log_type_enabled
, bool, return false); 
 552 #endif // TARGET_OS_OSX