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 <CoreFoundation/CFSet.h> 32#include <CoreFoundation/CFString.h> 33#include <CoreFoundation/CFPreferences.h> 34 35#include <dispatch/dispatch.h> 36 37#include <stdarg.h> 38#include <stdlib.h> 39#include <stdio.h> 40#include <string.h> 41#include <pthread.h> 42#include <asl.h> 43 44 45const CFStringRef kStringNegate = CFSTR("-"); 46const CFStringRef kStringAll = CFSTR("all"); 47 48const CFStringRef kAPIScope = CFSTR("api"); 49 50static CFMutableArrayRef sLogSettings = NULL; /* Either sets or dictionaries of level => set. */ 51 52static 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); 57 }); 58 return sLoggingScopeControlQueue; 59} 60 61static void with_scopes_read(dispatch_block_t action) { 62 dispatch_sync(GetDispatchControlQueue(), action); 63} 64 65static void with_scopes_write(dispatch_block_t action) { 66 dispatch_barrier_sync(GetDispatchControlQueue(), action); 67} 68 69bool IsScopeActive(int level, CFStringRef scope) 70{ 71 if (scope == NULL) 72 return true; 73 74 CFNumberRef level_number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &level); 75 76 __block bool isActive = false; 77 with_scopes_read(^{ 78 if (sLogSettings) { 79 CFArrayForEach(sLogSettings, ^(const void *value) { 80 CFSetRef setToCheck = NULL; 81 82 if (isSet(value)) { 83 setToCheck = (CFSetRef) value; 84 } else if (isDictionary(value)) { 85 CFDictionaryRef levels = (CFDictionaryRef) value; 86 87 setToCheck = CFDictionaryGetValue(levels, level_number); 88 89 if (!isSet(setToCheck)) 90 setToCheck = NULL; 91 } 92 93 if (setToCheck != NULL && !isActive) { 94 bool negated = CFSetContainsValue(setToCheck, kStringNegate); 95 bool inSet = CFSetContainsValue(setToCheck, scope); 96 97 isActive = negated ^ inSet; 98 } 99 }); 100 } 101 }); 102 103 CFReleaseNull(level_number); 104 105 return isActive; 106} 107 108bool IsScopeActiveC(int level, const char *scope) 109{ 110 CFStringRef scopeString = CFStringCreateWithBytes(kCFAllocatorDefault, (const uint8_t*)scope, strlen(scope), kCFStringEncodingUTF8, false); 111 bool isActive = IsScopeActive(level, scopeString); 112 CFReleaseNull(scopeString); 113 114 return isActive; 115} 116 117 118 119static CFStringRef copyScopeName(const char *scope, CFIndex scopeLen) { 120 return CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8 *)scope, 121 scopeLen, kCFStringEncodingUTF8, false); 122} 123 124static CFMutableSetRef CopyScopesFromScopeList(CFStringRef scopes) { 125 CFMutableSetRef resultSet = CFSetCreateMutableForCFTypes(kCFAllocatorDefault); 126 127 CFStringRef allocated_scope_list = NULL; 128 CFStringRef clean_scope_list = scopes; 129 bool add_negate = false; 130 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; 134 add_negate = true; 135 } 136 137 CFArrayRef commaArray = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, clean_scope_list, CFSTR(",")); 138 139 if (commaArray) { 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); 145 CFReleaseNull(copy); 146 } 147 }); 148 } 149 150 CFSetRemoveValue(resultSet, CFSTR("none")); 151 CFSetRemoveValue(resultSet, CFSTR("")); 152 153 if (CFSetContainsValue(resultSet, CFSTR("all"))) { 154 CFSetRemoveAllValues(resultSet); 155 add_negate = !add_negate; 156 } 157 158 if (add_negate) 159 CFSetSetValue(resultSet, kStringNegate); 160 161 CFReleaseNull(commaArray); 162 CFReleaseNull(allocated_scope_list); 163 164 return resultSet; 165} 166 167static CFMutableArrayRef CFArrayCreateMutableForCFTypesFilledWithCFNull(CFAllocatorRef allocator, CFIndex capacity) { 168 CFMutableArrayRef result = CFArrayCreateMutableForCFTypesWithCapacity(kCFAllocatorDefault, kScopeIDMax); 169 170 for(int count = 0; count <= capacity; ++count) 171 CFArrayAppendValue(result, kCFNull); 172 173 return result; 174} 175 176static bool CFArrayIsAll(CFArrayRef array, const void *value) 177{ 178 return CFArrayGetCountOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), value) == CFArrayGetCount(array); 179} 180 181static void SetNthScopeSet(int nth, CFTypeRef collection) 182{ 183 with_scopes_write(^{ 184 if (sLogSettings == NULL) { 185 sLogSettings = CFArrayCreateMutableForCFTypesFilledWithCFNull(kCFAllocatorDefault, kScopeIDMax); 186 } 187 188 CFArraySetValueAtIndex(sLogSettings, nth, collection); 189 190 if (CFArrayIsAll(sLogSettings, kCFNull)) { 191 CFReleaseNull(sLogSettings); 192 } 193 }); 194} 195 196static 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; 213 else 214 return -1; 215} 216 217static void CFSetAppendValues(CFSetRef set, CFMutableArrayRef appendTo) 218{ 219 CFSetForEach(set, ^(const void *value) { 220 CFArrayAppendValue(appendTo, value); 221 }); 222} 223 224static CFMutableArrayRef CFSetOfCFObjectsCopyValues(CFSetRef setOfCFs) 225{ 226 CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 227 228 CFSetForEach(setOfCFs, ^(const void *value) { 229 CFArrayAppendValue(result, value); 230 }); 231 232 return result; 233} 234 235CFPropertyListRef CopyCurrentScopePlist(void) 236{ 237 CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 238 with_scopes_read(^{ 239 CFArrayForEach(sLogSettings, ^(const void *value) { 240 if (isSet(value)) { 241 CFArrayRef values = CFSetOfCFObjectsCopyValues((CFSetRef) value); 242 CFArrayAppendValue(result, values); 243 CFReleaseNull(values); 244 } else if (isDictionary(value)) { 245 CFMutableDictionaryRef levels = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 246 247 CFDictionaryForEach((CFDictionaryRef) value, ^(const void *key, const void *value) { 248 if (isSet(value)) { 249 CFArrayRef values = CFSetOfCFObjectsCopyValues((CFSetRef) value); 250 CFDictionaryAddValue(levels, key, values); 251 CFReleaseNull(values); 252 } 253 }); 254 255 CFArrayAppendValue(result, levels); 256 } else { 257 CFArrayAppendValue(result, kCFNull); 258 } 259 }); 260 }); 261 return result; 262} 263 264void ApplyScopeDictionaryForID(CFDictionaryRef scopeDictionary, SecDebugScopeID whichID) 265{ 266 CFMutableDictionaryRef dictionary_for_id = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 267 268 CFDictionaryForEach(scopeDictionary, ^(const void *key, const void *value) { 269 CFSetRef scope_set = NULL; 270 CFNumberRef key_number = NULL; 271 if (isString(key)) { 272 int level = string_to_log_level((CFStringRef) key); 273 274 if (level >= 0) 275 key_number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &level); 276 } else if (isNumber(key)) { 277 key_number = CFRetainSafe(key); 278 } 279 280 if (isString(value)) { 281 scope_set = CopyScopesFromScopeList(value); 282 } 283 284 if (key_number && scope_set) 285 CFDictionaryAddValue(dictionary_for_id, key_number, scope_set); 286 287 CFReleaseNull(key_number); 288 CFReleaseNull(scope_set); 289 }); 290 291 if (CFDictionaryGetCount(dictionary_for_id) > 0) { 292 SetNthScopeSet(whichID, dictionary_for_id); 293 } 294 295 CFReleaseNull(dictionary_for_id); 296} 297 298void ApplyScopeListForID(CFStringRef scopeList, SecDebugScopeID whichID) 299{ 300 CFMutableSetRef scopesToUse = CopyScopesFromScopeList(scopeList); 301 302 SetNthScopeSet(whichID, scopesToUse); 303 304 CFReleaseNull(scopesToUse); 305} 306 307void ApplyScopeListForIDC(const char *scopeList, SecDebugScopeID whichID) { 308 CFStringRef scope_string = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, scopeList, kCFStringEncodingUTF8, kCFAllocatorNull); 309 310 ApplyScopeListForID(scope_string, whichID); 311 312 CFReleaseNull(scope_string); 313} 314 315#pragma mark - Log Handlers to catch log information 316 317static CFMutableArrayRef sSecurityLogHandlers; 318 319#if TARGET_OS_IPHONE 320 321/* 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 327 * enviroment. 328 */ 329 330static CFPropertyListRef 331CopyPlistFromFile(CFURLRef url) 332{ 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); 337 } 338 CFReleaseSafe(s); 339 340 return d; 341} 342#endif 343 344static void ApplyScopeByTypeForID(CFPropertyListRef scopes, SecDebugScopeID whichID) { 345 if (isDictionary(scopes)) { 346 ApplyScopeDictionaryForID(scopes, whichID); 347 } else if (isString(scopes)) { 348 ApplyScopeListForID(scopes, whichID); 349 } 350} 351 352static void setup_config_settings() { 353#if TARGET_OS_IPHONE 354 CFURLRef prefURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR("/Library/Managed Preferences/mobile/.GlobalPreferences.plist"), kCFURLPOSIXPathStyle, false); 355 if(prefURL) { 356 CFPropertyListRef plist = CopyPlistFromFile(prefURL); 357 if (plist) { 358 ApplyScopeByTypeForID(CFDictionaryGetValue(plist, CFSTR("SecLogging")), kScopeIDConfig); 359 } 360 CFReleaseSafe(plist); 361 } 362 CFReleaseSafe(prefURL); 363#endif 364} 365 366static void setup_defaults_settings() { 367 CFPropertyListRef scopes_value = CFPreferencesCopyValue(CFSTR("Logging"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost); 368 369 ApplyScopeByTypeForID(scopes_value, kScopeIDDefaults); 370 371 CFReleaseSafe(scopes_value); 372} 373 374static void setup_environment_scopes() { 375 const char *cur_scope = getenv("DEBUGSCOPE"); 376 if (cur_scope == NULL) 377 cur_scope = ""; 378 379 ApplyScopeListForIDC(cur_scope, kScopeIDEnvironment); 380} 381 382void __security_debug_init(void) { 383 static dispatch_once_t sdOnceToken; 384 385 dispatch_once(&sdOnceToken, ^{ 386 setup_environment_scopes(); 387 setup_config_settings(); 388 setup_defaults_settings(); 389 }); 390} 391 392 393// MARK: Log handler recording (e.g. grabbing security logging and sending it to test results). 394static void clean_aslclient(void *client) 395{ 396 asl_close(client); 397} 398 399static aslclient get_aslclient() 400{ 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); 405 }); 406 aslclient client = pthread_getspecific(asl_client_key); 407 if (!client) { 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); 411 } 412 413 return client; 414} 415 416static CFMutableArrayRef get_log_handlers() 417{ 418 static dispatch_once_t handlers_once; 419 420 dispatch_once(&handlers_once, ^{ 421 sSecurityLogHandlers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 422 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); 428 if (scope) { 429 CFStringPerformWithCString(scope, ^(const char *scopeStr) { 430 asl_set(msg, ASL_KEY_FACILITY, scopeStr); 431 }); 432 } 433 asl_log(get_aslclient(), msg, level, "%s", logMsg); 434 asl_free(msg); 435 }); 436 CFReleaseSafe(logStr); 437 }); 438 }); 439 440 return sSecurityLogHandlers; 441} 442 443static void log_api_trace_v(const char *api, const char *caller_info, CFStringRef format, va_list args) 444{ 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); 449 }); 450 asl_set(msg, "SecAPITrace", api); 451 asl_set(msg, caller_info ? "ENTER" : "RETURN", ""); 452 453 if (format) { 454 CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args); 455 CFStringPerformWithCString(message, ^(const char *utf8Str) { 456 asl_set(msg, ASL_KEY_MSG, utf8Str); 457 }); 458 CFReleaseSafe(message); 459 } 460 461 if (caller_info) { 462 asl_set(msg, "CALLER", caller_info); 463 } 464 465 asl_send(get_aslclient(), msg); 466 asl_free(msg); 467} 468 469void __security_trace_enter_api(const char *api, CFStringRef format, ...) 470{ 471 if (!IsScopeActive(ASL_LEVEL_DEBUG, kAPIScope)) 472 return; 473 474 va_list args; 475 va_start(args, format); 476 477 { 478 char stack_info[80]; 479 480 snprintf(stack_info, sizeof(stack_info), "C%p F%p", __builtin_return_address(1), __builtin_frame_address(2)); 481 482 log_api_trace_v(api, stack_info, format, args); 483 } 484 485 va_end(args); 486} 487 488void __security_trace_return_api(const char *api, CFStringRef format, ...) 489{ 490 if (!IsScopeActive(ASL_LEVEL_DEBUG, kAPIScope)) 491 return; 492 493 va_list args; 494 va_start(args, format); 495 496 log_api_trace_v(api, NULL, format, args); 497 498 va_end(args); 499} 500 501 502void add_security_log_handler(security_log_handler handler) 503{ 504 CFArrayAppendValue(get_log_handlers(), handler); 505} 506 507void remove_security_log_handler(security_log_handler handler) 508{ 509 CFArrayRemoveAllValue(get_log_handlers(), handler); 510} 511 512static void __security_post_msg(int level, CFStringRef scope, const char *function, 513 const char *file, int line, CFStringRef message) 514{ 515 CFArrayForEach(get_log_handlers(), ^(const void *value) { 516 security_log_handler handler = (security_log_handler) value; 517 518 handler(level, scope, function, file, line, message); 519 }); 520} 521 522static void __security_log_msg_v(int level, CFStringRef scope, const char *function, 523 const char *file, int line, CFStringRef format, va_list args) 524{ 525 __security_debug_init(); 526 527 if (!IsScopeActive(level, scope)) 528 return; 529 530 CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args); 531 __security_post_msg(level, scope, function, file, line, message); 532 CFRelease(message); 533 534} 535 536void __security_debug(CFStringRef scope, const char *function, 537 const char *file, int line, CFStringRef format, ...) 538{ 539 va_list args; 540 va_start(args, format); 541 542 __security_log_msg_v(ASL_LEVEL_DEBUG, scope, function, file, line, format, args); 543 544 va_end(args); 545} 546 547void __security_log(int level, CFStringRef scope, const char *function, 548 const char *file, int line, CFStringRef format, ...) 549{ 550 va_list args; 551 va_start(args, format); 552 553 __security_log_msg_v(level, scope, function, file, line, format, args); 554 555 va_end(args); 556} 557