]> git.saurik.com Git - apple/security.git/blob - Security/utilities/src/debugging.c
Security-57031.30.12.tar.gz
[apple/security.git] / Security / 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 <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
45 const CFStringRef kStringNegate = CFSTR("-");
46 const CFStringRef kStringAll = CFSTR("all");
47
48 const CFStringRef kAPIScope = CFSTR("api");
49
50 static CFMutableArrayRef sLogSettings = NULL; /* Either sets or dictionaries of level => set. */
51
52 static 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
61 static void with_scopes_read(dispatch_block_t action) {
62 dispatch_sync(GetDispatchControlQueue(), action);
63 }
64
65 static void with_scopes_write(dispatch_block_t action) {
66 dispatch_barrier_sync(GetDispatchControlQueue(), action);
67 }
68
69 bool 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
108 bool 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
119 static CFStringRef copyScopeName(const char *scope, CFIndex scopeLen) {
120 return CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8 *)scope,
121 scopeLen, kCFStringEncodingUTF8, false);
122 }
123
124 static 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
167 static 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
176 static bool CFArrayIsAll(CFArrayRef array, const void *value)
177 {
178 return CFArrayGetCountOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), value) == CFArrayGetCount(array);
179 }
180
181 static 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
196 static 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
217 static void CFSetAppendValues(CFSetRef set, CFMutableArrayRef appendTo)
218 {
219 CFSetForEach(set, ^(const void *value) {
220 CFArrayAppendValue(appendTo, value);
221 });
222 }
223
224 static 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
235 CFPropertyListRef 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
264 void 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
298 void ApplyScopeListForID(CFStringRef scopeList, SecDebugScopeID whichID)
299 {
300 CFMutableSetRef scopesToUse = CopyScopesFromScopeList(scopeList);
301
302 SetNthScopeSet(whichID, scopesToUse);
303
304 CFReleaseNull(scopesToUse);
305 }
306
307 void 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
317 static 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
330 static CFPropertyListRef
331 CopyPlistFromFile(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
344 static 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
352 static 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
366 static 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
374 static 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
382 #define XPCSCOPESTRWANT "api,account,accountChange,circle,circleChange,circleCreat,flush,fresh,keygen,signing,talkwithkvs"
383 #define XPCSCOPESTRDONTWANT "-event,http,item,keytrace,lockassertions,otr_keysetup,securityd,server,serverxpc,session,sync,titc,transport,trust,updates,xpc"
384 static void setup_xpcdefault_scopes() {
385
386 CFDictionaryRef noticeLogging = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
387 CFSTR(ASL_STRING_NOTICE), CFSTR(XPCSCOPESTRDONTWANT), NULL);
388
389 ApplyScopeDictionaryForID(noticeLogging, kScopeIDXPC);
390
391 CFReleaseNull(noticeLogging);
392 }
393
394 void __security_debug_init(void) {
395 static dispatch_once_t sdOnceToken;
396
397 dispatch_once(&sdOnceToken, ^{
398 setup_environment_scopes();
399 setup_config_settings();
400 setup_defaults_settings();
401 //setup_xpcdefault_scopes();
402 });
403 }
404
405 // MARK: Log handler recording (e.g. grabbing security logging and sending it to test results).
406 static void clean_aslclient(void *client)
407 {
408 asl_close(client);
409 }
410
411 static aslclient get_aslclient()
412 {
413 static dispatch_once_t once;
414 static pthread_key_t asl_client_key;
415 dispatch_once(&once, ^{
416 pthread_key_create(&asl_client_key, clean_aslclient);
417 });
418 aslclient client = pthread_getspecific(asl_client_key);
419 if (!client) {
420 client = asl_open(NULL, "SecLogging", 0);
421 asl_set_filter(client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
422 pthread_setspecific(asl_client_key, client);
423 }
424
425 return client;
426 }
427
428 static CFMutableArrayRef get_log_handlers()
429 {
430 static dispatch_once_t handlers_once;
431
432 dispatch_once(&handlers_once, ^{
433 sSecurityLogHandlers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
434
435 CFArrayAppendValue(sSecurityLogHandlers, ^(int level, CFStringRef scope, const char *function,
436 const char *file, int line, CFStringRef message){
437 CFStringRef logStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ %s %@\n"), scope ? scope : CFSTR(""), function, message);
438 CFStringPerformWithCString(logStr, ^(const char *logMsg) {
439 aslmsg msg = asl_new(ASL_TYPE_MSG);
440 if (scope) {
441 CFStringPerformWithCString(scope, ^(const char *scopeStr) {
442 asl_set(msg, ASL_KEY_FACILITY, scopeStr);
443 });
444 }
445 asl_log(get_aslclient(), msg, level, "%s", logMsg);
446 asl_free(msg);
447 });
448 CFReleaseSafe(logStr);
449 });
450 });
451
452 return sSecurityLogHandlers;
453 }
454
455 static void log_api_trace_v(const char *api, const char *caller_info, CFStringRef format, va_list args)
456 {
457 aslmsg msg = asl_new(ASL_TYPE_MSG);
458 asl_set(msg, ASL_KEY_LEVEL, ASL_STRING_DEBUG);
459 CFStringPerformWithCString(kAPIScope, ^(const char *scopeStr) {
460 asl_set(msg, ASL_KEY_FACILITY, scopeStr);
461 });
462 asl_set(msg, "SecAPITrace", api);
463 asl_set(msg, caller_info ? "ENTER" : "RETURN", "");
464
465 if (format) {
466 CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args);
467 CFStringPerformWithCString(message, ^(const char *utf8Str) {
468 asl_set(msg, ASL_KEY_MSG, utf8Str);
469 });
470 CFReleaseSafe(message);
471 }
472
473 if (caller_info) {
474 asl_set(msg, "CALLER", caller_info);
475 }
476
477 asl_send(get_aslclient(), msg);
478 asl_free(msg);
479 }
480
481 void __security_trace_enter_api(const char *api, CFStringRef format, ...)
482 {
483 if (!IsScopeActive(ASL_LEVEL_DEBUG, kAPIScope))
484 return;
485
486 va_list args;
487 va_start(args, format);
488
489 {
490 char stack_info[80];
491
492 snprintf(stack_info, sizeof(stack_info), "C%p F%p", __builtin_return_address(1), __builtin_frame_address(2));
493
494 log_api_trace_v(api, stack_info, format, args);
495 }
496
497 va_end(args);
498 }
499
500 void __security_trace_return_api(const char *api, CFStringRef format, ...)
501 {
502 if (!IsScopeActive(ASL_LEVEL_DEBUG, kAPIScope))
503 return;
504
505 va_list args;
506 va_start(args, format);
507
508 log_api_trace_v(api, NULL, format, args);
509
510 va_end(args);
511 }
512
513
514 void add_security_log_handler(security_log_handler handler)
515 {
516 CFArrayAppendValue(get_log_handlers(), handler);
517 }
518
519 void remove_security_log_handler(security_log_handler handler)
520 {
521 CFArrayRemoveAllValue(get_log_handlers(), handler);
522 }
523
524 static void __security_post_msg(int level, CFStringRef scope, const char *function,
525 const char *file, int line, CFStringRef message)
526 {
527 CFArrayForEach(get_log_handlers(), ^(const void *value) {
528 security_log_handler handler = (security_log_handler) value;
529
530 handler(level, scope, function, file, line, message);
531 });
532 }
533
534 static void __security_log_msg_v(int level, CFStringRef scope, const char *function,
535 const char *file, int line, CFStringRef format, va_list args)
536 {
537 __security_debug_init();
538
539 if (!IsScopeActive(level, scope))
540 return;
541
542 CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args);
543 __security_post_msg(level, scope, function, file, line, message);
544 CFRelease(message);
545
546 }
547
548 void __security_debug(CFStringRef scope, const char *function,
549 const char *file, int line, CFStringRef format, ...)
550 {
551 va_list args;
552 va_start(args, format);
553
554 __security_log_msg_v(ASL_LEVEL_DEBUG, scope, function, file, line, format, args);
555
556 va_end(args);
557 }
558
559 void __security_log(int level, CFStringRef scope, const char *function,
560 const char *file, int line, CFStringRef format, ...)
561 {
562 va_list args;
563 va_start(args, format);
564
565 __security_log_msg_v(level, scope, function, file, line, format, args);
566
567 va_end(args);
568 }