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