debugging.c
1 /* 2 * Copyright (c) 2006-2010,2012-2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 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 11 * file. 12 * 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. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25 /* 26 * debugging.c - non-trivial debug support 27 */ 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> 35 36 #include <dispatch/dispatch.h> 37 38 #include <stdarg.h> 39 #include <stdlib.h> 40 #include <stdio.h> 41 #include <string.h> 42 #include <pthread.h> 43 #include <asl.h> 44 #include <os/trace.h> 45 #include <os/log_private.h> 46 #include <sqlite3.h> 47 #include <os/lock_private.h> 48 49 const char *api_trace = "api_trace"; 50 51 52 const CFStringRef kStringNegate = CFSTR("-"); 53 const CFStringRef kStringAll = CFSTR("all"); 54 55 const CFStringRef kAPIScope = CFSTR("api"); 56 57 static CFMutableArrayRef sLogSettings = NULL; /* Either sets or dictionaries of level => set. */ 58 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); 64 }); 65 return sLoggingScopeControlQueue; 66 } 67 68 static void with_scopes_read(dispatch_block_t action) { 69 dispatch_sync(GetDispatchControlQueue(), action); 70 } 71 72 static void with_scopes_write(dispatch_block_t action) { 73 dispatch_barrier_sync(GetDispatchControlQueue(), action); 74 } 75 76 bool IsScopeActive(int level, CFStringRef scope) 77 { 78 if (scope == NULL) 79 return true; 80 81 CFNumberRef level_number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &level); 82 83 __block bool isActive = false; 84 with_scopes_read(^{ 85 if (sLogSettings) { 86 CFArrayForEach(sLogSettings, ^(const void *value) { 87 CFSetRef setToCheck = NULL; 88 89 if (isSet(value)) { 90 setToCheck = (CFSetRef) value; 91 } else if (isDictionary(value)) { 92 CFDictionaryRef levels = (CFDictionaryRef) value; 93 94 setToCheck = CFDictionaryGetValue(levels, level_number); 95 96 if (!isSet(setToCheck)) 97 setToCheck = NULL; 98 } 99 100 if (setToCheck != NULL && !isActive) { 101 bool negated = CFSetContainsValue(setToCheck, kStringNegate); 102 bool inSet = CFSetContainsValue(setToCheck, scope); 103 104 isActive = negated ^ inSet; 105 } 106 }); 107 } 108 }); 109 110 CFReleaseNull(level_number); 111 112 return isActive; 113 } 114 115 bool IsScopeActiveC(int level, const char *scope) 116 { 117 CFStringRef scopeString = CFStringCreateWithBytes(kCFAllocatorDefault, (const uint8_t*)scope, strlen(scope), kCFStringEncodingUTF8, false); 118 bool isActive = IsScopeActive(level, scopeString); 119 CFReleaseNull(scopeString); 120 121 return isActive; 122 } 123 124 125 static CFMutableSetRef CopyScopesFromScopeList(CFStringRef scopes) { 126 CFMutableSetRef resultSet = CFSetCreateMutableForCFTypes(kCFAllocatorDefault); 127 128 CFStringRef allocated_scope_list = NULL; 129 CFStringRef clean_scope_list = scopes; 130 bool add_negate = false; 131 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; 135 add_negate = true; 136 } 137 138 CFArrayRef commaArray = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, clean_scope_list, CFSTR(",")); 139 140 if (commaArray) { 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); 146 CFReleaseNull(copy); 147 } 148 }); 149 } 150 151 CFSetRemoveValue(resultSet, CFSTR("none")); 152 CFSetRemoveValue(resultSet, CFSTR("")); 153 154 if (CFSetContainsValue(resultSet, CFSTR("all"))) { 155 CFSetRemoveAllValues(resultSet); 156 add_negate = !add_negate; 157 } 158 159 if (add_negate) 160 CFSetSetValue(resultSet, kStringNegate); 161 162 CFReleaseNull(commaArray); 163 CFReleaseNull(allocated_scope_list); 164 165 return resultSet; 166 } 167 168 static CFMutableArrayRef CFArrayCreateMutableForCFTypesFilledWithCFNull(CFAllocatorRef allocator, CFIndex capacity) { 169 CFMutableArrayRef result = CFArrayCreateMutableForCFTypesWithCapacity(kCFAllocatorDefault, kScopeIDMax); 170 171 for(int count = 0; count <= capacity; ++count) 172 CFArrayAppendValue(result, kCFNull); 173 174 return result; 175 } 176 177 static bool CFArrayIsAll(CFArrayRef array, const void *value) 178 { 179 return CFArrayGetCountOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), value) == CFArrayGetCount(array); 180 } 181 182 static void SetNthScopeSet(int nth, CFTypeRef collection) 183 { 184 with_scopes_write(^{ 185 if (sLogSettings == NULL) { 186 sLogSettings = CFArrayCreateMutableForCFTypesFilledWithCFNull(kCFAllocatorDefault, kScopeIDMax); 187 } 188 189 CFArraySetValueAtIndex(sLogSettings, nth, collection); 190 191 if (CFArrayIsAll(sLogSettings, kCFNull)) { 192 CFReleaseNull(sLogSettings); 193 } 194 }); 195 } 196 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; 214 else 215 return -1; 216 } 217 218 static CFMutableArrayRef CFSetOfCFObjectsCopyValues(CFSetRef setOfCFs) 219 { 220 CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 221 222 CFSetForEach(setOfCFs, ^(const void *value) { 223 CFArrayAppendValue(result, value); 224 }); 225 226 return result; 227 } 228 229 CFPropertyListRef CopyCurrentScopePlist(void) 230 { 231 CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 232 with_scopes_read(^{ 233 CFArrayForEach(sLogSettings, ^(const void *value) { 234 if (isSet(value)) { 235 CFArrayRef values = CFSetOfCFObjectsCopyValues((CFSetRef) value); 236 CFArrayAppendValue(result, values); 237 CFReleaseNull(values); 238 } else if (isDictionary(value)) { 239 CFMutableDictionaryRef levels = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 240 241 CFDictionaryForEach((CFDictionaryRef) value, ^(const void *key, const void *value) { 242 if (isSet(value)) { 243 CFArrayRef values = CFSetOfCFObjectsCopyValues((CFSetRef) value); 244 CFDictionaryAddValue(levels, key, values); 245 CFReleaseNull(values); 246 } 247 }); 248 249 CFArrayAppendValue(result, levels); 250 } else { 251 CFArrayAppendValue(result, kCFNull); 252 } 253 }); 254 }); 255 return result; 256 } 257 258 void ApplyScopeDictionaryForID(CFDictionaryRef scopeDictionary, SecDebugScopeID whichID) 259 { 260 CFMutableDictionaryRef dictionary_for_id = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 261 262 CFDictionaryForEach(scopeDictionary, ^(const void *key, const void *value) { 263 CFSetRef scope_set = NULL; 264 CFNumberRef key_number = NULL; 265 if (isString(key)) { 266 int level = string_to_log_level((CFStringRef) key); 267 268 if (level >= 0) 269 key_number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &level); 270 } else if (isNumber(key)) { 271 key_number = CFRetainSafe(key); 272 } 273 274 if (isString(value)) { 275 scope_set = CopyScopesFromScopeList(value); 276 } 277 278 if (key_number && scope_set) 279 CFDictionaryAddValue(dictionary_for_id, key_number, scope_set); 280 281 CFReleaseNull(key_number); 282 CFReleaseNull(scope_set); 283 }); 284 285 if (CFDictionaryGetCount(dictionary_for_id) > 0) { 286 SetNthScopeSet(whichID, dictionary_for_id); 287 } 288 289 CFReleaseNull(dictionary_for_id); 290 } 291 292 void ApplyScopeListForID(CFStringRef scopeList, SecDebugScopeID whichID) 293 { 294 CFMutableSetRef scopesToUse = CopyScopesFromScopeList(scopeList); 295 296 SetNthScopeSet(whichID, scopesToUse); 297 298 CFReleaseNull(scopesToUse); 299 } 300 301 void ApplyScopeListForIDC(const char *scopeList, SecDebugScopeID whichID) { 302 CFStringRef scope_string = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, scopeList, kCFStringEncodingUTF8, kCFAllocatorNull); 303 304 ApplyScopeListForID(scope_string, whichID); 305 306 CFReleaseNull(scope_string); 307 } 308 309 #pragma mark - Log Handlers to catch log information 310 311 312 /* 313 * Instead of using CFPropertyListReadFromFile we use a 314 * CFPropertyListCreateWithStream directly 315 * here. CFPropertyListReadFromFile() uses 316 * CFURLCopyResourcePropertyForKey() andCF pulls in CoreServices for 317 * CFURLCopyResourcePropertyForKey() and that doesn't work in install 318 * environment. 319 */ 320 321 static CFPropertyListRef 322 CopyPlistFromFile(CFURLRef url) 323 { 324 CFDictionaryRef d = NULL; 325 CFReadStreamRef s = CFReadStreamCreateWithFile(kCFAllocatorDefault, url); 326 if (s && CFReadStreamOpen(s)) { 327 d = (CFDictionaryRef)CFPropertyListCreateWithStream(kCFAllocatorDefault, s, 0, kCFPropertyListImmutable, NULL, NULL); 328 } 329 CFReleaseSafe(s); 330 331 return d; 332 } 333 334 static void ApplyScopeByTypeForID(CFPropertyListRef scopes, SecDebugScopeID whichID) { 335 if (isDictionary(scopes)) { 336 ApplyScopeDictionaryForID(scopes, whichID); 337 } else if (isString(scopes)) { 338 ApplyScopeListForID(scopes, whichID); 339 } 340 } 341 342 static void setup_config_settings() { 343 CFStringRef logFileName; 344 #if TARGET_OS_IPHONE 345 logFileName = CFSTR(".GlobalPreferences.plist"); 346 #else 347 logFileName = CFSTR("com.apple.security.logging.plist"); 348 #endif 349 CFURLRef prefURL = SecCopyURLForFileInManagedPreferencesDirectory(logFileName); 350 if(prefURL) { 351 CFPropertyListRef plist = CopyPlistFromFile(prefURL); 352 if (plist) { 353 ApplyScopeByTypeForID(CFDictionaryGetValue(plist, CFSTR("SecLogging")), kScopeIDConfig); 354 } 355 CFReleaseSafe(plist); 356 } 357 CFReleaseSafe(prefURL); 358 } 359 360 static void setup_defaults_settings() { 361 CFPropertyListRef scopes_value = CFPreferencesCopyValue(CFSTR("Logging"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost); 362 363 ApplyScopeByTypeForID(scopes_value, kScopeIDDefaults); 364 365 CFReleaseSafe(scopes_value); 366 } 367 368 static void setup_circle_defaults_settings() { 369 CFPropertyListRef scopes_value = CFPreferencesCopyValue(CFSTR("Circle-Logging"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost); 370 371 ApplyScopeByTypeForID(scopes_value, kScopeIDDefaults); 372 373 CFReleaseSafe(scopes_value); 374 } 375 376 static void setup_environment_scopes() { 377 const char *cur_scope = getenv("DEBUGSCOPE"); 378 if (cur_scope == NULL) 379 cur_scope = ""; 380 381 ApplyScopeListForIDC(cur_scope, kScopeIDEnvironment); 382 } 383 384 385 void __security_debug_init(void) { 386 static dispatch_once_t sdOnceToken; 387 388 dispatch_once(&sdOnceToken, ^{ 389 setup_environment_scopes(); 390 setup_config_settings(); 391 setup_defaults_settings(); 392 setup_circle_defaults_settings(); 393 }); 394 } 395 396 397 os_log_t 398 secLogObjForCFScope(CFStringRef scope) 399 { 400 os_log_t retval = OS_LOG_DISABLED; 401 static os_unfair_lock lock = OS_UNFAIR_LOCK_INIT; 402 static CFMutableDictionaryRef scopeMap = NULL; 403 404 if (scope == NULL) { 405 scope = CFSTR("logging"); 406 } 407 408 os_unfair_lock_lock_with_options(&lock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); 409 410 if (scopeMap == NULL) { 411 scopeMap = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, NULL); 412 } 413 414 retval = (os_log_t)CFDictionaryGetValue(scopeMap, scope); 415 if (retval == NULL) { 416 CFStringPerformWithCString(scope, ^(const char *scopeStr) { 417 CFDictionaryAddValue(scopeMap, scope, os_log_create("com.apple.securityd", scopeStr)); 418 }); 419 retval = (os_log_t)CFDictionaryGetValue(scopeMap, scope); 420 } 421 422 os_unfair_lock_unlock(&lock); 423 424 return retval; 425 } 426 427 static bool loggingEnabled = true; 428 static pthread_mutex_t loggingMutex = PTHREAD_MUTEX_INITIALIZER; 429 430 bool secLogEnabled(void) { 431 bool r = false; 432 pthread_mutex_lock(&loggingMutex); 433 r = loggingEnabled; 434 pthread_mutex_unlock(&loggingMutex); 435 return r; 436 } 437 void secLogDisable(void) { 438 pthread_mutex_lock(&loggingMutex); 439 loggingEnabled = false; 440 pthread_mutex_unlock(&loggingMutex); 441 } 442 443 void secLogEnable(void) { 444 pthread_mutex_lock(&loggingMutex); 445 loggingEnabled = true; 446 pthread_mutex_unlock(&loggingMutex); 447 } 448 449 os_log_t secLogObjForScope(const char *scope) { 450 if (!secLogEnabled()) 451 return OS_LOG_DISABLED; 452 CFStringRef cfscope = NULL; 453 if(scope) cfscope = CFStringCreateWithCString(kCFAllocatorDefault, scope, kCFStringEncodingASCII); 454 os_log_t retval = secLogObjForCFScope(cfscope); 455 CFReleaseNull(cfscope); 456 return retval; 457 } 458 459 460 461 CFStringRef SecLogAPICreate(bool apiIn, const char *api, CFStringRef format, ... ) { 462 CFMutableStringRef outStr = CFStringCreateMutable(kCFAllocatorDefault, 0); 463 464 char *direction = apiIn ? "ENTER" : "RETURN"; 465 va_list args; 466 va_start(args, format); 467 468 CFStringAppend(outStr, CFSTR("SecAPITrace ")); 469 CFStringAppendCString(outStr, api, kCFStringEncodingASCII); 470 CFStringAppendCString(outStr, direction, kCFStringEncodingASCII); 471 472 if (format) { 473 CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args); 474 CFStringAppend(outStr, message); 475 CFReleaseSafe(message); 476 } 477 478 if (apiIn) { 479 char caller_info[80]; 480 snprintf(caller_info, sizeof(caller_info), "C%p F%p", __builtin_return_address(1), __builtin_frame_address(2)); 481 CFStringAppend(outStr, CFSTR("CALLER ")); 482 CFStringAppendCString(outStr, caller_info, kCFStringEncodingASCII); 483 } 484 va_end(args); 485 486 return outStr; 487 }