1/*
2 * Copyright (c) 2000-2004,2011-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 - non-trivial debugging support
27//
28#include <security_utilities/debugsupport.h>
29#include <security_utilities/globalizer.h>
30#include <cstdarg>
31#include <ctype.h>
32
33#define SYSLOG_NAMES		// compile syslog name tables
34#include <syslog.h>
35
36#include <cxxabi.h>			// for name demangling
37#include <mach-o/dyld.h>	// for _NSGetExecutablePath
38#include <limits.h>
39
40// enable kernel tracing
41#define ENABLE_SECTRACE 1
42
43
44namespace Security {
45namespace Debug {
46
47
48//
49// Dump facility
50//
51bool dumping(const char *scope)
52{
53#if defined(NDEBUG_STUBS)
54    return false;
55#else
56	return Target::get().dump(scope);
57#endif
58}
59
60void dump(const char *format, ...)
61{
62#if !defined(NDEBUG_CODE)
63	va_list args;
64	va_start(args, format);
65	Target::get().dump(format, args);
66	va_end(args);
67#endif
68}
69
70void dumpData(const void *ptr, size_t size)
71{
72#if !defined(NDEBUG_CODE)
73	const char *addr = reinterpret_cast<const char *>(ptr);
74	const char *end = addr + size;
75	bool isText = true;
76	for (const char *p = addr; p < end; p++)
77		if (!isprint(*p)) { isText = false; break; }
78
79	if (isText) {
80		dump("\"");
81		for (const char *p = addr; p < end; p++)
82			dump("%c", *p);
83		dump("\"");
84	} else {
85		dump("0x");
86		for (const char *p = addr; p < end; p++)
87			dump("%2.2x", static_cast<unsigned char>(*p));
88	}
89#endif //NDEBUG_STUBS
90}
91
92void dumpData(const char *title, const void *ptr, size_t size)
93{
94#if !defined(NDEBUG_CODE)
95	dump("%s: ", title);
96	dumpData(ptr, size);
97	dump("\n");
98#endif //NDEBUG_STUBS
99}
100
101
102//
103// Turn a C++ typeid into a nice type name.
104// This uses the C++ ABI where available.
105// We're stripping out a few C++ prefixes; they're pretty redundant (and obvious).
106//
107string makeTypeName(const type_info &type)
108{
109	int status;
110	char *cname = abi::__cxa_demangle(type.name(), NULL, NULL, &status);
111	string name = !strncmp(cname, "Security::", 10) ? (cname + 10) :
112		!strncmp(cname, "std::", 5) ? (cname + 5) :
113		cname;
114	::free(cname);	// yes, really (ABI rules)
115	return name;
116}
117
118
119//
120// Target initialization.
121// This where we should do all "first time" initializations.
122//
123#if !defined(NDEBUG_CODE)
124
125char Target::progName[maxProgNameLength + 1];
126unsigned int Target::PerThread::lastUsed;
127
128Target::Target()
129	: showScope(false), showThread(false), showProc(false), showDate(false),
130	  sink(NULL)
131{
132	// put into singleton slot if first
133	if (singleton == NULL)
134		singleton = this;
135
136	// insert terminate handler
137	if (!previousTerminator)	// first time we do this
138		previousTerminator = set_terminate(terminator);
139
140	// get program name
141	char execPath[PATH_MAX];
142	uint32_t length = sizeof(execPath);
143	if (_NSGetExecutablePath(execPath, &length)) {
144		strcpy(progName, "unknown");
145	} else {
146		const char *p = strrchr(execPath, '/');
147		if (p)
148			p++;
149		else
150			p = execPath;
151		size_t plen = strlen(p);
152		if (plen > maxProgNameLength)		// too long
153			p += plen - maxProgNameLength; // take rear
154		strcpy(progName, p);
155	}
156}
157
158Target::~Target()
159{
160}
161
162
163static void addScope(char *&bufp, const char *scope)
164{
165	if (const char *sep = strchr(scope, ',')) {
166		bufp += sprintf(bufp, "%-*s", Name::maxLength, (const char *)Name(scope, sep));
167	} else {    // single scope
168		bufp += sprintf(bufp, "%-*s", Name::maxLength, scope);
169	}
170}
171
172
173//
174// The core logging function of a Target
175//
176void Target::message(const char *scope, const char *format, va_list args)
177{
178	if (logSelector(scope)) {
179		// note: messageConstructionSize is big enough for all prefixes constructed
180		char buffer[messageConstructionSize];	// building the message here
181		char *bufp = buffer;
182
183		// date option
184		if (showDate && sink->needsDate) {
185			time_t now = time(NULL);
186			char *date = ctime(&now);
187			date[19] = '\0';
188			bufp += sprintf(bufp, "%s ", date + 4);	// Nov 24 18:22:48
189		}
190
191		// leading scope
192		if (showScope && scope)
193			addScope(bufp, scope);
194
195		if (showProc || showThread) {
196			char sub[maxProgNameLength + 20];
197			unsigned plen = 0;
198			if (showProc && showThread)
199				plen = sprintf(sub, "%s[%d]", progName, getpid());
200			else if (showProc)
201				plen = sprintf(sub, "%s", progName);
202			else
203				plen = sprintf(sub, "[%d]", getpid());
204			unsigned int id = perThread().id;
205			if (id > 1)
206				plen += sprintf(sub + plen, ":%d", id);
207			if (plen <= procLength)
208				bufp += sprintf(bufp, "%-*s ", int(procLength), sub);
209			else
210				bufp += sprintf(bufp, "%s ", sub + plen - procLength);
211		}
212
213		// scope after proc/thread/pid
214		if (showScopeRight && scope)
215			addScope(bufp, scope);
216
217		// now stuff the message body in, slightly roasted
218		size_t left = buffer + sizeof(buffer) - bufp - 1;	// reserve one
219		size_t written = vsnprintf(bufp, left, format, args);
220        for (char *p = bufp; *p; p++)
221            if (!isprint(*p))
222                *p = '?';
223		if (written >= left) {	// snprintf overflowed
224			bufp += left;
225			strcpy(bufp - 3, "...");
226		} else
227			bufp += written;
228
229		// now append a newline and a null
230		bufp[0] = '\n';
231		bufp[1] = '\0';
232
233		// submit to sink (do not count newline and null in count)
234		sink->put(buffer, (unsigned int)(bufp - buffer));
235	}
236}
237
238bool Target::debugging(const char *scope)
239{
240	return logSelector(scope);
241}
242
243
244//
245// The core debug-dump function of a target
246//
247void Target::dump(const char *format, va_list args)
248{
249	char buffer[messageConstructionSize];	// building the message here
250	vsnprintf(buffer, sizeof(buffer), format, args);
251	for (char *p = buffer; *p; p++)
252		if ((!isprint(*p) && !isspace(*p)) || *p == '\r')
253			*p = '?';
254	sink->dump(buffer);
255}
256
257bool Target::dump(const char *scope)
258{
259	return dumpSelector(scope);
260}
261
262
263//
264// Selector objects.
265//
266Target::Selector::Selector() : useSet(false), negate(false)
267{ }
268
269void Target::Selector::operator = (const char *scope)
270{
271	if (scope) {
272		// initial values
273		if (!strcmp(scope, "all")) {
274			useSet = false;
275			negate = true;
276		} else if (!strcmp(scope, "none")) {
277			useSet = negate = false;
278		} else {
279			useSet = true;
280			enableSet.erase(enableSet.begin(), enableSet.end());
281			if (scope[0] == '-') {
282				negate = true;
283				scope++;
284			} else
285				negate = false;
286			while (const char *sep = strchr(scope, ',')) {
287				enableSet.insert(Name(scope, sep));
288				scope = sep + 1;
289			}
290			enableSet.insert(scope);
291		}
292	} else {
293		useSet = negate = false;
294	}
295}
296
297bool Target::Selector::operator () (const char *scope) const
298{
299	// a scope of NULL is a special override; it always qualifies
300	if (scope == NULL)
301		return true;
302
303	if (useSet) {
304		while (const char *sep = strchr(scope, ',')) {
305			if (enableSet.find(Name(scope, sep)) != enableSet.end())
306				return !negate;
307			scope = sep + 1;
308		}
309		return (enableSet.find(scope) != enableSet.end()) != negate;
310	} else {
311		return negate;
312	}
313}
314
315
316//
317// Establish Target state from the environment
318//
319void Target::setFromEnvironment()
320{
321	// set scopes
322	logSelector = getenv("DEBUGSCOPE");
323	dumpSelector = getenv("DEBUGDUMP");
324
325	//
326	// Set and configure destination. Currently available:
327	//	/some/where -> that file
328	//	LOG_SOMETHING -> syslog facility
329	//	>&number -> that (already) open (for write or append) file descriptor
330	//	anything else -> try as a filename sight unseen [may change]
331	//	DEBUGDEST not set -> stderr
332	//	anything in error -> stderr (with an error message on it)
333	//
334	if (const char *dest = getenv("DEBUGDEST")) {
335		if (dest[0] == '/') {	// full pathname, write to file
336			to(dest);
337		} else if (!strncmp(dest, "LOG_", 4)) {	// syslog
338			int facility = LOG_DAEMON;
339			for (CODE *cp = facilitynames; cp->c_name; cp++)
340				if (!strcmp(dest, cp->c_name))
341					facility = cp->c_val;
342			to(facility | LOG_DEBUG);
343		} else if (!strncmp(dest, ">&", 2)) {	// to file descriptor
344			int fd = atoi(dest+2);
345			if (FILE *f = fdopen(fd, "a")) {
346				to(f);
347			} else {
348				to(stderr);
349				secdebug("", "cannot log to fd[%d]: %s", fd, strerror(errno));
350			}
351		} else {	// if everything else fails, write a file
352			to(dest);
353		}
354	} else {		// default destination is stderr
355		to(stderr);
356	}
357	configure();
358}
359
360
361void Target::configure()
362{
363	configure(getenv("DEBUGOPTIONS"));
364}
365
366void Target::configure(const char *config)
367{
368	// configure global options
369	showScopeRight = config && strstr(config, "rscope");
370	showScope = !showScopeRight && config && strstr(config, "scope");
371	showThread = config && (strstr(config, "thread") || strstr(config, "pid")); // (legacy)
372	showProc = config && strstr(config, "proc");
373	showDate = config && strstr(config, "date");
374
375	// configure sink
376	if (sink)
377		sink->configure(config);
378}
379
380
381//
382// Explicit destination assignments
383//
384void Target::to(Sink *s)
385{
386	delete sink;
387	sink = s;
388}
389
390void Target::to(FILE *file)
391{
392	to(new FileSink(file));
393}
394
395void Target::to(const char *filename)
396{
397	if (FILE *f = fopen(filename, "a")) {
398		to(new FileSink(f));
399	} else {
400		to(stderr);
401		secdebug("", "cannot debug to \"%s\": %s", filename, strerror(errno));
402	}
403}
404
405void Target::to(int syslogPriority)
406{
407	to(new SyslogSink(syslogPriority));
408}
409
410
411//
412// Making and retrieving the default singleton
413//
414Target *Target::singleton;
415
416Target &Target::get()
417{
418	if (singleton == NULL) {
419		Target *t = new Target;
420		t->setFromEnvironment();
421	}
422	return *singleton;
423}
424
425
426//
427// Standard sink implementations
428//
429Target::Sink::~Sink()
430{ }
431
432void Target::Sink::dump(const char *)
433{ }
434
435void Target::Sink::configure(const char *)
436{ }
437
438
439//
440// The terminate handler installed when a Target is created
441//
442terminate_handler Target::previousTerminator;
443
444void Target::terminator()
445{
446	secdebug("exception", "uncaught exception terminates program");
447	previousTerminator();
448	secdebug("exception", "prior termination handler failed to abort; forcing abort");
449	abort();
450}
451
452
453//
454// File sinks (write to file via stdio)
455//
456void FileSink::put(const char *inbuf, unsigned int length)
457{
458	fwrite(inbuf, 1, length + 1, file);	// do pick up the trailing newline
459}
460
461void FileSink::dump(const char *text)
462{
463	fputs(text, file);
464}
465
466void FileSink::configure(const char *options)
467{
468	if (options == NULL || !strstr(options, "noflush")) {
469		// we mean "if the file isn't unbuffered", but what's the portable way to say that?
470		if (file != stderr)
471			setlinebuf(file);
472	}
473}
474
475
476//
477// Syslog sinks (write to syslog)
478//
479void SyslogSink::put(const char *buffer, unsigned int length)
480{
481	syslog(priority, "%1.*s", length, buffer); // don't pick up trailing newline
482}
483
484void SyslogSink::dump(const char *text)
485{
486	// add to dump buffer
487	snprintf(dumpPtr, dumpBuffer + dumpBufferSize - dumpPtr, "%s", text);
488
489	// take off full lines and submit
490	char *p = dumpBase;
491	while (char *q = strchr(p, '\n')) {
492		*q++ = '\0';	// terminate/break
493		syslog(priority, " @@ %s", p);
494		p = q;
495	}
496
497	if (*p) {	// left-over unterminated line segment in buffer
498		dumpPtr = p + strlen(p);
499		if ((dumpBase = p) > dumpBuffer + dumpBufferSize / 2) {
500			// shift buffer down to make room
501			memmove(dumpBuffer, dumpBase, dumpPtr - dumpBase);
502			dumpPtr -= (dumpBase - dumpBuffer);
503			dumpBase = dumpBuffer;
504		}
505	} else {	// buffer is empty; reset to start
506		dumpBase = dumpPtr = dumpBuffer;
507	}
508}
509
510void SyslogSink::configure(const char *options)
511{
512}
513
514#endif //NDEBUG_CODE
515
516
517} // end namespace Debug
518} // end namespace Security
519