]> git.saurik.com Git - apple/security.git/blob - OSX/utilities/src/debugging.c
Security-58286.270.3.0.1.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 #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 void CFSetAppendValues(CFSetRef set, CFMutableArrayRef appendTo)
219 {
220 CFSetForEach(set, ^(const void *value) {
221 CFArrayAppendValue(appendTo, value);
222 });
223 }
224
225 static CFMutableArrayRef CFSetOfCFObjectsCopyValues(CFSetRef setOfCFs)
226 {
227 CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
228
229 CFSetForEach(setOfCFs, ^(const void *value) {
230 CFArrayAppendValue(result, value);
231 });
232
233 return result;
234 }
235
236 CFPropertyListRef CopyCurrentScopePlist(void)
237 {
238 CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
239 with_scopes_read(^{
240 CFArrayForEach(sLogSettings, ^(const void *value) {
241 if (isSet(value)) {
242 CFArrayRef values = CFSetOfCFObjectsCopyValues((CFSetRef) value);
243 CFArrayAppendValue(result, values);
244 CFReleaseNull(values);
245 } else if (isDictionary(value)) {
246 CFMutableDictionaryRef levels = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
247
248 CFDictionaryForEach((CFDictionaryRef) value, ^(const void *key, const void *value) {
249 if (isSet(value)) {
250 CFArrayRef values = CFSetOfCFObjectsCopyValues((CFSetRef) value);
251 CFDictionaryAddValue(levels, key, values);
252 CFReleaseNull(values);
253 }
254 });
255
256 CFArrayAppendValue(result, levels);
257 } else {
258 CFArrayAppendValue(result, kCFNull);
259 }
260 });
261 });
262 return result;
263 }
264
265 void ApplyScopeDictionaryForID(CFDictionaryRef scopeDictionary, SecDebugScopeID whichID)
266 {
267 CFMutableDictionaryRef dictionary_for_id = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
268
269 CFDictionaryForEach(scopeDictionary, ^(const void *key, const void *value) {
270 CFSetRef scope_set = NULL;
271 CFNumberRef key_number = NULL;
272 if (isString(key)) {
273 int level = string_to_log_level((CFStringRef) key);
274
275 if (level >= 0)
276 key_number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &level);
277 } else if (isNumber(key)) {
278 key_number = CFRetainSafe(key);
279 }
280
281 if (isString(value)) {
282 scope_set = CopyScopesFromScopeList(value);
283 }
284
285 if (key_number && scope_set)
286 CFDictionaryAddValue(dictionary_for_id, key_number, scope_set);
287
288 CFReleaseNull(key_number);
289 CFReleaseNull(scope_set);
290 });
291
292 if (CFDictionaryGetCount(dictionary_for_id) > 0) {
293 SetNthScopeSet(whichID, dictionary_for_id);
294 }
295
296 CFReleaseNull(dictionary_for_id);
297 }
298
299 void ApplyScopeListForID(CFStringRef scopeList, SecDebugScopeID whichID)
300 {
301 CFMutableSetRef scopesToUse = CopyScopesFromScopeList(scopeList);
302
303 SetNthScopeSet(whichID, scopesToUse);
304
305 CFReleaseNull(scopesToUse);
306 }
307
308 void ApplyScopeListForIDC(const char *scopeList, SecDebugScopeID whichID) {
309 CFStringRef scope_string = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, scopeList, kCFStringEncodingUTF8, kCFAllocatorNull);
310
311 ApplyScopeListForID(scope_string, whichID);
312
313 CFReleaseNull(scope_string);
314 }
315
316 #pragma mark - Log Handlers to catch log information
317
318
319 /*
320 * Instead of using CFPropertyListReadFromFile we use a
321 * CFPropertyListCreateWithStream directly
322 * here. CFPropertyListReadFromFile() uses
323 * CFURLCopyResourcePropertyForKey() andCF pulls in CoreServices for
324 * CFURLCopyResourcePropertyForKey() and that doesn't work in install
325 * environment.
326 */
327
328 static CFPropertyListRef
329 CopyPlistFromFile(CFURLRef url)
330 {
331 CFDictionaryRef d = NULL;
332 CFReadStreamRef s = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
333 if (s && CFReadStreamOpen(s)) {
334 d = (CFDictionaryRef)CFPropertyListCreateWithStream(kCFAllocatorDefault, s, 0, kCFPropertyListImmutable, NULL, NULL);
335 }
336 CFReleaseSafe(s);
337
338 return d;
339 }
340
341 static void ApplyScopeByTypeForID(CFPropertyListRef scopes, SecDebugScopeID whichID) {
342 if (isDictionary(scopes)) {
343 ApplyScopeDictionaryForID(scopes, whichID);
344 } else if (isString(scopes)) {
345 ApplyScopeListForID(scopes, whichID);
346 }
347 }
348
349 static void setup_config_settings() {
350 CFStringRef logFileName;
351 #if TARGET_OS_IPHONE
352 logFileName = CFSTR(".GlobalPreferences.plist");
353 #else
354 logFileName = CFSTR("com.apple.security.logging.plist");
355 #endif
356 CFURLRef prefURL = SecCopyURLForFileInManagedPreferencesDirectory(logFileName);
357 if(prefURL) {
358 CFPropertyListRef plist = CopyPlistFromFile(prefURL);
359 if (plist) {
360 ApplyScopeByTypeForID(CFDictionaryGetValue(plist, CFSTR("SecLogging")), kScopeIDConfig);
361 }
362 CFReleaseSafe(plist);
363 }
364 CFReleaseSafe(prefURL);
365 }
366
367 static void setup_defaults_settings() {
368 CFPropertyListRef scopes_value = CFPreferencesCopyValue(CFSTR("Logging"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
369
370 ApplyScopeByTypeForID(scopes_value, kScopeIDDefaults);
371
372 CFReleaseSafe(scopes_value);
373 }
374
375 static void setup_circle_defaults_settings() {
376 CFPropertyListRef scopes_value = CFPreferencesCopyValue(CFSTR("Circle-Logging"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
377
378 ApplyScopeByTypeForID(scopes_value, kScopeIDDefaults);
379
380 CFReleaseSafe(scopes_value);
381 }
382
383 static void setup_environment_scopes() {
384 const char *cur_scope = getenv("DEBUGSCOPE");
385 if (cur_scope == NULL)
386 cur_scope = "";
387
388 ApplyScopeListForIDC(cur_scope, kScopeIDEnvironment);
389 }
390
391
392 void __security_debug_init(void) {
393 static dispatch_once_t sdOnceToken;
394
395 dispatch_once(&sdOnceToken, ^{
396 setup_environment_scopes();
397 setup_config_settings();
398 setup_defaults_settings();
399 setup_circle_defaults_settings();
400 });
401 }
402
403
404
405
406 static char *copyScopeStr(CFStringRef scope, char *alternative) {
407 char *scopeStr = NULL;
408 if(scope) {
409 scopeStr = CFStringToCString(scope);
410 } else {
411 scopeStr = strdup("noScope");
412 }
413 return scopeStr;
414 }
415
416 os_log_t
417 secLogObjForCFScope(CFStringRef scope)
418 {
419 os_log_t retval = OS_LOG_DISABLED;
420 static os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
421 static CFMutableDictionaryRef scopeMap = NULL;
422
423 if (scope == NULL) {
424 scope = CFSTR("logging");
425 }
426
427 os_unfair_lock_lock_with_options(&lock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
428
429 if (scopeMap == NULL) {
430 scopeMap = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, NULL);
431 }
432
433 retval = (os_log_t)CFDictionaryGetValue(scopeMap, scope);
434 if (retval == NULL) {
435 CFStringPerformWithCString(scope, ^(const char *scopeStr) {
436 CFDictionaryAddValue(scopeMap, scope, os_log_create("com.apple.securityd", scopeStr));
437 });
438 retval = (os_log_t)CFDictionaryGetValue(scopeMap, scope);
439 }
440
441 os_unfair_lock_unlock(&lock);
442
443 return retval;
444 }
445
446 static bool loggingEnabled = true;
447 static pthread_mutex_t loggingMutex = PTHREAD_MUTEX_INITIALIZER;
448
449 bool secLogEnabled(void) {
450 bool r = false;
451 pthread_mutex_lock(&loggingMutex);
452 r = loggingEnabled;
453 pthread_mutex_unlock(&loggingMutex);
454 return r;
455 }
456 void secLogDisable(void) {
457 pthread_mutex_lock(&loggingMutex);
458 loggingEnabled = false;
459 pthread_mutex_unlock(&loggingMutex);
460 }
461
462 void secLogEnable(void) {
463 pthread_mutex_lock(&loggingMutex);
464 loggingEnabled = true;
465 pthread_mutex_unlock(&loggingMutex);
466 }
467
468 os_log_t secLogObjForScope(const char *scope) {
469 if (!secLogEnabled())
470 return OS_LOG_DISABLED;
471 CFStringRef cfscope = NULL;
472 if(scope) cfscope = CFStringCreateWithCString(kCFAllocatorDefault, scope, kCFStringEncodingASCII);
473 os_log_t retval = secLogObjForCFScope(cfscope);
474 CFReleaseNull(cfscope);
475 return retval;
476 }
477
478
479
480 CFStringRef SecLogAPICreate(bool apiIn, const char *api, CFStringRef format, ... ) {
481 CFMutableStringRef outStr = CFStringCreateMutable(kCFAllocatorDefault, 0);
482
483 char *direction = apiIn ? "ENTER" : "RETURN";
484 va_list args;
485 va_start(args, format);
486
487 CFStringAppend(outStr, CFSTR("SecAPITrace "));
488 CFStringAppendCString(outStr, api, kCFStringEncodingASCII);
489 CFStringAppendCString(outStr, direction, kCFStringEncodingASCII);
490
491 if (format) {
492 CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args);
493 CFStringAppend(outStr, message);
494 CFReleaseSafe(message);
495 }
496
497 if (apiIn) {
498 char caller_info[80];
499 snprintf(caller_info, sizeof(caller_info), "C%p F%p", __builtin_return_address(1), __builtin_frame_address(2));
500 CFStringAppend(outStr, CFSTR("CALLER "));
501 CFStringAppendCString(outStr, caller_info, kCFStringEncodingASCII);
502 }
503 va_end(args);
504
505 return outStr;
506 }
507
508 #if TARGET_OS_OSX
509 // Functions for weak-linking os_log functions
510 #include <dlfcn.h>
511
512 #define weak_log_f(fname, newname, rettype, fallthrough) \
513 rettype newname(log_args) { \
514 static dispatch_once_t onceToken = 0; \
515 static rettype (*newname)(log_args) = NULL; \
516 \
517 dispatch_once(&onceToken, ^{ \
518 void* libtrace = dlopen("/usr/lib/system/libsystem_trace.dylib", RTLD_LAZY | RTLD_LOCAL); \
519 if (libtrace) { \
520 newname = (rettype(*)(log_args)) dlsym(libtrace, #fname); \
521 } \
522 }); \
523 \
524 if(newname) { \
525 return newname(log_argnames); \
526 } \
527 fallthrough;\
528 }
529
530 #define log_args void *dso, os_log_t log, os_log_type_t type, const char *format, uint8_t *buf, unsigned int size
531 #define log_argnames dso, log, type, format, buf, size
532 weak_log_f(_os_log_impl, weak_os_log_impl, void, return);
533 #undef log_args
534 #undef log_argnames
535
536 #define log_args const char *subsystem, const char *category
537 #define log_argnames subsystem, category
538 weak_log_f(os_log_create, weak_os_log_create, os_log_t, return NULL);
539 #undef log_args
540 #undef log_argnames
541
542 #define log_args os_log_t oslog, os_log_type_t type
543 #define log_argnames oslog, type
544 weak_log_f(os_log_type_enabled, weak_os_log_type_enabled, bool, return false);
545 #undef log_args
546 #undef log_argnames
547
548 #undef weak_log_f
549
550 #endif // TARGET_OS_OSX
551