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