1/*
2 * Copyright (c) 2006-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23#include <stdio.h>
24#include <string.h>
25#include <stdlib.h>
26#include <errno.h>
27#include <search.h>
28#include <stdint.h>
29#include <pthread.h>
30#include <stdarg.h>
31#include <regex.h>
32#include <asl.h>
33#include <asl_private.h>
34#include <unistd.h>
35#include <sys/sysctl.h>
36#include <GSS/gssapi.h>
37#include <GSS/gssapi_krb5.h>
38#include <GSS/gssapi_ntlm.h>
39#include <GSS/gssapi_spnego.h>
40#include <GSS/gssapi_netlogon.h>
41#include "gssd.h"
42
43/*
44 * Limit the number of contexts that we can have.
45 * If a kernel thread gets a context from us with
46 * CONTINUE NEEDED, but never finish the context we
47 * and then loops before we idle out we can end up
48 * consuming a large amount of memory.
49 */
50#define MAX_GSS_CONTEXTS 100
51
52static void *rootp = (void *)0;
53static pthread_mutex_t smutex;
54static pthread_once_t sonce = PTHREAD_ONCE_INIT;
55int ctx_counter = 0;
56
57
58static void
59init(void)
60{
61	pthread_mutex_init(&smutex, NULL);
62}
63
64static int
65compare(const void *p1, const void *p2)
66{
67	uintptr_t v1 = (uintptr_t)p1;
68	uintptr_t v2 = (uintptr_t)p2;
69	if (v1 == v2)
70		return (0);
71	else if (v1 < v2)
72		return (-1);
73	return (1);
74}
75
76void
77gssd_enter(void *ptr)
78{
79	if (ptr == NULL)
80		return;
81
82	pthread_once(&sonce, init);
83	(void) pthread_mutex_lock(&smutex);
84	if (ctx_counter > MAX_GSS_CONTEXTS) {
85		Log("To many contexes. Exiting\n");
86		/* Will exit with 0 so lanchd will start as again */
87		exit(0);
88	}
89
90	if ((tfind(ptr, &rootp, compare) == (void *)0)) {
91		if (tsearch(ptr, &rootp, compare))
92			ctx_counter++;
93	}
94	(void) pthread_mutex_unlock(&smutex);
95}
96
97void
98gssd_remove(void *ptr)
99{
100	if (ptr == NULL)
101		return;
102
103	pthread_once(&sonce, init);
104	(void) pthread_mutex_lock(&smutex);
105	if (tdelete(ptr, &rootp, compare))
106		ctx_counter--;
107	(void) pthread_mutex_unlock(&smutex);
108}
109
110int
111gssd_check(void *ptr)
112{
113	int rc;
114	if (ptr == (void *)0)
115		return (1);
116
117	pthread_once(&sonce, init);
118	(void) pthread_mutex_lock(&smutex);
119	rc = (tfind(ptr, &rootp, compare) != (void *)0);
120	(void) pthread_mutex_unlock(&smutex);
121	return (rc);
122}
123
124char *
125buf_to_str(gss_buffer_t buf)
126{
127	char *s = malloc(buf->length + 1);
128	uint32_t min;
129
130	if (s) {
131		memcpy(s, buf->value, buf->length);
132		s[buf->length] = '\0';
133	}
134	(void) gss_release_buffer(&min, buf);
135
136	return (s);
137}
138
139int
140traced()
141{
142	struct kinfo_proc kp;
143	int mib[4];
144	size_t len;
145
146	/* Fill out the first three components of the mib */
147	len = 4;
148	sysctlnametomib("kern.proc.pid", mib, &len);
149	mib[3] = getpid();
150	len = sizeof(kp);
151	if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) {
152		gssd_log(ASL_LEVEL_ERR, "sysctl: %s", strerror(errno));
153		return (FALSE);
154	}
155
156	return ((kp.kp_proc.p_flag & P_TRACED) == P_TRACED);
157}
158
159static void
160regerr(int rerr, regex_t *re)
161{
162	char errbuff[60];
163
164	(void)regerror(rerr, re, errbuff, sizeof(errbuff));
165	Fatal("regex error %s\n", errbuff);
166}
167
168/*
169 * Convert the oid in to a sane name if possible, if not return
170 * the string constant of unkown.
171 *
172 */
173
174struct oid_name_entry {
175	gss_OID oid;
176	const char *name;
177} oid_name_tbl[] = {
178	{ GSS_C_NT_USER_NAME, "user name" },
179	{ GSS_C_NT_MACHINE_UID_NAME, "uid" },
180	{ GSS_C_NT_STRING_UID_NAME, "uid string" },
181	{ GSS_C_NT_HOSTBASED_SERVICE_X, "host based service" },
182	{ GSS_C_NT_HOSTBASED_SERVICE, "host based service" },
183	{ GSS_C_NT_ANONYMOUS, "anonymous" },
184	{ GSS_C_NT_EXPORT_NAME, "export name" },
185	{ GSS_C_NT_DN, "distinguished name" },
186	{ GSS_SASL_DIGEST_MD5_MECHANISM, "sasl md5 mech" },
187	{ GSS_NETLOGON_MECHANISM, "netlogon mech" },
188	{ GSS_C_INQ_SSPI_SESSION_KEY, "session key" },
189	{ GSS_C_INQ_WIN2K_PAC_X, "Win2k PAC" },
190	{ GSS_KRB5_NT_PRINCIPAL_NAME, "KRB5 principal" },
191	{ GSS_KRB5_NT_PRINCIPAL_NAME_REFERRAL, "KRB5 principal referral" },
192	{ GSS_KRB5_NT_PRINCIPAL, "KRB5 principal" },
193	{ GSS_KRB5_NT_USER_NAME, "KRB5 user name" },
194	{ GSS_KRB5_NT_MACHINE_UID_NAME, "KRB5 uid" },
195	{ GSS_KRB5_NT_STRING_UID_NAME, "KRB5 uid string" },
196	{ GSS_KRB5_MECHANISM, "KRB5 mech" },
197	{ GSS_PKU2U_MECHANISM, "PKU2U mech" },
198	{ GSS_IAKERB_MECHANISM, "IA Kerb mech" },
199	/* OTHER gssapi_krb5.h oids here */
200	{ GSS_NTLM_MECHANISM, "NTLM mech" },
201	{ GSS_C_NT_NTLM, "NTLM name" },
202	{ GSS_C_NTLM_GUEST, "NTLM guest" },
203	{ GSS_NTLM_GET_SESSION_KEY_X, "NTLM session key" },
204	{ GSS_SPNEGO_MECHANISM, "SPNEGO mech" },
205	{ GSS_C_NT_UUID, "UUID name" },
206	{ NULL, NULL }
207};
208
209char *
210oid_name(gss_OID oid)
211{
212	uint32_t maj, min;
213	gss_buffer_desc buf;
214	struct oid_name_entry *oep;
215	const char *name = NULL;
216	char *rn;
217
218	for (oep = oid_name_tbl; oep->name != NULL; oep++) {
219		if (gss_oid_equal(oid, oep->oid)) {
220			name = oep->name;
221			break;
222		}
223	}
224	if (oid == NULL)
225	    name = "default (null) oid";
226	if (name == NULL) {
227		maj = gss_oid_to_str(&min, oid, &buf);
228		if (maj != GSS_S_COMPLETE)
229			rn = strdup("Bad oid");
230		else
231			rn = buf_to_str(&buf);
232	} else
233		rn = strdup(name);
234
235	return (rn);
236}
237
238/*
239 * GSSAPI ERRORS
240 *
241 */
242static const char *gss_call_err_names[] = {
243	"GSS_S_CALL_INACCESSIBLE_READ",
244	"GSS_S_CALL_INACCESSIBLE_WRITE",
245	"GSS_S_CALL_BAD_STRUCTURE"
246};
247#define CALL_ERR_SIZE (sizeof(gss_call_err_names)/sizeof(const char *))
248
249static const char *gss_err_names[] = {
250	"GSS_S_BAD_MECH",
251	"GSS_S_BAD_NAME",
252	"GSS_S_BAD_NAMETYPE",
253	"GSS_S_BAD_BINDINGS",
254	"GSS_S_BAD_STATUS",
255	"GSS_S_BAD_MIC",
256	"GSS_S_NO_CRED",
257	"GSS_S_NO_CONTEXT",
258	"GSS_S_DEFECTIVE_TOKEN",
259	"GSS_S_DEFECTIVE_CREDENTIAL",
260	"GSS_S_CREDENTIALS_EXPIRED",
261	"GSS_S_CONTEXT_EXPIRED",
262	"GSS_S_FAILURE",
263	"GSS_S_BAD_QOP",
264	"GSS_S_UNAUTHORIZED",
265	"GSS_S_UNAVAILABLE",
266	"GSS_S_DUPLICATE_ELEMENT",
267	"GSS_S_NAME_NOT_MN",
268};
269#define ERR_SIZE (sizeof(gss_err_names)/sizeof(const char *))
270
271static const char *gss_sup_names[] = {
272	"GSS_CONTINUE_NEEDED",
273	"GSS_S_DUPLICATE_TOKEN",
274	"GSS_S_OLD_TOKEN",
275	"GSS_S_UNSEQ_TOKEN",
276	"GSS_S_GAP_TOKEN",
277};
278#define SUP_SIZE (sizeof(gss_sup_names)/sizeof(const char *))
279#define SUP_STRING_SIZE 128
280
281static const char*
282gss_error(uint32_t status)
283{
284	status >>= GSS_C_ROUTINE_ERROR_OFFSET;
285	status &= (uint32_t)GSS_C_ROUTINE_ERROR_MASK;
286	if (status == 0)
287		return (NULL);
288	if (status > ERR_SIZE)
289		return ("GSS_UNKOWN_ERROR");
290	return (gss_err_names[status - 1]);
291}
292
293static const char*
294gss_call_error(uint32_t status)
295{
296	status >>= GSS_C_CALLING_ERROR_OFFSET;
297	status &= (uint32_t)GSS_C_CALLING_ERROR_MASK;
298	if (status == 0)
299		return (NULL);
300		if (status > ERR_SIZE)
301			return ("GSS_UNKOWN_CALL_ERROR");
302	return (gss_call_err_names[status - 1]);
303}
304
305static char*
306gss_sup_info(uint32_t status)
307{
308
309	int previous = 0;
310	size_t i;
311	char *str;
312
313	status >>= GSS_C_SUPPLEMENTARY_OFFSET;
314	status &= (uint32_t) GSS_C_SUPPLEMENTARY_MASK;
315	if (status == 0)
316		return (NULL);
317
318	str = malloc(SUP_STRING_SIZE);
319	if (str == NULL)
320		Fatal("Gssd, gssd-agent out of memory");
321
322	for (i = 0, *str = '\0'; status && i < SUP_SIZE; i++, status >>= 1)  {
323		if (status & 1) {
324			if (previous)
325				strlcat(str, " ", SUP_STRING_SIZE);
326			strlcat(str, gss_sup_names[i], SUP_STRING_SIZE);
327			previous = 1;
328		}
329	}
330	if (status)
331		strlcat(str, previous ? " GSS_UNKOWN_INFO" : "GSS_UNKOWN_INFO", SUP_STRING_SIZE);
332
333	return (str);
334}
335
336static char *
337gss_alt_error(uint32_t status)
338{
339	const char *error, *call_err;
340	char *ret, *sup_info;
341
342	if (status == GSS_S_COMPLETE)
343		return (strdup("GSS_S_COMPLETE"));
344
345	error = gss_error(status);
346	call_err = gss_call_error(status);
347	sup_info = gss_sup_info(status);
348
349	if (error && call_err && sup_info)
350		asprintf(&ret, "%s (%s): %s", error, call_err, sup_info);
351	else if (error && call_err && sup_info == NULL)
352		asprintf(&ret, "%s (%s)", error, call_err);
353	else if (error && call_err == NULL && sup_info)
354		asprintf(&ret, "%s: %s", error, sup_info);
355	else if (error && call_err == NULL && sup_info == NULL)
356		ret = strdup(error);
357	else if (error == NULL && call_err && sup_info)
358		asprintf(&ret, "(%s): %s", call_err, sup_info);
359	else if (error == NULL && call_err && sup_info == NULL)
360		ret = strdup(call_err);
361	else {
362		ret = sup_info;
363		sup_info = NULL;
364	}
365
366	if (sup_info)
367		free(sup_info);
368
369	return (ret);
370}
371
372
373/*
374 * Convert the major and minor error codes for a given id into a string.
375 *
376 * GSS API has a function, gss_display_status, that will do this and that can handle
377 * very long messages by repeatedly calling the routine with a handle returned
378 * from the first call. This is too long to be useful and I have never seen a
379 * case where the entire message was not retrieved on the first call. If we
380 * should find more text to extract will notate that by appending a '<' code value '>'
381 * at the end of the returned string. The caller will be responsible to free
382 * the result. If the OID passed in is GSS_C_NO_OID then we will look up the
383 * major status, else we will return the string for the minor status. If a code
384 * does not map to a string we'll return the string representation of the code
385 * value.
386 */
387
388/*
389 * gss_display_status for minor codes that are not from the last failure on the
390 * current thread are displayed unknown mech-code <blah> from mech <string of numbers>
391 * for the oid in the mech in question. In the abbreviated format, "#" specifier,
392 * will strip that of, since we are printing a nice readable name and its redundant.
393 */
394
395static char *nomechexp = " for mech ([0-9]+ )*[0-9]+$";
396static pthread_once_t gonce = PTHREAD_ONCE_INIT;
397static regex_t mre;
398
399static void
400gss_strerror_init(void)
401{
402	int rerr = regcomp(&mre, nomechexp, REG_EXTENDED);
403	if (rerr)
404		regerr(rerr, &mre);
405}
406
407char *
408gss_strerror(gss_OID oid, uint32_t code, uint32_t flag)
409{
410	uint32_t maj, min;
411	uint32_t display_ctx = 0;
412	int code_space;
413	gss_buffer_desc msg_buf;
414	char *ret_msg = NULL;
415	char *tmp_msg;
416	char *oidstr;
417	regmatch_t match[1];
418	int rerr;
419
420	pthread_once(&gonce, gss_strerror_init);
421	code_space = (oid == GSS_C_NO_OID) ? GSS_C_GSS_CODE : GSS_C_MECH_CODE;
422	oidstr = (oid == GSS_C_NO_OID) ? strdup("GSSAPI") : oid_name(oid);
423	if (oidstr == NULL)
424		Fatal("Gssd or gssd-agent out of memory -- exiting\n");
425
426	if (oid == GSS_C_NO_OID && flag) {
427		free(oidstr);
428		return (gss_alt_error(code));
429	}
430
431	maj = gss_display_status(&min, code, code_space, oid, &display_ctx, &msg_buf);
432	if (maj != GSS_S_COMPLETE) {
433		asprintf(&ret_msg, "%s status %d", oidstr, code);
434		free(oidstr);
435		return (ret_msg);
436	} else {
437		tmp_msg = buf_to_str(&msg_buf);
438		if (tmp_msg == NULL)
439			Fatal("Gssd or gssd-agent out of memory -- exiting\n");
440		if (flag && (oid != GSS_C_NO_OID)) {
441
442			rerr = regexec(&mre, tmp_msg, 1, match, 0);
443			if (rerr == REG_NOMATCH)
444				goto done;
445			else if (rerr)
446				regerr(rerr, &mre);
447			else
448				tmp_msg[match[0].rm_so] = '\0';
449		}
450	}
451
452done:
453	if (flag) {
454		if (display_ctx)
455			asprintf(&ret_msg, "%s: %s <%d>", oidstr, tmp_msg, code);
456		else
457			asprintf(&ret_msg, "%s: %s", oidstr, tmp_msg);
458	} else {
459		if (display_ctx)
460			asprintf(&ret_msg, "%s status: %s <%d>", oidstr, tmp_msg, code);
461		else
462			asprintf(&ret_msg, "%s status: %s", oidstr, tmp_msg);
463	}
464	free(oidstr);
465	free(tmp_msg);
466
467	return (ret_msg);
468}
469
470static int foreground;
471static int istraced;
472
473#include <fcntl.h>
474int
475in_foreground(int ttyfd)
476{
477	int ttyfd2 = -1;
478	pid_t tpg;
479
480	if (ttyfd == -1) {
481		ttyfd2 = open("/dev/tty", O_WRONLY);
482		if (ttyfd2 == -1)
483			return (FALSE);
484		ttyfd = ttyfd2;
485	}
486	tpg = tcgetpgrp(ttyfd);
487
488	if (ttyfd2 > -1)
489		close(ttyfd2);
490
491	if (tpg == -1)
492		return (FALSE);
493
494	return (tpg == getpgid(getpid()));
495}
496
497/*
498 * Regular expression for printf fields:
499 * Regexec will match an array of regmatch_t's as follows:
500 * index 0 is the whole expression
501 * skip the optional accessor specification
502 * index 1 is the optional flags fields
503 * skip the optional AltiVec/SSE vector value separator
504 * index 2 is the optional minimum field width
505 * index 3 is the optional precision field. (also max digits or characters to print)
506 * index 4 is the optional sub-field from above; the characters after the '.'
507 * index 5 is the intergral lenght type modifier (diouxXn)
508 * index  is the type conversion character
509 */
510#define PRF_WHOLE_MATCH		0
511#define PRF_ACCESSOR		-1
512#define PRF_FLAGS		1
513#define PRF_VSEP		-1
514#define PRF_WIDTH		2
515#define PRF_PREC		3
516#define PRF_PREC_SPEC		4
517#define PRF_LENGTH		5
518#define PRF_TYPE		6
519
520#define PRF_FIELDS		7
521
522//static char *prfrexp = "%([0-9]+\\$)?([-#0 +']+)?([,;:_])?(\\*|[0-9]+)?(.(\\*|[0-9]*)?)?(hh?|ll?|j|t|z|q|L)?([diouxXDOUeEfFgGaACcSsp%kK])";
523static char *prfrexp = "%([-#0 +']+)?(\\*|[0-9]+)?(.(\\*|[0-9]*)?)?(hh?|ll?|j|t|z|q|L)?([diouxXDOUeEfFgGaACcSsp%kK])";
524static regex_t pre;
525
526#ifndef GSSD_LOG_DEBUG
527static char *gssrexp = "%([0-9]+\\$)?([-#0 +']+)?([,;:_])?(\\*|[0-9]+)?(.(\\*|[0-9]*)?)?(hh?|ll?|j|t|z|q|L)?([kK])";
528static regex_t gre;
529#endif
530
531static pthread_once_t ronce = PTHREAD_ONCE_INIT;
532
533#define GSSD_FACILITY "com.apple.gssd"
534static aslclient asl = NULL;
535
536#define ASL_INIT_FILTER ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE)
537
538static void
539gssd_log_init(void)
540{
541	int rerr;
542
543
544	/* Check if were in the forground */
545	foreground = in_foreground(2);
546	istraced = traced();
547
548	asl = asl_open(getprogname(), GSSD_FACILITY, 0);
549	asl_set_filter(asl, ASL_INIT_FILTER);
550
551	rerr = regcomp(&pre, prfrexp, REG_EXTENDED);
552	if (rerr)
553		regerr(rerr, &pre);
554#ifndef GSSD_LOG_DEBUG
555	rerr = regcomp(&gre, gssrexp, REG_EXTENDED | REG_NOSUB);
556	if (rerr)
557		regerr(rerr, &gre);
558#endif
559}
560
561void
562fatal(const char *fmt, ...)
563{
564	va_list ap;
565
566	va_start(ap, fmt);
567	asl_vlog(asl, NULL, ASL_LEVEL_ERR, fmt, ap);
568	va_end(ap);
569	exit(1);
570}
571
572static int debug = 0;
573static void (*disable_timeout)(int);
574
575/*
576 * set a callback to disable the processes timeout.
577 * If the argument is zero reenable the time out
578 * else disable the timeout.
579 */
580void set_debug_level_init(void (*dto)(int))
581{
582	disable_timeout = dto;
583}
584
585int get_debug_level(void)
586{
587	return (debug);
588}
589
590/*
591 * Set the debug level and filter mask to allow asl debug messages.
592 * This routine is called at startup and from the signal handler thread.
593 * That thread registers for notifications from syslog the master or remote
594 * filter have been install/removed.
595 *
596 * - debug level 0 - turn off debugging and do not send messages syslogd
597 * - debug level 1 - set debugging to 1 and log to syslog at info level
598 * - debug level >= 2 set the debugging level and log to syslogd at debug level
599 *
600 * - debug level -1, set the debug level to what the asl log level allows.
601 *   N.B. If the active asl log level is debug and the current debug level is
602 *   greater than 1, log at the current debug level. If the previous active filter
603 *   was remote and the current active filter is local turn off debug. This would
604 *   indicate that someone explicitly is turning off debuging with syslog -c gssd off.
605 *
606 *   Normally seting a debug level greater than zero will stop gssd from timing out.
607 *   The exception is that we are turning debugging on because of a master filter notification.
608 *   in that case we leave the time out setting alone. As long as the master filter is
609 *   active and is greater than notice, we will log to syslog in each startup of gssd.
610 *
611 *   Limitations: Only one filter is active at a time. The filter priorities are
612 *	remote > master > local.
613 *	Raising the debug level with SIGUSR1 will have no apparent effect if the
614 *	remote or master filter is the active filter and if the filter does not have
615 *	debug set. If info is set you will see  only info messages.
616 */
617void
618set_debug_level(int debug_level)
619{
620	int filter, local, master, remote, active = 0;
621	int status;
622	static int last_active = 0;
623
624	pthread_once(&ronce, gssd_log_init);
625	if (debug_level < 0) {
626		/*
627		 * We've got notified by syslog that our filter mask have changed.
628		 * Use that to determine the debug level.
629		 */
630		status = asl_get_filter(asl, &local, &master, &remote, &active);
631		if (status) {
632			Log("asl_get_filter failed\n");
633			return;
634		}
635
636		//Log("l = %x m = %x r = %x active = %d, last_active = %d", local, master, remote, active, last_active);
637		switch (active) {
638			case 0:	filter = local;
639				break;
640			case 1: filter = master;
641				/*
642				 * If someone turns on global debugging we don't
643				 * alter whether we time out or not. If global
644				 * debugging was in effect when gssd started, we
645				 * will continue to send debug output as long as
646				 * interest in global debugging is in effect.
647				 * If global debugging is enabled after explicit debugging
648				 * was enabled either with a remote filter or signal then
649				 * we should keep the current timout behavor until the user
650				 * turns off explicit debugging.
651				 */
652				break;
653			case 2: filter = remote;
654				break;
655			default:
656				Log("Unkown active ASL filter %d", active);
657				return;
658		}
659		if (ASL_FILTER_MASK(ASL_LEVEL_DEBUG) & filter)
660			debug_level = maximum(debug, 2);
661		else if (ASL_FILTER_MASK(ASL_LEVEL_INFO) & filter)
662			debug_level = 1;
663		else
664			debug_level = 0;
665		if (last_active == 2 && active == 0) {
666			/*
667			 * We got here because the user gave the command
668			 * syslog -c gssd off, so the user as explicitly
669			 * told us, she is no longer interested in debugging.
670			 * turn debugging off.
671			 */
672			debug_level = 0;
673		}
674		last_active = active;
675	}
676	if (debug == debug_level)
677		return; /* Nothing has changed. */
678
679	if (active == 0) {
680		switch (debug_level) {
681		case 0:	/* Debug has been turned off. */
682			local = ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE);
683			break;
684		case 1:	/* Debug hs been set to 1 turn on INFO level loging. */
685			local = ASL_FILTER_MASK_UPTO(ASL_LEVEL_INFO) | ASL_FILTER_MASK_TUNNEL;
686			break;
687		default:	/* Anything else turn on DEBUG level logging. */
688			local = ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG) | ASL_FILTER_MASK_TUNNEL;
689			break;
690		}
691		asl_set_filter(asl, local);
692	}
693
694	debug = debug_level;
695	if (debug == 0)
696		gssd_log(ASL_LEVEL_NOTICE, "Leaving debug mode");
697	if (active != 1 && disable_timeout)
698		disable_timeout(debug != 0);
699}
700
701static void
702g_vlog(int level, const char *fmt, va_list ap)
703{
704	int saved_errno = errno;
705
706	if (foreground) {
707		vfprintf(stderr, fmt, ap);
708		fflush(stderr);
709	} else {
710		if (istraced && isatty(1)) {
711			va_list ap2;
712			va_copy(ap2, ap);
713			vprintf(fmt, ap2);
714			fflush(stdout);
715			va_end(ap2);
716		}
717		asl_vlog(asl, NULL, level, fmt, ap);
718	}
719
720	errno = saved_errno;
721}
722
723static void
724g_log(int level, const char *fmt, ...)
725{
726	va_list ap;
727
728	va_start(ap, fmt);
729	g_vlog(level, fmt, ap);
730	va_end(ap);
731}
732
733#ifndef GSSD_LOG_DEBUG
734static int
735has_gss_conv(const char *fmt)
736{
737	int rerr;
738
739	rerr = regexec(&gre, fmt, 0, NULL, 0);
740
741	if (rerr == REG_NOMATCH)
742		return (FALSE);
743	if (rerr)
744		regerr(rerr, &gre);
745
746	return (TRUE);
747}
748#endif
749
750static int
751vslprintf(char *buf, size_t size, const char *fmt, va_list ap)
752{
753	size_t n = strlen(buf);
754	int rv;
755
756	if (n >= size)
757		return (0);
758
759	size -= n;
760	rv = vsnprintf(&buf[n], size, fmt, ap);
761	return (((size_t)rv > size) ? (int)size : rv);
762}
763
764static int
765slprintf(char *buf, size_t size, const char *fmt, ...)
766{
767	va_list ap;
768	int rv;
769
770	va_start(ap, fmt);
771	rv = vslprintf(buf, size, fmt, ap);
772	va_end(ap);
773
774	return (rv);
775}
776
777static size_t
778strlncat(char *buf, const char *s, size_t bufsize, size_t slen)
779{
780	char *d;
781	size_t n = strlen(buf);
782	size_t i;
783
784	if (slen + n >= bufsize)
785		Fatal("copy to big\n");
786
787	d = &buf[n];
788	for (i = 0; i < slen && *s && (n + i < bufsize - 1); i++) {
789		*d++ = *s++;
790	}
791	*d = '\0';
792
793	return (d - buf);
794}
795
796static size_t
797strlncpy(char *buf, const char *s, size_t bufsize, size_t slen)
798{
799	*buf = '\0';
800	return (strlncat(buf, s, bufsize, slen));
801}
802
803#define MAX_FMT 256
804#define MAX_LOG_MESSAGE 1024
805
806/*
807 * Advance the va_list given a printf style format.
808 * We will make a copy of our argment list and pass that to slprintf.
809 * We then need to advance ap by the same amount.
810 */
811static void
812va_next(va_list *ap, const char *fmt, regmatch_t match[PRF_FIELDS])
813{
814	char ftype;
815	char tlen[3];
816	regoff_t i, j;
817
818	ftype = fmt[match[PRF_TYPE].rm_so];
819	for (i = match[PRF_LENGTH].rm_so, j = 0; i < match[PRF_LENGTH].rm_eo; i++, j++) {
820		tlen[j] = fmt[i];
821	}
822	tlen[j] = '\0';
823	if (match[PRF_WIDTH].rm_eo - match[PRF_WIDTH].rm_so == 1 &&
824	    fmt[match[PRF_WIDTH].rm_so] == '*')
825		va_arg(*ap, int);
826	if (match[PRF_PREC_SPEC].rm_eo - match[PRF_PREC_SPEC].rm_so == 1 &&
827	    fmt[match[PRF_PREC_SPEC].rm_so] == '*')
828		va_arg(*ap, int);
829
830	switch(ftype) {
831	/* Shamelessly assume unsigned sizes are the same size as signed ones. */
832	case 'd':
833	case 'i':
834	case 'o':
835	case 'u':
836	case 'x':
837	case 'X':
838		if (tlen[0] == 'h' && tlen[1] == 'h')
839			va_arg(*ap, int);
840		else if (tlen[0] == 'l' && tlen[1] == 'l')
841			va_arg(*ap, long long);
842		else if (*tlen == 'l')
843			va_arg(*ap, long);
844		else if (*tlen == 'j')
845			va_arg(*ap, intmax_t);
846		else if (*tlen == 't')
847			va_arg(*ap, ptrdiff_t);
848		else if (*tlen == 'z')
849			va_arg(*ap, size_t);
850		else if (*tlen == 'q')
851			va_arg(*ap, quad_t);
852		else
853			/* tlen in NULL or invalid and ignored */
854			va_arg(*ap, int);
855		break;
856	case 'D':
857	case 'O':
858	case 'U':
859		va_arg(*ap, long);
860		break;
861	case 'a':
862	case 'A':
863	case 'e':
864	case 'E':
865	case 'f':
866	case 'F':
867	case 'g':
868	case 'G':
869		if (*tlen == 'L')
870			va_arg(*ap, long double);
871		else
872			va_arg(*ap, double);
873		break;
874	case 'c':
875		if (*tlen == 'l' && tlen[1] == '\0')
876			va_arg(*ap, wchar_t);
877		else
878			va_arg(*ap, int);
879		break;
880	case 's':
881		if (*tlen == 'l' && tlen[1] == '\0')
882			va_arg(*ap, wchar_t *);
883		else
884			va_arg(*ap, char *);
885		break;
886	case 'C':
887		va_arg(*ap, wchar_t);
888		break;
889	case 'S':
890		va_arg(*ap, wchar_t);
891		break;
892	case 'p':
893		va_arg(*ap, void *);
894		break;
895	case '%':
896	default:
897		break;
898	}
899}
900
901static const char *
902fmt_parse(char *out_buffer, char **obp, const char *ofp, va_list *ap)
903{
904	int rerr;
905	regmatch_t match[PRF_FIELDS];
906	char tconv;
907	size_t mlen;
908	const char *mstr;
909	char kfmt[MAX_FMT];
910
911	rerr = regexec(&pre, ofp, PRF_FIELDS, match, 0);
912	if (rerr == REG_NOMATCH)
913		return (NULL);  /* Invalid format return NULL */
914	else if (rerr)
915		regerr(rerr, &pre); /* Fatal error doesn't return, should never happen */
916
917	tconv = ofp[match[PRF_TYPE].rm_so];
918	mlen = (size_t)(match[PRF_WHOLE_MATCH].rm_eo - match[PRF_WHOLE_MATCH].rm_so);
919	mstr = &ofp[match[PRF_WHOLE_MATCH].rm_so];
920	if (mlen >= MAX_FMT)
921		return (NULL);
922	if (tconv == 'k' || tconv == 'K') {
923		regoff_t i;
924		gss_OID oid;
925		char *gss_error_string;
926		uint32_t flag = 0;
927		uint32_t code;
928		int prec = -1, width = -1;
929
930		// handle 'k|K' format stream fromt ap to output buffer
931		for (i = match[PRF_FLAGS].rm_so; i < match[PRF_FLAGS].rm_eo; i++) {
932			if (ofp[i] == '#') {
933				flag++;
934				break;
935			}
936		}
937		if (match[PRF_WIDTH].rm_eo - match[PRF_WIDTH].rm_so == 1 &&
938		    ofp[match[PRF_WIDTH].rm_so] == '*')
939			width = va_arg(*ap, int);
940
941		if (match[PRF_PREC_SPEC].rm_eo - match[PRF_PREC_SPEC].rm_so == 1 &&
942		    ofp[match[PRF_PREC_SPEC].rm_so] == '*')
943			prec = va_arg(*ap, int);
944
945		oid = (tconv == 'k') ? va_arg(*ap, gss_OID) : GSS_C_NO_OID;
946		code = va_arg(*ap, uint32_t);
947		gss_error_string = gss_strerror(oid, code, flag);
948		kfmt[0] = '\0';
949		strlncpy(kfmt, mstr, MAX_FMT, mlen);
950		// Change the 'k' to 's'
951		kfmt[strnlen(kfmt, MAX_FMT) - 1 ] = 's';
952		if (width > -1 && prec > -1)
953			*obp += slprintf(out_buffer, MAX_LOG_MESSAGE, kfmt, width, prec, gss_error_string);
954		else if (width > -1)
955			*obp += slprintf(out_buffer, MAX_LOG_MESSAGE, kfmt, width, gss_error_string);
956		else if (prec > -1)
957			*obp += slprintf(out_buffer, MAX_LOG_MESSAGE, kfmt, prec, gss_error_string);
958		else
959			*obp += slprintf(out_buffer, MAX_LOG_MESSAGE, kfmt, gss_error_string);
960		free(gss_error_string);
961	} else {
962		va_list ap2;
963		va_copy(ap2, *ap);
964		strlncpy(kfmt, mstr, MAX_FMT, mlen);
965		*obp += vslprintf(out_buffer, MAX_LOG_MESSAGE, kfmt, ap2);
966		va_end(ap2);
967		va_next(ap, ofp, match); /* Eat the arguments printed */
968	}
969	ofp += match[PRF_WHOLE_MATCH].rm_eo;
970
971	return (ofp);
972}
973
974void
975gssd_log(int log_level, const char *fmt, ...)
976{
977	int saved_errno = errno;
978	const char *ofp = fmt;
979	char output_buffer[MAX_LOG_MESSAGE];
980	char *obp = output_buffer;
981	va_list ap;
982
983	pthread_once(&ronce, gssd_log_init);
984
985	va_start(ap, fmt);
986#ifndef GSSD_LOG_DEBUG
987	if (!has_gss_conv(fmt) && 0) {
988		g_vlog(log_level, fmt, ap);
989		va_end(ap);
990		return;
991	}
992#endif
993
994	*obp = '\0';
995	while (*ofp) {
996		if (*ofp != '%' ) {
997			if (obp - output_buffer < MAX_LOG_MESSAGE - 1) {
998				*obp++ = *ofp++;
999				*obp = '\0';
1000			}
1001		} else {
1002			ofp = fmt_parse(output_buffer, &obp, ofp, &ap);
1003			if (ofp == NULL) {
1004				g_log(ASL_LEVEL_ERR, "Invalid log format %s skipping ...\n", fmt);
1005				return;
1006			}
1007		}
1008	}
1009
1010	va_end(ap);
1011
1012	if (*output_buffer) {
1013		size_t len = strlen(output_buffer);
1014		g_log(log_level, output_buffer[len -1] == '\n' ? "%s" : "%s\n", output_buffer);
1015	}
1016
1017	errno = saved_errno;
1018}
1019
1020#if 0
1021/*
1022 * Display the major and minor GSS return codes from routine.
1023 */
1024void
1025display_GSS_err(char* rtnName, gss_OID mech, OM_uint32 maj, OM_uint32 min, int display_debug)
1026{
1027	OM_uint32 msg_context = 0;
1028	OM_uint32 min_stat = 0;
1029	OM_uint32 maj_stat = 0;
1030	gss_buffer_desc errBuf;
1031	char *str;
1032	int count = 1;
1033
1034	if (maj == GSS_S_NO_CRED)
1035		display_debug = 1;
1036
1037	MSG(display_debug, "Error returned by %s:\n", rtnName);
1038	do {
1039		maj_stat = gss_display_status(&min_stat, maj, GSS_C_GSS_CODE,
1040					      mech, &msg_context, &errBuf);
1041		str = (maj_stat == GSS_S_COMPLETE) ? buf_to_str(&errBuf) : NULL;
1042		if (count == 1)
1043			MSG(display_debug, "\tMajor error = %d: %s\n", maj, str ? str : "");
1044		else
1045			MSG(display_debug, "\t\t%s\n", str ? str : "");
1046		free(str);
1047		++count;
1048	} while (msg_context != 0);
1049
1050	count = 1;
1051	msg_context = 0;
1052	do {
1053		maj_stat = gss_display_status (&min_stat, min, GSS_C_MECH_CODE,
1054					       mech, &msg_context, &errBuf);
1055		str = (maj_stat == GSS_S_COMPLETE) ? buf_to_str(&errBuf) : NULL;
1056		if (count == 1)
1057			MSG(display_debug, "\tMinor error = %d: %s\n", min, str ? str : "");
1058		else
1059			MSG(display_debug, "\t\t%s\n", str ? str : "");
1060		free(str);
1061		++count;
1062	} while (msg_context != 0);
1063}
1064#endif
1065
1066static const char HexChars[16] = {
1067	'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
1068};
1069
1070/*
1071 * Dump 16 bytes or bufSize bytes (<16) to line buf in hex followed by
1072 * character representation.
1073 */
1074static void
1075HexLine(const char *buf, size_t *bufSize, char linebuf[80])
1076{
1077	const char *bptr = buf;
1078	size_t	limit;
1079	size_t	i;
1080	char	*cptr = linebuf;
1081
1082	memset(linebuf, 0, 80);
1083
1084	limit = (*bufSize > 16) ? 16 : *bufSize;
1085	*bufSize -= limit;
1086
1087	for(i = 0; i < 16; i++)
1088	{
1089		if(i < limit)
1090		{
1091			*cptr++ = HexChars[(*bptr >> 4) & 0x0f];
1092			*cptr++ = HexChars[*bptr & 0x0f];
1093			*cptr++ = ' ';
1094			bptr++;
1095		} else {
1096			*cptr++ = ' ';
1097			*cptr++ = ' ';
1098			*cptr++ = ' ';
1099
1100		}
1101	}
1102	bptr = buf;
1103	*cptr++ = ' ';
1104	*cptr++ = ' ';
1105	*cptr++ = ' ';
1106	for(i = 0; i < limit; i++)
1107	{
1108		*cptr++ = (char) (((*bptr > 0x1f) && (*bptr < 0x7f)) ? *bptr : '.');
1109		bptr++;
1110	}
1111	*cptr++ = '\n';
1112	*cptr = '\0';
1113}
1114
1115/*
1116 * Dump the supplied buffer in hex.
1117 */
1118void
1119HexDump(const char *inBuffer, size_t inLength)
1120{
1121	size_t currentSize = inLength;
1122	char linebuf[80];
1123
1124	while(currentSize > 0)
1125	{
1126		HexLine(inBuffer, &currentSize, linebuf);
1127		gssd_log(ASL_LEVEL_DEBUG, "\t%s", linebuf);
1128		inBuffer += 16;
1129	}
1130}
1131
1132#if 0
1133/* LogToMessageTracer.
1134 * Currently not used, but we may want this in the future.
1135 * At any rate this apparently is how it is done.
1136 */
1137
1138void LogToMessageTracer(const char *domain, const char *signature,
1139						const char *optResult, const char *optValue,
1140						const char *fmt,...)
1141{
1142	aslmsg m;
1143	va_list ap;
1144
1145	if ( (domain == NULL) || (signature == NULL) || (fmt == NULL) ) {
1146		/* domain, signature and msg are required */
1147		return;
1148	}
1149
1150	m = asl_new(ASL_TYPE_MSG);
1151	asl_set(m, "com.apple.message.domain", domain);
1152	asl_set(m, "com.apple.message.signature", signature);
1153
1154	if (optResult != NULL) {
1155		asl_set(m, "com.apple.message.result", optResult);
1156	}
1157	if (optValue != NULL) {
1158		asl_set(m, "com.apple.message.value", optValue);
1159	}
1160
1161	va_start(ap, fmt);
1162	asl_vlog(NULL, m, ASL_LEVEL_NOTICE, fmt, ap);
1163	va_end(ap);
1164
1165	asl_free(m);
1166}
1167#endif
1168