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