gssapictx.c revision 193149
1/*
2 * Copyright (C) 2004-2008  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000, 2001  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* $Id: gssapictx.c,v 1.12 2008/04/03 06:09:04 tbox Exp $ */
19
20#include <config.h>
21
22#include <stdlib.h>
23#include <string.h>
24
25#include <isc/buffer.h>
26#include <isc/dir.h>
27#include <isc/entropy.h>
28#include <isc/lex.h>
29#include <isc/mem.h>
30#include <isc/once.h>
31#include <isc/print.h>
32#include <isc/random.h>
33#include <isc/string.h>
34#include <isc/time.h>
35#include <isc/util.h>
36
37#include <dns/fixedname.h>
38#include <dns/name.h>
39#include <dns/rdata.h>
40#include <dns/rdataclass.h>
41#include <dns/result.h>
42#include <dns/types.h>
43#include <dns/keyvalues.h>
44#include <dns/log.h>
45
46#include <dst/gssapi.h>
47#include <dst/result.h>
48
49#include "dst_internal.h"
50
51/*
52 * If we're using our own SPNEGO implementation (see configure.in),
53 * pull it in now.  Otherwise, we just use whatever GSSAPI supplies.
54 */
55#if defined(GSSAPI) && defined(USE_ISC_SPNEGO)
56#include "spnego.h"
57#define	gss_accept_sec_context	gss_accept_sec_context_spnego
58#define	gss_init_sec_context	gss_init_sec_context_spnego
59#endif
60
61/*
62 * Solaris8 apparently needs an explicit OID set, and Solaris10 needs
63 * one for anything but Kerberos.  Supplying an explicit OID set
64 * doesn't appear to hurt anything in other implementations, so we
65 * always use one.  If we're not using our own SPNEGO implementation,
66 * we include SPNEGO's OID.
67 */
68#if defined(GSSAPI)
69
70static unsigned char krb5_mech_oid_bytes[] = {
71	0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02
72};
73
74#ifndef USE_ISC_SPNEGO
75static unsigned char spnego_mech_oid_bytes[] = {
76	0x2b, 0x06, 0x01, 0x05, 0x05, 0x02
77};
78#endif
79
80static gss_OID_desc mech_oid_set_array[] = {
81	{ sizeof(krb5_mech_oid_bytes), krb5_mech_oid_bytes },
82#ifndef USE_ISC_SPNEGO
83	{ sizeof(spnego_mech_oid_bytes), spnego_mech_oid_bytes },
84#endif
85};
86
87static gss_OID_set_desc mech_oid_set = {
88	sizeof(mech_oid_set_array) / sizeof(*mech_oid_set_array),
89	mech_oid_set_array
90};
91
92#endif
93
94#define REGION_TO_GBUFFER(r, gb) \
95	do { \
96		(gb).length = (r).length; \
97		(gb).value = (r).base; \
98	} while (0)
99
100#define GBUFFER_TO_REGION(gb, r) \
101	do { \
102		(r).length = (gb).length; \
103		(r).base = (gb).value; \
104	} while (0)
105
106
107#define RETERR(x) do { \
108	result = (x); \
109	if (result != ISC_R_SUCCESS) \
110		goto out; \
111	} while (0)
112
113#ifdef GSSAPI
114static inline void
115name_to_gbuffer(dns_name_t *name, isc_buffer_t *buffer,
116		gss_buffer_desc *gbuffer)
117{
118	dns_name_t tname, *namep;
119	isc_region_t r;
120	isc_result_t result;
121
122	if (!dns_name_isabsolute(name))
123		namep = name;
124	else
125	{
126		unsigned int labels;
127		dns_name_init(&tname, NULL);
128		labels = dns_name_countlabels(name);
129		dns_name_getlabelsequence(name, 0, labels - 1, &tname);
130		namep = &tname;
131	}
132
133	result = dns_name_totext(namep, ISC_FALSE, buffer);
134	isc_buffer_putuint8(buffer, 0);
135	isc_buffer_usedregion(buffer, &r);
136	REGION_TO_GBUFFER(r, *gbuffer);
137}
138
139static void
140log_cred(const gss_cred_id_t cred) {
141	OM_uint32 gret, minor, lifetime;
142	gss_name_t gname;
143	gss_buffer_desc gbuffer;
144	gss_cred_usage_t usage;
145	const char *usage_text;
146	char buf[1024];
147
148	gret = gss_inquire_cred(&minor, cred, &gname, &lifetime, &usage, NULL);
149	if (gret != GSS_S_COMPLETE) {
150		gss_log(3, "failed gss_inquire_cred: %s",
151			gss_error_tostring(gret, minor, buf, sizeof(buf)));
152		return;
153	}
154
155	gret = gss_display_name(&minor, gname, &gbuffer, NULL);
156	if (gret != GSS_S_COMPLETE)
157		gss_log(3, "failed gss_display_name: %s",
158			gss_error_tostring(gret, minor, buf, sizeof(buf)));
159	else {
160		switch (usage) {
161		case GSS_C_BOTH:
162			usage_text = "GSS_C_BOTH";
163			break;
164		case GSS_C_INITIATE:
165			usage_text = "GSS_C_INITIATE";
166			break;
167		case GSS_C_ACCEPT:
168			usage_text = "GSS_C_ACCEPT";
169			break;
170		default:
171			usage_text = "???";
172		}
173		gss_log(3, "gss cred: \"%s\", %s, %lu", (char *)gbuffer.value,
174			usage_text, (unsigned long)lifetime);
175	}
176
177	if (gret == GSS_S_COMPLETE) {
178		if (gbuffer.length != 0) {
179			gret = gss_release_buffer(&minor, &gbuffer);
180			if (gret != GSS_S_COMPLETE)
181				gss_log(3, "failed gss_release_buffer: %s",
182					gss_error_tostring(gret, minor, buf,
183							   sizeof(buf)));
184		}
185	}
186
187	gret = gss_release_name(&minor, &gname);
188	if (gret != GSS_S_COMPLETE)
189		gss_log(3, "failed gss_release_name: %s",
190			gss_error_tostring(gret, minor, buf, sizeof(buf)));
191}
192#endif
193
194isc_result_t
195dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate,
196		       gss_cred_id_t *cred)
197{
198#ifdef GSSAPI
199	isc_buffer_t namebuf;
200	gss_name_t gname;
201	gss_buffer_desc gnamebuf;
202	unsigned char array[DNS_NAME_MAXTEXT + 1];
203	OM_uint32 gret, minor;
204	gss_OID_set mechs;
205	OM_uint32 lifetime;
206	gss_cred_usage_t usage;
207	char buf[1024];
208
209	REQUIRE(cred != NULL && *cred == NULL);
210
211	/*
212	 * XXXSRA In theory we could use GSS_C_NT_HOSTBASED_SERVICE
213	 * here when we're in the acceptor role, which would let us
214	 * default the hostname and use a compiled in default service
215	 * name of "DNS", giving one less thing to configure in
216	 * named.conf.  Unfortunately, this creates a circular
217	 * dependency due to DNS-based realm lookup in at least one
218	 * GSSAPI implementation (Heimdal).  Oh well.
219	 */
220	if (name != NULL) {
221		isc_buffer_init(&namebuf, array, sizeof(array));
222		name_to_gbuffer(name, &namebuf, &gnamebuf);
223		gret = gss_import_name(&minor, &gnamebuf,
224				       GSS_C_NO_OID, &gname);
225		if (gret != GSS_S_COMPLETE) {
226			gss_log(3, "failed gss_import_name: %s",
227				gss_error_tostring(gret, minor, buf,
228						   sizeof(buf)));
229			return (ISC_R_FAILURE);
230		}
231	} else
232		gname = NULL;
233
234	/* Get the credentials. */
235	if (gname != NULL)
236		gss_log(3, "acquiring credentials for %s",
237			(char *)gnamebuf.value);
238	else {
239		/* XXXDCL does this even make any sense? */
240		gss_log(3, "acquiring credentials for ?");
241	}
242
243	if (initiate)
244		usage = GSS_C_INITIATE;
245	else
246		usage = GSS_C_ACCEPT;
247
248	gret = gss_acquire_cred(&minor, gname, GSS_C_INDEFINITE,
249				&mech_oid_set,
250				usage, cred, &mechs, &lifetime);
251
252	if (gret != GSS_S_COMPLETE) {
253		gss_log(3, "failed to acquire %s credentials for %s: %s",
254			initiate ? "initiate" : "accept",
255			(char *)gnamebuf.value,
256			gss_error_tostring(gret, minor, buf, sizeof(buf)));
257		return (ISC_R_FAILURE);
258	}
259
260	gss_log(4, "acquired %s credentials for %s",
261		initiate ? "initiate" : "accept",
262		(char *)gnamebuf.value);
263
264	log_cred(*cred);
265
266	return (ISC_R_SUCCESS);
267#else
268	UNUSED(name);
269	UNUSED(initiate);
270	UNUSED(cred);
271
272	return (ISC_R_NOTIMPLEMENTED);
273#endif
274}
275
276isc_boolean_t
277dst_gssapi_identitymatchesrealmkrb5(dns_name_t *signer, dns_name_t *name,
278				    dns_name_t *realm)
279{
280#ifdef GSSAPI
281	char sbuf[DNS_NAME_FORMATSIZE];
282	char nbuf[DNS_NAME_FORMATSIZE];
283	char rbuf[DNS_NAME_FORMATSIZE];
284	char *sname;
285	char *rname;
286
287	/*
288	 * It is far, far easier to write the names we are looking at into
289	 * a string, and do string operations on them.
290	 */
291	dns_name_format(signer, sbuf, sizeof(sbuf));
292	if (name != NULL)
293		dns_name_format(name, nbuf, sizeof(nbuf));
294	dns_name_format(realm, rbuf, sizeof(rbuf));
295
296	/*
297	 * Find the realm portion.  This is the part after the @.  If it
298	 * does not exist, we don't have something we like, so we fail our
299	 * compare.
300	 */
301	rname = strstr(sbuf, "\\@");
302	if (rname == NULL)
303		return (isc_boolean_false);
304	*rname = '\0';
305	rname += 2;
306
307	/*
308	 * Find the host portion of the signer's name.  We do this by
309	 * searching for the first / character.  We then check to make
310	 * certain the instance name is "host"
311	 *
312	 * This will work for
313	 *    host/example.com@EXAMPLE.COM
314	 */
315	sname = strchr(sbuf, '/');
316	if (sname == NULL)
317		return (isc_boolean_false);
318	*sname = '\0';
319	sname++;
320	if (strcmp(sbuf, "host") != 0)
321		return (isc_boolean_false);
322
323	/*
324	 * Now, we do a simple comparison between the name and the realm.
325	 */
326	if (name != NULL) {
327		if ((strcasecmp(sname, nbuf) == 0)
328		    && (strcmp(rname, rbuf) == 0))
329			return (isc_boolean_true);
330	} else {
331		if (strcmp(rname, rbuf) == 0)
332			return (isc_boolean_true);
333	}
334
335	return (isc_boolean_false);
336#else
337	UNUSED(signer);
338	UNUSED(name);
339	UNUSED(realm);
340	return (isc_boolean_false);
341#endif
342}
343
344isc_boolean_t
345dst_gssapi_identitymatchesrealmms(dns_name_t *signer, dns_name_t *name,
346				  dns_name_t *realm)
347{
348#ifdef GSSAPI
349	char sbuf[DNS_NAME_FORMATSIZE];
350	char nbuf[DNS_NAME_FORMATSIZE];
351	char rbuf[DNS_NAME_FORMATSIZE];
352	char *sname;
353	char *nname;
354	char *rname;
355
356	/*
357	 * It is far, far easier to write the names we are looking at into
358	 * a string, and do string operations on them.
359	 */
360	dns_name_format(signer, sbuf, sizeof(sbuf));
361	if (name != NULL)
362		dns_name_format(name, nbuf, sizeof(nbuf));
363	dns_name_format(realm, rbuf, sizeof(rbuf));
364
365	/*
366	 * Find the realm portion.  This is the part after the @.  If it
367	 * does not exist, we don't have something we like, so we fail our
368	 * compare.
369	 */
370	rname = strstr(sbuf, "\\@");
371	if (rname == NULL)
372		return (isc_boolean_false);
373	sname = strstr(sbuf, "\\$");
374	if (sname == NULL)
375		return (isc_boolean_false);
376
377	/*
378	 * Verify that the $ and @ follow one another.
379	 */
380	if (rname - sname != 2)
381		return (isc_boolean_false);
382
383	/*
384	 * Find the host portion of the signer's name.  Zero out the $ so
385	 * it terminates the signer's name, and skip past the @ for
386	 * the realm.
387	 *
388	 * All service principals in Microsoft format seem to be in
389	 *    machinename$@EXAMPLE.COM
390	 * format.
391	 */
392	*rname = '\0';
393	rname += 2;
394	*sname = '\0';
395	sname = sbuf;
396
397	/*
398	 * Find the first . in the target name, and make it the end of
399	 * the string.   The rest of the name has to match the realm.
400	 */
401	if (name != NULL) {
402		nname = strchr(nbuf, '.');
403		if (nname == NULL)
404			return (isc_boolean_false);
405		*nname++ = '\0';
406	}
407
408	/*
409	 * Now, we do a simple comparison between the name and the realm.
410	 */
411	if (name != NULL) {
412		if ((strcasecmp(sname, nbuf) == 0)
413		    && (strcmp(rname, rbuf) == 0)
414		    && (strcasecmp(nname, rbuf) == 0))
415			return (isc_boolean_true);
416	} else {
417		if (strcmp(rname, rbuf) == 0)
418			return (isc_boolean_true);
419	}
420
421
422	return (isc_boolean_false);
423#else
424	UNUSED(signer);
425	UNUSED(name);
426	UNUSED(realm);
427	return (isc_boolean_false);
428#endif
429}
430
431isc_result_t
432dst_gssapi_releasecred(gss_cred_id_t *cred) {
433#ifdef GSSAPI
434	OM_uint32 gret, minor;
435	char buf[1024];
436
437	REQUIRE(cred != NULL && *cred != NULL);
438
439	gret = gss_release_cred(&minor, cred);
440	if (gret != GSS_S_COMPLETE) {
441		/* Log the error, but still free the credential's memory */
442		gss_log(3, "failed releasing credential: %s",
443			gss_error_tostring(gret, minor, buf, sizeof(buf)));
444	}
445	*cred = NULL;
446
447	return(ISC_R_SUCCESS);
448#else
449	UNUSED(cred);
450
451	return (ISC_R_NOTIMPLEMENTED);
452#endif
453}
454
455isc_result_t
456dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
457		   isc_buffer_t *outtoken, gss_ctx_id_t *gssctx)
458{
459#ifdef GSSAPI
460	isc_region_t r;
461	isc_buffer_t namebuf;
462	gss_name_t gname;
463	OM_uint32 gret, minor, ret_flags, flags;
464	gss_buffer_desc gintoken, *gintokenp, gouttoken = GSS_C_EMPTY_BUFFER;
465	isc_result_t result;
466	gss_buffer_desc gnamebuf;
467	unsigned char array[DNS_NAME_MAXTEXT + 1];
468	char buf[1024];
469
470	/* Client must pass us a valid gss_ctx_id_t here */
471	REQUIRE(gssctx != NULL);
472
473	isc_buffer_init(&namebuf, array, sizeof(array));
474	name_to_gbuffer(name, &namebuf, &gnamebuf);
475
476	/* Get the name as a GSS name */
477	gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname);
478	if (gret != GSS_S_COMPLETE) {
479		result = ISC_R_FAILURE;
480		goto out;
481	}
482
483	if (intoken != NULL) {
484		/* Don't call gss_release_buffer for gintoken! */
485		REGION_TO_GBUFFER(*intoken, gintoken);
486		gintokenp = &gintoken;
487	} else {
488		gintokenp = NULL;
489	}
490
491	flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG |
492		GSS_C_SEQUENCE_FLAG | GSS_C_INTEG_FLAG;
493
494	gret = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, gssctx,
495				    gname, GSS_SPNEGO_MECHANISM, flags,
496				    0, NULL, gintokenp,
497				    NULL, &gouttoken, &ret_flags, NULL);
498
499	if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED) {
500		gss_log(3, "Failure initiating security context");
501		gss_log(3, "%s", gss_error_tostring(gret, minor,
502						    buf, sizeof(buf)));
503		result = ISC_R_FAILURE;
504		goto out;
505	}
506
507	/*
508	 * XXXSRA Not handled yet: RFC 3645 3.1.1: check ret_flags
509	 * MUTUAL and INTEG flags, fail if either not set.
510	 */
511
512	/*
513	 * RFC 2744 states the a valid output token has a non-zero length.
514	 */
515	if (gouttoken.length != 0) {
516		GBUFFER_TO_REGION(gouttoken, r);
517		RETERR(isc_buffer_copyregion(outtoken, &r));
518		(void)gss_release_buffer(&minor, &gouttoken);
519	}
520	(void)gss_release_name(&minor, &gname);
521
522	if (gret == GSS_S_COMPLETE)
523		result = ISC_R_SUCCESS;
524	else
525		result = DNS_R_CONTINUE;
526
527 out:
528	return (result);
529#else
530	UNUSED(name);
531	UNUSED(intoken);
532	UNUSED(outtoken);
533	UNUSED(gssctx);
534
535	return (ISC_R_NOTIMPLEMENTED);
536#endif
537}
538
539isc_result_t
540dst_gssapi_acceptctx(gss_cred_id_t cred,
541		     isc_region_t *intoken, isc_buffer_t **outtoken,
542		     gss_ctx_id_t *ctxout, dns_name_t *principal,
543		     isc_mem_t *mctx)
544{
545#ifdef GSSAPI
546	isc_region_t r;
547	isc_buffer_t namebuf;
548	gss_buffer_desc gnamebuf = GSS_C_EMPTY_BUFFER, gintoken,
549			gouttoken = GSS_C_EMPTY_BUFFER;
550	OM_uint32 gret, minor;
551	gss_ctx_id_t context = GSS_C_NO_CONTEXT;
552	gss_name_t gname = NULL;
553	isc_result_t result;
554	char buf[1024];
555
556	REQUIRE(outtoken != NULL && *outtoken == NULL);
557
558	log_cred(cred);
559
560	REGION_TO_GBUFFER(*intoken, gintoken);
561
562	if (*ctxout == NULL)
563		context = GSS_C_NO_CONTEXT;
564	else
565		context = *ctxout;
566
567	gret = gss_accept_sec_context(&minor, &context, cred, &gintoken,
568				      GSS_C_NO_CHANNEL_BINDINGS, &gname,
569				      NULL, &gouttoken, NULL, NULL, NULL);
570
571	result = ISC_R_FAILURE;
572
573	switch (gret) {
574	case GSS_S_COMPLETE:
575		result = ISC_R_SUCCESS;
576		break;
577	case GSS_S_CONTINUE_NEEDED:
578		result = DNS_R_CONTINUE;
579		break;
580	case GSS_S_DEFECTIVE_TOKEN:
581	case GSS_S_DEFECTIVE_CREDENTIAL:
582	case GSS_S_BAD_SIG:
583	case GSS_S_DUPLICATE_TOKEN:
584	case GSS_S_OLD_TOKEN:
585	case GSS_S_NO_CRED:
586	case GSS_S_CREDENTIALS_EXPIRED:
587	case GSS_S_BAD_BINDINGS:
588	case GSS_S_NO_CONTEXT:
589	case GSS_S_BAD_MECH:
590	case GSS_S_FAILURE:
591		result = DNS_R_INVALIDTKEY;
592		/* fall through */
593	default:
594		gss_log(3, "failed gss_accept_sec_context: %s",
595			gss_error_tostring(gret, minor, buf, sizeof(buf)));
596		return (result);
597	}
598
599	if (gouttoken.length > 0) {
600		RETERR(isc_buffer_allocate(mctx, outtoken, gouttoken.length));
601		GBUFFER_TO_REGION(gouttoken, r);
602		RETERR(isc_buffer_copyregion(*outtoken, &r));
603		(void)gss_release_buffer(&minor, &gouttoken);
604	}
605
606	if (gret == GSS_S_COMPLETE) {
607		gret = gss_display_name(&minor, gname, &gnamebuf, NULL);
608		if (gret != GSS_S_COMPLETE) {
609			gss_log(3, "failed gss_display_name: %s",
610				gss_error_tostring(gret, minor,
611						   buf, sizeof(buf)));
612			RETERR(ISC_R_FAILURE);
613		}
614
615		/*
616		 * Compensate for a bug in Solaris8's implementation
617		 * of gss_display_name().  Should be harmless in any
618		 * case, since principal names really should not
619		 * contain null characters.
620		 */
621		if (gnamebuf.length > 0 &&
622		    ((char *)gnamebuf.value)[gnamebuf.length - 1] == '\0')
623			gnamebuf.length--;
624
625		gss_log(3, "gss-api source name (accept) is %.*s",
626			(int)gnamebuf.length, (char *)gnamebuf.value);
627
628		GBUFFER_TO_REGION(gnamebuf, r);
629		isc_buffer_init(&namebuf, r.base, r.length);
630		isc_buffer_add(&namebuf, r.length);
631
632		RETERR(dns_name_fromtext(principal, &namebuf, dns_rootname,
633					 ISC_FALSE, NULL));
634
635		if (gnamebuf.length != 0) {
636			gret = gss_release_buffer(&minor, &gnamebuf);
637			if (gret != GSS_S_COMPLETE)
638				gss_log(3, "failed gss_release_buffer: %s",
639					gss_error_tostring(gret, minor, buf,
640							   sizeof(buf)));
641		}
642	}
643
644	*ctxout = context;
645
646 out:
647	if (gname != NULL) {
648		gret = gss_release_name(&minor, &gname);
649		if (gret != GSS_S_COMPLETE)
650			gss_log(3, "failed gss_release_name: %s",
651				gss_error_tostring(gret, minor, buf,
652						   sizeof(buf)));
653	}
654
655	return (result);
656#else
657	UNUSED(cred);
658	UNUSED(intoken);
659	UNUSED(outtoken);
660	UNUSED(ctxout);
661	UNUSED(principal);
662	UNUSED(mctx);
663
664	return (ISC_R_NOTIMPLEMENTED);
665#endif
666}
667
668isc_result_t
669dst_gssapi_deletectx(isc_mem_t *mctx, gss_ctx_id_t *gssctx)
670{
671#ifdef GSSAPI
672	OM_uint32 gret, minor;
673	char buf[1024];
674
675	UNUSED(mctx);
676
677	REQUIRE(gssctx != NULL && *gssctx != NULL);
678
679	/* Delete the context from the GSS provider */
680	gret = gss_delete_sec_context(&minor, gssctx, GSS_C_NO_BUFFER);
681	if (gret != GSS_S_COMPLETE) {
682		/* Log the error, but still free the context's memory */
683		gss_log(3, "Failure deleting security context %s",
684			gss_error_tostring(gret, minor, buf, sizeof(buf)));
685	}
686	return(ISC_R_SUCCESS);
687#else
688	UNUSED(mctx);
689	UNUSED(gssctx);
690	return (ISC_R_NOTIMPLEMENTED);
691#endif
692}
693
694char *
695gss_error_tostring(isc_uint32_t major, isc_uint32_t minor,
696		   char *buf, size_t buflen) {
697#ifdef GSSAPI
698	gss_buffer_desc msg_minor = GSS_C_EMPTY_BUFFER,
699			msg_major = GSS_C_EMPTY_BUFFER;
700	OM_uint32 msg_ctx, minor_stat;
701
702	/* Handle major status */
703	msg_ctx = 0;
704	(void)gss_display_status(&minor_stat, major, GSS_C_GSS_CODE,
705				 GSS_C_NULL_OID, &msg_ctx, &msg_major);
706
707	/* Handle minor status */
708	msg_ctx = 0;
709	(void)gss_display_status(&minor_stat, minor, GSS_C_MECH_CODE,
710				 GSS_C_NULL_OID, &msg_ctx, &msg_minor);
711
712	snprintf(buf, buflen, "GSSAPI error: Major = %s, Minor = %s.",
713		(char *)msg_major.value, (char *)msg_minor.value);
714
715	if (msg_major.length != 0)
716		(void)gss_release_buffer(&minor_stat, &msg_major);
717	if (msg_minor.length != 0)
718		(void)gss_release_buffer(&minor_stat, &msg_minor);
719	return(buf);
720#else
721	snprintf(buf, buflen, "GSSAPI error: Major = %u, Minor = %u.",
722		 major, minor);
723
724	return (buf);
725#endif
726}
727
728void
729gss_log(int level, const char *fmt, ...) {
730	va_list ap;
731
732	va_start(ap, fmt);
733	isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL,
734		       DNS_LOGMODULE_TKEY, ISC_LOG_DEBUG(level), fmt, ap);
735	va_end(ap);
736}
737
738/*! \file */
739