]> git.saurik.com Git - apple/security.git/blob - OSX/utilities/src/debugging.c
Security-57337.40.85.tar.gz
[apple/security.git] / OSX / utilities / src / 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
48 const uint8_t _os_trace_type_map[8] = {
49 OS_TRACE_TYPE_FAULT, // ASL_LEVEL_EMERG
50 OS_TRACE_TYPE_FAULT, // ASL_LEVEL_ALERT
51 OS_TRACE_TYPE_FAULT, // ASL_LEVEL_CRIT
52 OS_TRACE_TYPE_ERROR, // ASL_LEVEL_ERR
53 OS_TRACE_TYPE_RELEASE, // ASL_LEVEL_WARNING
54 OS_TRACE_TYPE_RELEASE, // ASL_LEVEL_NOTICE
55 OS_TRACE_TYPE_RELEASE, // ASL_LEVEL_INFO
56 OS_TRACE_TYPE_DEBUG // ASL_LEVEL_DEBUG
57 };
58
59 const char *_asl_string_map[8] = {
60 ASL_STRING_EMERG, // ASL_LEVEL_EMERG
61 ASL_STRING_ALERT, // ASL_LEVEL_ALERT
62 ASL_STRING_CRIT, // ASL_LEVEL_CRIT
63 ASL_STRING_ERR, // ASL_LEVEL_ERR
64 ASL_STRING_WARNING, // ASL_LEVEL_WARNING
65 ASL_STRING_NOTICE, // ASL_LEVEL_NOTICE
66 ASL_STRING_INFO, // ASL_LEVEL_INFO
67 ASL_STRING_DEBUG // ASL_LEVEL_DEBUG
68 };
69
70 const CFStringRef kStringNegate = CFSTR("-");
71 const CFStringRef kStringAll = CFSTR("all");
72
73 const CFStringRef kAPIScope = CFSTR("api");
74
75 static CFMutableArrayRef sLogSettings = NULL; /* Either sets or dictionaries of level => set. */
76
77 static dispatch_queue_t GetDispatchControlQueue(void) {
78 static dispatch_queue_t sLoggingScopeControlQueue;
79 static dispatch_once_t onceToken;
80 dispatch_once(&onceToken, ^{
81 sLoggingScopeControlQueue = dispatch_queue_create("security scope control", DISPATCH_QUEUE_CONCURRENT);
82 });
83 return sLoggingScopeControlQueue;
84 }
85
86 static void with_scopes_read(dispatch_block_t action) {
87 dispatch_sync(GetDispatchControlQueue(), action);
88 }
89
90 static void with_scopes_write(dispatch_block_t action) {
91 dispatch_barrier_sync(GetDispatchControlQueue(), action);
92 }
93
94 bool IsScopeActive(int level, CFStringRef scope)
95 {
96 if (scope == NULL)
97 return true;
98
99 CFNumberRef level_number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &level);
100
101 __block bool isActive = false;
102 with_scopes_read(^{
103 if (sLogSettings) {
104 CFArrayForEach(sLogSettings, ^(const void *value) {
105 CFSetRef setToCheck = NULL;
106
107 if (isSet(value)) {
108 setToCheck = (CFSetRef) value;
109 } else if (isDictionary(value)) {
110 CFDictionaryRef levels = (CFDictionaryRef) value;
111
112 setToCheck = CFDictionaryGetValue(levels, level_number);
113
114 if (!isSet(setToCheck))
115 setToCheck = NULL;
116 }
117
118 if (setToCheck != NULL && !isActive) {
119 bool negated = CFSetContainsValue(setToCheck, kStringNegate);
120 bool inSet = CFSetContainsValue(setToCheck, scope);
121
122 isActive = negated ^ inSet;
123 }
124 });
125 }
126 });
127
128 CFReleaseNull(level_number);
129
130 return isActive;
131 }
132
133 bool IsScopeActiveC(int level, const char *scope)
134 {
135 CFStringRef scopeString = CFStringCreateWithBytes(kCFAllocatorDefault, (const uint8_t*)scope, strlen(scope), kCFStringEncodingUTF8, false);
136 bool isActive = IsScopeActive(level, scopeString);
137 CFReleaseNull(scopeString);
138
139 return isActive;
140 }
141
142
143
144 static CFStringRef copyScopeName(const char *scope, CFIndex scopeLen) {
145 return CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8 *)scope,
146 scopeLen, kCFStringEncodingUTF8, false);
147 }
148
149 static CFMutableSetRef CopyScopesFromScopeList(CFStringRef scopes) {
150 CFMutableSetRef resultSet = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
151
152 CFStringRef allocated_scope_list = NULL;
153 CFStringRef clean_scope_list = scopes;
154 bool add_negate = false;
155
156 if (CFStringHasPrefix(scopes, kStringNegate)) {
157 allocated_scope_list = CFStringCreateWithSubstring(kCFAllocatorDefault, scopes, CFRangeMake(CFStringGetLength(kStringNegate), CFStringGetLength(scopes) - 1));
158 clean_scope_list = allocated_scope_list;
159 add_negate = true;
160 }
161
162 CFArrayRef commaArray = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, clean_scope_list, CFSTR(","));
163
164 if (commaArray) {
165 CFArrayForEach(commaArray, ^(const void *value) {
166 if (isString(value)) {
167 CFMutableStringRef copy = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, (CFStringRef) value);
168 CFStringTrimWhitespace(copy);
169 CFSetSetValue(resultSet, copy);
170 CFReleaseNull(copy);
171 }
172 });
173 }
174
175 CFSetRemoveValue(resultSet, CFSTR("none"));
176 CFSetRemoveValue(resultSet, CFSTR(""));
177
178 if (CFSetContainsValue(resultSet, CFSTR("all"))) {
179 CFSetRemoveAllValues(resultSet);
180 add_negate = !add_negate;
181 }
182
183 if (add_negate)
184 CFSetSetValue(resultSet, kStringNegate);
185
186 CFReleaseNull(commaArray);
187 CFReleaseNull(allocated_scope_list);
188
189 return resultSet;
190 }
191
192 static CFMutableArrayRef CFArrayCreateMutableForCFTypesFilledWithCFNull(CFAllocatorRef allocator, CFIndex capacity) {
193 CFMutableArrayRef result = CFArrayCreateMutableForCFTypesWithCapacity(kCFAllocatorDefault, kScopeIDMax);
194
195 for(int count = 0; count <= capacity; ++count)
196 CFArrayAppendValue(result, kCFNull);
197
198 return result;
199 }
200
201 static bool CFArrayIsAll(CFArrayRef array, const void *value)
202 {
203 return CFArrayGetCountOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), value) == CFArrayGetCount(array);
204 }
205
206 static void SetNthScopeSet(int nth, CFTypeRef collection)
207 {
208 with_scopes_write(^{
209 if (sLogSettings == NULL) {
210 sLogSettings = CFArrayCreateMutableForCFTypesFilledWithCFNull(kCFAllocatorDefault, kScopeIDMax);
211 }
212
213 CFArraySetValueAtIndex(sLogSettings, nth, collection);
214
215 if (CFArrayIsAll(sLogSettings, kCFNull)) {
216 CFReleaseNull(sLogSettings);
217 }
218 });
219 }
220
221 static int string_to_log_level(CFStringRef string) {
222 if (CFEqual(string, CFSTR(ASL_STRING_EMERG)))
223 return ASL_LEVEL_EMERG;
224 else if (CFEqual(string, CFSTR(ASL_STRING_ALERT)))
225 return ASL_LEVEL_ALERT;
226 else if (CFEqual(string, CFSTR(ASL_STRING_CRIT)))
227 return ASL_LEVEL_CRIT;
228 else if (CFEqual(string, CFSTR(ASL_STRING_ERR)))
229 return ASL_LEVEL_ERR;
230 else if (CFEqual(string, CFSTR(ASL_STRING_WARNING)))
231 return ASL_LEVEL_WARNING;
232 else if (CFEqual(string, CFSTR(ASL_STRING_NOTICE)))
233 return ASL_LEVEL_NOTICE;
234 else if (CFEqual(string, CFSTR(ASL_STRING_INFO)))
235 return ASL_LEVEL_INFO;
236 else if (CFEqual(string, CFSTR(ASL_STRING_DEBUG)))
237 return ASL_LEVEL_DEBUG;
238 else
239 return -1;
240 }
241
242 static void CFSetAppendValues(CFSetRef set, CFMutableArrayRef appendTo)
243 {
244 CFSetForEach(set, ^(const void *value) {
245 CFArrayAppendValue(appendTo, value);
246 });
247 }
248
249 static CFMutableArrayRef CFSetOfCFObjectsCopyValues(CFSetRef setOfCFs)
250 {
251 CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
252
253 CFSetForEach(setOfCFs, ^(const void *value) {
254 CFArrayAppendValue(result, value);
255 });
256
257 return result;
258 }
259
260 CFPropertyListRef CopyCurrentScopePlist(void)
261 {
262 CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
263 with_scopes_read(^{
264 CFArrayForEach(sLogSettings, ^(const void *value) {
265 if (isSet(value)) {
266 CFArrayRef values = CFSetOfCFObjectsCopyValues((CFSetRef) value);
267 CFArrayAppendValue(result, values);
268 CFReleaseNull(values);
269 } else if (isDictionary(value)) {
270 CFMutableDictionaryRef levels = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
271
272 CFDictionaryForEach((CFDictionaryRef) value, ^(const void *key, const void *value) {
273 if (isSet(value)) {
274 CFArrayRef values = CFSetOfCFObjectsCopyValues((CFSetRef) value);
275 CFDictionaryAddValue(levels, key, values);
276 CFReleaseNull(values);
277 }
278 });
279
280 CFArrayAppendValue(result, levels);
281 } else {
282 CFArrayAppendValue(result, kCFNull);
283 }
284 });
285 });
286 return result;
287 }
288
289 void ApplyScopeDictionaryForID(CFDictionaryRef scopeDictionary, SecDebugScopeID whichID)
290 {
291 CFMutableDictionaryRef dictionary_for_id = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
292
293 CFDictionaryForEach(scopeDictionary, ^(const void *key, const void *value) {
294 CFSetRef scope_set = NULL;
295 CFNumberRef key_number = NULL;
296 if (isString(key)) {
297 int level = string_to_log_level((CFStringRef) key);
298
299 if (level >= 0)
300 key_number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &level);
301 } else if (isNumber(key)) {
302 key_number = CFRetainSafe(key);
303 }
304
305 if (isString(value)) {
306 scope_set = CopyScopesFromScopeList(value);
307 }
308
309 if (key_number && scope_set)
310 CFDictionaryAddValue(dictionary_for_id, key_number, scope_set);
311
312 CFReleaseNull(key_number);
313 CFReleaseNull(scope_set);
314 });
315
316 if (CFDictionaryGetCount(dictionary_for_id) > 0) {
317 SetNthScopeSet(whichID, dictionary_for_id);
318 }
319
320 CFReleaseNull(dictionary_for_id);
321 }
322
323 void ApplyScopeListForID(CFStringRef scopeList, SecDebugScopeID whichID)
324 {
325 CFMutableSetRef scopesToUse = CopyScopesFromScopeList(scopeList);
326
327 SetNthScopeSet(whichID, scopesToUse);
328
329 CFReleaseNull(scopesToUse);
330 }
331
332 void ApplyScopeListForIDC(const char *scopeList, SecDebugScopeID whichID) {
333 CFStringRef scope_string = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, scopeList, kCFStringEncodingUTF8, kCFAllocatorNull);
334
335 ApplyScopeListForID(scope_string, whichID);
336
337 CFReleaseNull(scope_string);
338 }
339
340 #pragma mark - Log Handlers to catch log information
341
342 static CFMutableArrayRef sSecurityLogHandlers;
343
344
345 /*
346 * Instead of using CFPropertyListReadFromFile we use a
347 * CFPropertyListCreateWithStream directly
348 * here. CFPropertyListReadFromFile() uses
349 * CFURLCopyResourcePropertyForKey() andCF pulls in CoreServices for
350 * CFURLCopyResourcePropertyForKey() and that doesn't work in install
351 * environment.
352 */
353
354 static CFPropertyListRef
355 CopyPlistFromFile(CFURLRef url)
356 {
357 CFDictionaryRef d = NULL;
358 CFReadStreamRef s = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
359 if (s && CFReadStreamOpen(s)) {
360 d = (CFDictionaryRef)CFPropertyListCreateWithStream(kCFAllocatorDefault, s, 0, kCFPropertyListImmutable, NULL, NULL);
361 }
362 CFReleaseSafe(s);
363
364 return d;
365 }
366
367 static void ApplyScopeByTypeForID(CFPropertyListRef scopes, SecDebugScopeID whichID) {
368 if (isDictionary(scopes)) {
369 ApplyScopeDictionaryForID(scopes, whichID);
370 } else if (isString(scopes)) {
371 ApplyScopeListForID(scopes, whichID);
372 }
373 }
374
375 static void setup_config_settings() {
376 CFStringRef logFileName;
377 #if TARGET_OS_IPHONE
378 logFileName = CFSTR(".GlobalPreferences.plist");
379 #else
380 logFileName = CFSTR("com.apple.security.logging.plist");
381 #endif
382 CFURLRef prefURL = SecCopyURLForFileInManagedPreferencesDirectory(logFileName);
383 if(prefURL) {
384 CFPropertyListRef plist = CopyPlistFromFile(prefURL);
385 if (plist) {
386 ApplyScopeByTypeForID(CFDictionaryGetValue(plist, CFSTR("SecLogging")), kScopeIDConfig);
387 }
388 CFReleaseSafe(plist);
389 }
390 CFReleaseSafe(prefURL);
391 }
392
393 static void setup_defaults_settings() {
394 CFPropertyListRef scopes_value = CFPreferencesCopyValue(CFSTR("Logging"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
395
396 ApplyScopeByTypeForID(scopes_value, kScopeIDDefaults);
397
398 CFReleaseSafe(scopes_value);
399 }
400
401 static void setup_circle_defaults_settings() {
402 CFPropertyListRef scopes_value = CFPreferencesCopyValue(CFSTR("Circle-Logging"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
403
404 ApplyScopeByTypeForID(scopes_value, kScopeIDDefaults);
405
406 CFReleaseSafe(scopes_value);
407 }
408
409 static void setup_environment_scopes() {
410 const char *cur_scope = getenv("DEBUGSCOPE");
411 if (cur_scope == NULL)
412 cur_scope = "";
413
414 ApplyScopeListForIDC(cur_scope, kScopeIDEnvironment);
415 }
416
417 #define XPCSCOPESTRWANT "api,account,accountChange,circle,circleChange,circleCreat,flush,fresh,keygen,signing,talkwithkvs,syncbubble"
418 #define XPCSCOPESTRDONTWANT "-event,http,item,keytrace,lockassertions,otr_keysetup,securityd,server,serverxpc,session,sync,titc,transport,trust,updates,xpc"
419 static void setup_xpcdefault_scopes() {
420
421 CFDictionaryRef noticeLogging = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
422 CFSTR(ASL_STRING_NOTICE), CFSTR(XPCSCOPESTRDONTWANT), NULL);
423
424 ApplyScopeDictionaryForID(noticeLogging, kScopeIDXPC);
425
426 CFReleaseNull(noticeLogging);
427 }
428
429 void __security_debug_init(void) {
430 static dispatch_once_t sdOnceToken;
431
432 dispatch_once(&sdOnceToken, ^{
433 setup_environment_scopes();
434 setup_config_settings();
435 setup_defaults_settings();
436 //setup_xpcdefault_scopes();
437 setup_circle_defaults_settings();
438 });
439 }
440
441 // MARK: Log handler recording (e.g. grabbing security logging and sending it to test results).
442 static void clean_aslclient(void *client)
443 {
444 asl_close(client);
445 }
446
447 static aslclient get_aslclient()
448 {
449 static dispatch_once_t once;
450 static pthread_key_t asl_client_key;
451 dispatch_once(&once, ^{
452 pthread_key_create(&asl_client_key, clean_aslclient);
453 });
454 aslclient client = pthread_getspecific(asl_client_key);
455 if (!client) {
456 client = asl_open(NULL, "SecLogging", 0);
457 asl_set_filter(client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
458 pthread_setspecific(asl_client_key, client);
459 }
460
461 return client;
462 }
463
464 static CFMutableArrayRef get_log_handlers()
465 {
466 static dispatch_once_t handlers_once;
467
468 dispatch_once(&handlers_once, ^{
469 sSecurityLogHandlers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
470
471 CFArrayAppendValue(sSecurityLogHandlers, ^(int level, CFStringRef scope, const char *function,
472 const char *file, int line, CFStringRef message){
473 CFStringRef logStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ %s %@\n"), scope ? scope : CFSTR(""), function, message);
474 CFStringPerformWithCString(logStr, ^(const char *logMsg) {
475 aslmsg msg = asl_new(ASL_TYPE_MSG);
476 if (scope) {
477 CFStringPerformWithCString(scope, ^(const char *scopeStr) {
478 asl_set(msg, ASL_KEY_FACILITY, scopeStr);
479 });
480 }
481 asl_log(get_aslclient(), msg, level, "%s", logMsg);
482 asl_free(msg);
483 });
484 CFReleaseSafe(logStr);
485 });
486 });
487
488 return sSecurityLogHandlers;
489 }
490
491 static void log_api_trace_v(const char *api, const char *caller_info, CFStringRef format, va_list args)
492 {
493 aslmsg msg = asl_new(ASL_TYPE_MSG);
494 asl_set(msg, ASL_KEY_LEVEL, ASL_STRING_DEBUG);
495 CFStringPerformWithCString(kAPIScope, ^(const char *scopeStr) {
496 asl_set(msg, ASL_KEY_FACILITY, scopeStr);
497 });
498 asl_set(msg, "SecAPITrace", api);
499 asl_set(msg, caller_info ? "ENTER" : "RETURN", "");
500
501 if (format) {
502 CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args);
503 CFStringPerformWithCString(message, ^(const char *utf8Str) {
504 asl_set(msg, ASL_KEY_MSG, utf8Str);
505 });
506 CFReleaseSafe(message);
507 }
508
509 if (caller_info) {
510 asl_set(msg, "CALLER", caller_info);
511 }
512
513 asl_send(get_aslclient(), msg);
514 asl_free(msg);
515 }
516
517 void __security_trace_enter_api(const char *api, CFStringRef format, ...)
518 {
519 if (!IsScopeActive(ASL_LEVEL_DEBUG, kAPIScope))
520 return;
521
522 va_list args;
523 va_start(args, format);
524
525 {
526 char stack_info[80];
527
528 snprintf(stack_info, sizeof(stack_info), "C%p F%p", __builtin_return_address(1), __builtin_frame_address(2));
529
530 log_api_trace_v(api, stack_info, format, args);
531 }
532
533 va_end(args);
534 }
535
536 void __security_trace_return_api(const char *api, CFStringRef format, ...)
537 {
538 if (!IsScopeActive(ASL_LEVEL_DEBUG, kAPIScope))
539 return;
540
541 va_list args;
542 va_start(args, format);
543
544 log_api_trace_v(api, NULL, format, args);
545
546 va_end(args);
547 }
548
549
550 void add_security_log_handler(security_log_handler handler)
551 {
552 CFArrayAppendValue(get_log_handlers(), handler);
553 }
554
555 void remove_security_log_handler(security_log_handler handler)
556 {
557 CFArrayRemoveAllValue(get_log_handlers(), handler);
558 }
559
560 static void __security_post_msg(int level, CFStringRef scope, const char *function,
561 const char *file, int line, CFStringRef message)
562 {
563 CFArrayForEach(get_log_handlers(), ^(const void *value) {
564 security_log_handler handler = (security_log_handler) value;
565 if (handler) {
566 handler(level, scope, function, file, line, message);
567 }
568 });
569 }
570
571 static void __security_log_msg_v(int level, CFStringRef scope, const char *function,
572 const char *file, int line, CFStringRef format, va_list args)
573 {
574 __security_debug_init();
575
576 if (!IsScopeActive(level, scope))
577 return;
578
579 CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args);
580 __security_post_msg(level, scope, function, file, line, message);
581 CFRelease(message);
582
583 }
584
585 void __security_debug(CFStringRef scope, const char *function,
586 const char *file, int line, CFStringRef format, ...)
587 {
588 va_list args;
589 va_start(args, format);
590
591 __security_log_msg_v(ASL_LEVEL_DEBUG, scope, function, file, line, format, args);
592
593 va_end(args);
594 }
595
596 static void __os_log_shim(void *addr, int32_t level, CFStringRef format, va_list in_args) {
597 if ((level & 0x7) == level) {
598 va_list args;
599 va_copy(args, in_args);
600 os_log_shim_with_CFString(addr, OS_LOG_DEFAULT, _os_trace_type_map[level], format, args, NULL);
601 va_end(args);
602 }
603 }
604
605 void __security_log(int level, CFStringRef scope, const char *function,
606 const char *file, int line, CFStringRef format, ...)
607 {
608 va_list args;
609 va_start(args, format);
610
611 __os_log_shim(__builtin_return_address(0), level, format, args);
612
613 if (os_log_shim_legacy_logging_enabled()) {
614 __security_log_msg_v(level, scope, function, file, line, format, args);
615 }
616
617 va_end(args);
618 }