1/*
2 * Copyright (c) 2006-2010 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/SecCFWrappers.h"
30#include <CoreFoundation/CFSet.h>
31#include <CoreFoundation/CFString.h>
32
33#include <dispatch/dispatch.h>
34
35#include <stdarg.h>
36#include <stdlib.h>
37#include <stdio.h>
38#include <string.h>
39#include <pthread.h>
40#include <asl.h>
41
42/** begin: For SimulateCrash **/
43#include <dlfcn.h>
44#include <mach/mach.h>
45/// Type to represent a boolean value.
46#if TARGET_OS_IPHONE  &&  __LP64__
47typedef bool BOOL;
48#else
49typedef signed char BOOL;
50// BOOL is explicitly signed so @encode(BOOL) == "c" rather than "C"
51// even if -funsigned-char is used.
52#endif
53/** end: For SimulateCrash **/
54
55#define MAX_SCOPE_LENGTH  12
56
57#if !defined(NDEBUG)
58static CFStringRef copyScopeName(const char *scope, CFIndex scopeLen) {
59	if (scopeLen > MAX_SCOPE_LENGTH)
60		scopeLen = MAX_SCOPE_LENGTH - 1;
61	return CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8 *)scope,
62		scopeLen, kCFStringEncodingUTF8, false);
63}
64
65pthread_once_t __security_debug_once = PTHREAD_ONCE_INIT;
66static const char *gDebugScope;
67static CFMutableSetRef scopeSet;
68static bool negate = false;
69
70static void __security_debug_init(void) {
71	const char *cur_scope = gDebugScope = getenv("DEBUGSCOPE");
72	if (cur_scope) {
73		if (!strcmp(cur_scope, "all")) {
74			scopeSet = NULL;
75			negate = true;
76		} else if (!strcmp(cur_scope, "none")) {
77			scopeSet = NULL;
78			negate = false;
79		} else {
80			scopeSet = CFSetCreateMutable(kCFAllocatorDefault, 0,
81				&kCFTypeSetCallBacks);
82			if (cur_scope[0] == '-') {
83				negate = true;
84				cur_scope++;
85			} else {
86				negate = false;
87			}
88
89			const char *sep;
90			while ((sep = strchr(cur_scope, ','))) {
91				CFStringRef scopeName = copyScopeName(cur_scope,
92					sep - cur_scope);
93				CFSetAddValue(scopeSet, scopeName);
94				CFRelease(scopeName);
95				cur_scope = sep + 1;
96			}
97
98			CFStringRef scopeName = copyScopeName(cur_scope,
99				strlen(cur_scope));
100			CFSetAddValue(scopeSet, scopeName);
101			CFRelease(scopeName);
102		}
103	} else {
104		scopeSet = NULL;
105		negate = false;
106	}
107}
108
109#endif
110
111static CFMutableArrayRef sSecurityLogHandlers;
112
113static CFMutableArrayRef get_log_handlers()
114{
115    static dispatch_once_t handlers_once;
116
117    dispatch_once(&handlers_once, ^{
118        sSecurityLogHandlers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
119
120        CFArrayAppendValue(sSecurityLogHandlers, ^(const char *level, CFStringRef scope, const char *function,
121                                                   const char *file, int line, CFStringRef message){
122            CFStringRef logStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ %s %@\n"), scope ? scope : CFSTR(""), function, message);
123            CFStringPerformWithCString(logStr, ^(const char *logMsg) {
124                aslmsg msg = asl_new(ASL_TYPE_MSG);
125                if (scope) {
126                    CFStringPerformWithCString(scope, ^(const char *scopeStr) {
127                        asl_set(msg, ASL_KEY_FACILITY, scopeStr);
128                    });
129                }
130                asl_set(msg, ASL_KEY_LEVEL, level);
131                asl_set(msg, ASL_KEY_MSG, logMsg);
132                asl_send(NULL, msg);
133                asl_free(msg);
134            });
135            CFReleaseSafe(logStr);
136        });
137    });
138
139    return sSecurityLogHandlers;
140}
141
142static void clean_aslclient(void *client)
143{
144    asl_close(client);
145}
146
147static aslclient get_aslclient()
148{
149    static dispatch_once_t once;
150    static pthread_key_t asl_client_key;
151    dispatch_once(&once, ^{
152        pthread_key_create(&asl_client_key, clean_aslclient);
153    });
154    aslclient client = pthread_getspecific(asl_client_key);
155    if (!client) {
156        client = asl_open(NULL, "SecAPI", 0);
157        asl_set_filter(client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
158        pthread_setspecific(asl_client_key, client);
159    }
160
161    return client;
162}
163
164void __security_trace_enter_api(const char *api, CFStringRef format, ...)
165{
166    aslmsg msg = asl_new(ASL_TYPE_MSG);
167    asl_set(msg, ASL_KEY_LEVEL, ASL_STRING_DEBUG);
168    asl_set(msg, "SecAPITrace", api);
169    asl_set(msg, "ENTER", "");
170	va_list args;
171	va_start(args, format);
172    if (format) {
173        CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args);
174        va_end(args);
175        CFStringPerformWithCString(message, ^(const char *utf8Str) {
176            asl_set(msg, ASL_KEY_MSG, utf8Str);
177        });
178        CFReleaseSafe(message);
179    }
180
181    {
182        char stack_info[80];
183
184        snprintf(stack_info, sizeof(stack_info), "C%p F%p", __builtin_return_address(1), __builtin_frame_address(2));
185        asl_set(msg, "CALLER", stack_info);
186    }
187
188    asl_send(get_aslclient(), msg);
189    asl_free(msg);
190}
191
192void __security_trace_return_api(const char *api, CFStringRef format, ...)
193{
194    aslmsg msg = asl_new(ASL_TYPE_MSG);
195    asl_set(msg, ASL_KEY_LEVEL, ASL_STRING_DEBUG);
196    asl_set(msg, "SecAPITrace", api);
197    asl_set(msg, "RETURN", "");
198	va_list args;
199	va_start(args, format);
200    if (format) {
201        CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args);
202        va_end(args);
203        CFStringPerformWithCString(message, ^(const char *utf8Str) {
204            asl_set(msg, ASL_KEY_MSG, utf8Str);
205        });
206        CFReleaseSafe(message);
207    }
208    asl_send(get_aslclient(), msg);
209    asl_free(msg);
210}
211
212
213void add_security_log_hanlder(security_log_handler handler)
214{
215    CFArrayAppendValue(get_log_handlers(), handler);
216}
217
218static void __security_log_msg(const char *level, CFStringRef scope, const char *function,
219                    const char *file, int line, CFStringRef message)
220{
221
222    CFArrayForEach(get_log_handlers(), ^(const void *value) {
223        security_log_handler handler = (security_log_handler) value;
224
225        handler(level, scope, function, file, line, message);
226    });
227}
228
229void __security_debug(CFStringRef scope, const char *function,
230                      const char *file, int line, CFStringRef format, ...)
231{
232#if !defined(NDEBUG)
233	pthread_once(&__security_debug_once, __security_debug_init);
234
235    /* Check if scope is enabled. */
236    if (scope && ((scopeSet && negate == CFSetContainsValue(scopeSet, scope)) ||
237                  (!scopeSet && !negate)))
238        return;
239#endif
240
241	va_list args;
242	va_start(args, format);
243	CFStringRef message = CFStringCreateWithFormatAndArguments(
244        kCFAllocatorDefault, NULL, format, args);
245	va_end(args);
246
247    /* DEBUG scopes are logged as notice when enabled. */
248    __security_log_msg(ASL_STRING_NOTICE, scope, function, file, line, message);
249    CFRelease(message);
250}
251
252void __security_log(const char *level, CFStringRef scope, const char *function,
253    const char *file, int line, CFStringRef format, ...)
254{
255	va_list args;
256	va_start(args, format);
257	CFStringRef message = CFStringCreateWithFormatAndArguments(
258		kCFAllocatorDefault, NULL, format, args);
259	va_end(args);
260    __security_log_msg(level, scope, function, file, line, message);
261    CFRelease(message);
262}
263
264static void __security_simulatecrash_link(CFStringRef reason, uint32_t code)
265{
266#if !TARGET_IPHONE_SIMULATOR
267    // Prototype defined in <CrashReporterSupport/CrashReporterSupport.h>, but objC only.
268    // Soft linking here so we don't link unless we hit this.
269    static BOOL (*__SimulateCrash)(pid_t pid, mach_exception_data_type_t exceptionCode, CFStringRef description);
270
271    static dispatch_once_t once = 0;
272    dispatch_once(&once, ^{
273        void *image = dlopen("/System/Library/PrivateFrameworks/CrashReporterSupport.framework/CrashReporterSupport", RTLD_NOW);
274        if (image)
275            __SimulateCrash = dlsym(image, "SimulateCrash");
276        else
277            __SimulateCrash = NULL;
278    });
279
280    if (__SimulateCrash)
281        __SimulateCrash(getpid(), code, reason);
282    else
283        secerror("SimulateCrash not available");
284#else
285    secerror("SimulateCrash not available in iOS simulator");
286#endif
287}
288
289
290void __security_simulatecrash(CFStringRef reason, uint32_t code)
291{
292    secerror("Simulating crash, reason: %@, code=%08x", reason, code);
293    __security_simulatecrash_link(reason, code);
294}
295