]> git.saurik.com Git - apple/security.git/blob - Security/utilities/src/debugging.c
Security-57031.1.35.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 void __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).
394 static void clean_aslclient(void *client)
395 {
396 asl_close(client);
397 }
398
399 static 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
416 static 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
443 static 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
469 void __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
488 void __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
502 void add_security_log_handler(security_log_handler handler)
503 {
504 CFArrayAppendValue(get_log_handlers(), handler);
505 }
506
507 void remove_security_log_handler(security_log_handler handler)
508 {
509 CFArrayRemoveAllValue(get_log_handlers(), handler);
510 }
511
512 static 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
522 static 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
536 void __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
547 void __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 }