Import bind 9.5.2 vendor sources.
[dragonfly.git] / contrib / bind-9.5.2 / lib / dns / gssapictx.c
blob614d88f710c50adc3bf63475646d59c0cce23a27
1 /*
2 * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000, 2001 Internet Software Consortium.
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.
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.
18 /* $Id: gssapictx.c,v 1.8.128.4 2008/04/03 06:08:26 tbox Exp $ */
20 #include <config.h>
22 #include <stdlib.h>
23 #include <string.h>
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>
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>
46 #include <dst/gssapi.h>
47 #include <dst/result.h>
49 #include "dst_internal.h"
52 * If we're using our own SPNEGO implementation (see configure.in),
53 * pull it in now. Otherwise, we just use whatever GSSAPI supplies.
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
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.
68 #if defined(GSSAPI)
70 static unsigned char krb5_mech_oid_bytes[] = {
71 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02
74 #ifndef USE_ISC_SPNEGO
75 static unsigned char spnego_mech_oid_bytes[] = {
76 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02
78 #endif
80 static 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
87 static gss_OID_set_desc mech_oid_set = {
88 sizeof(mech_oid_set_array) / sizeof(*mech_oid_set_array),
89 mech_oid_set_array
92 #endif
94 #define REGION_TO_GBUFFER(r, gb) \
95 do { \
96 (gb).length = (r).length; \
97 (gb).value = (r).base; \
98 } while (0)
100 #define GBUFFER_TO_REGION(gb, r) \
101 do { \
102 (r).length = (gb).length; \
103 (r).base = (gb).value; \
104 } while (0)
107 #define RETERR(x) do { \
108 result = (x); \
109 if (result != ISC_R_SUCCESS) \
110 goto out; \
111 } while (0)
113 #ifdef GSSAPI
114 static inline void
115 name_to_gbuffer(dns_name_t *name, isc_buffer_t *buffer,
116 gss_buffer_desc *gbuffer)
118 dns_name_t tname, *namep;
119 isc_region_t r;
120 isc_result_t result;
122 if (!dns_name_isabsolute(name))
123 namep = name;
124 else
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;
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);
139 static void
140 log_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];
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;
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 = "???";
173 gss_log(3, "gss cred: \"%s\", %s, %lu", (char *)gbuffer.value,
174 usage_text, (unsigned long)lifetime);
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)));
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)));
192 #endif
194 isc_result_t
195 dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate,
196 gss_cred_id_t *cred)
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];
209 REQUIRE(cred != NULL && *cred == NULL);
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.
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);
231 } else
232 gname = NULL;
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 ?");
243 if (initiate)
244 usage = GSS_C_INITIATE;
245 else
246 usage = GSS_C_ACCEPT;
248 gret = gss_acquire_cred(&minor, gname, GSS_C_INDEFINITE,
249 &mech_oid_set,
250 usage, cred, &mechs, &lifetime);
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);
260 gss_log(4, "acquired %s credentials for %s",
261 initiate ? "initiate" : "accept",
262 (char *)gnamebuf.value);
264 log_cred(*cred);
266 return (ISC_R_SUCCESS);
267 #else
268 UNUSED(name);
269 UNUSED(initiate);
270 UNUSED(cred);
272 return (ISC_R_NOTIMPLEMENTED);
273 #endif
276 isc_boolean_t
277 dst_gssapi_identitymatchesrealmkrb5(dns_name_t *signer, dns_name_t *name,
278 dns_name_t *realm)
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;
288 * It is far, far easier to write the names we are looking at into
289 * a string, and do string operations on them.
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));
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.
301 rname = strstr(sbuf, "\\@");
302 if (rname == NULL)
303 return (isc_boolean_false);
304 *rname = '\0';
305 rname += 2;
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"
312 * This will work for
313 * host/example.com@EXAMPLE.COM
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);
324 * Now, we do a simple comparison between the name and the realm.
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);
335 return (isc_boolean_false);
336 #else
337 UNUSED(signer);
338 UNUSED(name);
339 UNUSED(realm);
340 return (isc_boolean_false);
341 #endif
344 isc_boolean_t
345 dst_gssapi_identitymatchesrealmms(dns_name_t *signer, dns_name_t *name,
346 dns_name_t *realm)
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;
357 * It is far, far easier to write the names we are looking at into
358 * a string, and do string operations on them.
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));
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.
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);
378 * Verify that the $ and @ follow one another.
380 if (rname - sname != 2)
381 return (isc_boolean_false);
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.
388 * All service principals in Microsoft format seem to be in
389 * machinename$@EXAMPLE.COM
390 * format.
392 *rname = '\0';
393 rname += 2;
394 *sname = '\0';
395 sname = sbuf;
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.
401 if (name != NULL) {
402 nname = strchr(nbuf, '.');
403 if (nname == NULL)
404 return (isc_boolean_false);
405 *nname++ = '\0';
409 * Now, we do a simple comparison between the name and the realm.
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);
422 return (isc_boolean_false);
423 #else
424 UNUSED(signer);
425 UNUSED(name);
426 UNUSED(realm);
427 return (isc_boolean_false);
428 #endif
431 isc_result_t
432 dst_gssapi_releasecred(gss_cred_id_t *cred) {
433 #ifdef GSSAPI
434 OM_uint32 gret, minor;
435 char buf[1024];
437 REQUIRE(cred != NULL && *cred != NULL);
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)));
445 *cred = NULL;
447 return(ISC_R_SUCCESS);
448 #else
449 UNUSED(cred);
451 return (ISC_R_NOTIMPLEMENTED);
452 #endif
455 isc_result_t
456 dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
457 isc_buffer_t *outtoken, gss_ctx_id_t *gssctx)
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];
470 /* Client must pass us a valid gss_ctx_id_t here */
471 REQUIRE(gssctx != NULL);
473 isc_buffer_init(&namebuf, array, sizeof(array));
474 name_to_gbuffer(name, &namebuf, &gnamebuf);
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;
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;
491 flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG |
492 GSS_C_SEQUENCE_FLAG | GSS_C_INTEG_FLAG;
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);
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;
508 * XXXSRA Not handled yet: RFC 3645 3.1.1: check ret_flags
509 * MUTUAL and INTEG flags, fail if either not set.
513 * RFC 2744 states the a valid output token has a non-zero length.
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);
520 (void)gss_release_name(&minor, &gname);
522 if (gret == GSS_S_COMPLETE)
523 result = ISC_R_SUCCESS;
524 else
525 result = DNS_R_CONTINUE;
527 out:
528 return (result);
529 #else
530 UNUSED(name);
531 UNUSED(intoken);
532 UNUSED(outtoken);
533 UNUSED(gssctx);
535 return (ISC_R_NOTIMPLEMENTED);
536 #endif
539 isc_result_t
540 dst_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)
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];
556 REQUIRE(outtoken != NULL && *outtoken == NULL);
558 log_cred(cred);
560 REGION_TO_GBUFFER(*intoken, gintoken);
562 if (*ctxout == NULL)
563 context = GSS_C_NO_CONTEXT;
564 else
565 context = *ctxout;
567 gret = gss_accept_sec_context(&minor, &context, cred, &gintoken,
568 GSS_C_NO_CHANNEL_BINDINGS, &gname,
569 NULL, &gouttoken, NULL, NULL, NULL);
571 result = ISC_R_FAILURE;
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);
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);
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);
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.
621 if (gnamebuf.length > 0 &&
622 ((char *)gnamebuf.value)[gnamebuf.length - 1] == '\0')
623 gnamebuf.length--;
625 gss_log(3, "gss-api source name (accept) is %.*s",
626 (int)gnamebuf.length, (char *)gnamebuf.value);
628 GBUFFER_TO_REGION(gnamebuf, r);
629 isc_buffer_init(&namebuf, r.base, r.length);
630 isc_buffer_add(&namebuf, r.length);
632 RETERR(dns_name_fromtext(principal, &namebuf, dns_rootname,
633 ISC_FALSE, NULL));
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)));
644 *ctxout = context;
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)));
655 return (result);
656 #else
657 UNUSED(cred);
658 UNUSED(intoken);
659 UNUSED(outtoken);
660 UNUSED(ctxout);
661 UNUSED(principal);
662 UNUSED(mctx);
664 return (ISC_R_NOTIMPLEMENTED);
665 #endif
668 isc_result_t
669 dst_gssapi_deletectx(isc_mem_t *mctx, gss_ctx_id_t *gssctx)
671 #ifdef GSSAPI
672 OM_uint32 gret, minor;
673 char buf[1024];
675 UNUSED(mctx);
677 REQUIRE(gssctx != NULL && *gssctx != NULL);
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)));
686 return(ISC_R_SUCCESS);
687 #else
688 UNUSED(mctx);
689 UNUSED(gssctx);
690 return (ISC_R_NOTIMPLEMENTED);
691 #endif
694 char *
695 gss_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;
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);
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);
712 snprintf(buf, buflen, "GSSAPI error: Major = %s, Minor = %s.",
713 (char *)msg_major.value, (char *)msg_minor.value);
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);
724 return (buf);
725 #endif
728 void
729 gss_log(int level, const char *fmt, ...) {
730 va_list ap;
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);
738 /*! \file */