Remove building with NOCRYPTO option
[minix.git] / external / bsd / bind / dist / lib / dns / gssapictx.c
blob42349be2d434e1d625bbc65359c713e1979743bb
1 /* $NetBSD: gssapictx.c,v 1.8 2014/12/10 04:37:58 christos Exp $ */
3 /*
4 * Copyright (C) 2004-2014 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.29 2011/08/29 06:33:25 marka Exp */
22 #include <config.h>
24 #include <ctype.h>
25 #include <stdlib.h>
26 #include <string.h>
28 #include <isc/buffer.h>
29 #include <isc/dir.h>
30 #include <isc/entropy.h>
31 #include <isc/file.h>
32 #include <isc/lex.h>
33 #include <isc/mem.h>
34 #include <isc/once.h>
35 #include <isc/print.h>
36 #include <isc/platform.h>
37 #include <isc/random.h>
38 #include <isc/string.h>
39 #include <isc/time.h>
40 #include <isc/util.h>
42 #include <dns/fixedname.h>
43 #include <dns/name.h>
44 #include <dns/rdata.h>
45 #include <dns/rdataclass.h>
46 #include <dns/result.h>
47 #include <dns/types.h>
48 #include <dns/keyvalues.h>
49 #include <dns/log.h>
51 #include <dst/gssapi.h>
52 #include <dst/result.h>
54 #include "dst_internal.h"
57 * If we're using our own SPNEGO implementation (see configure.in),
58 * pull it in now. Otherwise, we just use whatever GSSAPI supplies.
60 #if defined(GSSAPI) && defined(USE_ISC_SPNEGO)
61 #include "spnego.h"
62 #define gss_accept_sec_context gss_accept_sec_context_spnego
63 #define gss_init_sec_context gss_init_sec_context_spnego
64 #endif
67 * Solaris8 apparently needs an explicit OID set, and Solaris10 needs
68 * one for anything but Kerberos. Supplying an explicit OID set
69 * doesn't appear to hurt anything in other implementations, so we
70 * always use one. If we're not using our own SPNEGO implementation,
71 * we include SPNEGO's OID.
73 #ifdef GSSAPI
74 #ifdef WIN32
75 #include <krb5/krb5.h>
76 #else
77 #include ISC_PLATFORM_KRB5HEADER
78 #endif
80 static unsigned char krb5_mech_oid_bytes[] = {
81 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02
84 #ifndef USE_ISC_SPNEGO
85 static unsigned char spnego_mech_oid_bytes[] = {
86 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02
88 #endif
90 static gss_OID_desc mech_oid_set_array[] = {
91 { sizeof(krb5_mech_oid_bytes), krb5_mech_oid_bytes },
92 #ifndef USE_ISC_SPNEGO
93 { sizeof(spnego_mech_oid_bytes), spnego_mech_oid_bytes },
94 #endif
97 static gss_OID_set_desc mech_oid_set = {
98 sizeof(mech_oid_set_array) / sizeof(*mech_oid_set_array),
99 mech_oid_set_array
102 #endif
104 #define REGION_TO_GBUFFER(r, gb) \
105 do { \
106 (gb).length = (r).length; \
107 (gb).value = (r).base; \
108 } while (/*CONSTCOND*/0)
110 #define GBUFFER_TO_REGION(gb, r) \
111 do { \
112 (r).length = (unsigned int)(gb).length; \
113 (r).base = (gb).value; \
114 } while (/*CONSTCOND*/0)
117 #define RETERR(x) do { \
118 result = (x); \
119 if (result != ISC_R_SUCCESS) \
120 goto out; \
121 } while (/*CONSTCOND*/0)
123 #ifdef GSSAPI
124 static inline void
125 name_to_gbuffer(dns_name_t *name, isc_buffer_t *buffer,
126 gss_buffer_desc *gbuffer)
128 dns_name_t tname, *namep;
129 isc_region_t r;
130 isc_result_t result;
132 if (!dns_name_isabsolute(name))
133 namep = name;
134 else
136 unsigned int labels;
137 dns_name_init(&tname, NULL);
138 labels = dns_name_countlabels(name);
139 dns_name_getlabelsequence(name, 0, labels - 1, &tname);
140 namep = &tname;
143 result = dns_name_toprincipal(namep, buffer);
144 RUNTIME_CHECK(result == ISC_R_SUCCESS);
145 isc_buffer_putuint8(buffer, 0);
146 isc_buffer_usedregion(buffer, &r);
147 REGION_TO_GBUFFER(r, *gbuffer);
150 static void
151 log_cred(const gss_cred_id_t cred) {
152 OM_uint32 gret, minor, lifetime;
153 gss_name_t gname;
154 gss_buffer_desc gbuffer;
155 gss_cred_usage_t usage;
156 const char *usage_text;
157 char buf[1024];
159 gret = gss_inquire_cred(&minor, cred, &gname, &lifetime, &usage, NULL);
160 if (gret != GSS_S_COMPLETE) {
161 gss_log(3, "failed gss_inquire_cred: %s",
162 gss_error_tostring(gret, minor, buf, sizeof(buf)));
163 return;
166 gret = gss_display_name(&minor, gname, &gbuffer, NULL);
167 if (gret != GSS_S_COMPLETE)
168 gss_log(3, "failed gss_display_name: %s",
169 gss_error_tostring(gret, minor, buf, sizeof(buf)));
170 else {
171 switch (usage) {
172 case GSS_C_BOTH:
173 usage_text = "GSS_C_BOTH";
174 break;
175 case GSS_C_INITIATE:
176 usage_text = "GSS_C_INITIATE";
177 break;
178 case GSS_C_ACCEPT:
179 usage_text = "GSS_C_ACCEPT";
180 break;
181 default:
182 usage_text = "???";
184 gss_log(3, "gss cred: \"%s\", %s, %lu", (char *)gbuffer.value,
185 usage_text, (unsigned long)lifetime);
188 if (gret == GSS_S_COMPLETE) {
189 if (gbuffer.length != 0U) {
190 gret = gss_release_buffer(&minor, &gbuffer);
191 if (gret != GSS_S_COMPLETE)
192 gss_log(3, "failed gss_release_buffer: %s",
193 gss_error_tostring(gret, minor, buf,
194 sizeof(buf)));
198 gret = gss_release_name(&minor, &gname);
199 if (gret != GSS_S_COMPLETE)
200 gss_log(3, "failed gss_release_name: %s",
201 gss_error_tostring(gret, minor, buf, sizeof(buf)));
203 #endif
205 #ifdef GSSAPI
207 * check for the most common configuration errors.
209 * The errors checked for are:
210 * - tkey-gssapi-credential doesn't start with DNS/
211 * - the default realm in /etc/krb5.conf and the
212 * tkey-gssapi-credential bind config option don't match
214 * Note that if tkey-gssapi-keytab is set then these configure checks
215 * are not performed, and runtime errors from gssapi are used instead
217 static void
218 check_config(const char *gss_name) {
219 const char *p;
220 krb5_context krb5_ctx;
221 char *krb5_realm = NULL;
223 if (strncasecmp(gss_name, "DNS/", 4) != 0) {
224 gss_log(ISC_LOG_ERROR, "tkey-gssapi-credential (%s) "
225 "should start with 'DNS/'", gss_name);
226 return;
229 if (krb5_init_context(&krb5_ctx) != 0) {
230 gss_log(ISC_LOG_ERROR, "Unable to initialise krb5 context");
231 return;
233 if (krb5_get_default_realm(krb5_ctx, &krb5_realm) != 0) {
234 gss_log(ISC_LOG_ERROR, "Unable to get krb5 default realm");
235 krb5_free_context(krb5_ctx);
236 return;
238 p = strchr(gss_name, '@');
239 if (p == NULL) {
240 gss_log(ISC_LOG_ERROR, "badly formatted "
241 "tkey-gssapi-credentials (%s)", gss_name);
242 krb5_free_context(krb5_ctx);
243 return;
245 if (strcasecmp(p + 1, krb5_realm) != 0) {
246 gss_log(ISC_LOG_ERROR, "default realm from krb5.conf (%s) "
247 "does not match tkey-gssapi-credential (%s)",
248 krb5_realm, gss_name);
249 krb5_free_context(krb5_ctx);
250 return;
252 krb5_free_context(krb5_ctx);
254 #endif
256 isc_result_t
257 dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate,
258 gss_cred_id_t *cred)
260 #ifdef GSSAPI
261 isc_result_t result;
262 isc_buffer_t namebuf;
263 gss_name_t gname;
264 gss_buffer_desc gnamebuf;
265 unsigned char array[DNS_NAME_MAXTEXT + 1];
266 OM_uint32 gret, minor;
267 OM_uint32 lifetime;
268 gss_cred_usage_t usage;
269 char buf[1024];
271 REQUIRE(cred != NULL && *cred == NULL);
274 * XXXSRA In theory we could use GSS_C_NT_HOSTBASED_SERVICE
275 * here when we're in the acceptor role, which would let us
276 * default the hostname and use a compiled in default service
277 * name of "DNS", giving one less thing to configure in
278 * named.conf. Unfortunately, this creates a circular
279 * dependency due to DNS-based realm lookup in at least one
280 * GSSAPI implementation (Heimdal). Oh well.
282 if (name != NULL) {
283 isc_buffer_init(&namebuf, array, sizeof(array));
284 name_to_gbuffer(name, &namebuf, &gnamebuf);
285 gret = gss_import_name(&minor, &gnamebuf,
286 GSS_C_NO_OID, &gname);
287 if (gret != GSS_S_COMPLETE) {
288 check_config((char *)array);
290 gss_log(3, "failed gss_import_name: %s",
291 gss_error_tostring(gret, minor, buf,
292 sizeof(buf)));
293 return (ISC_R_FAILURE);
295 } else
296 gname = NULL;
298 /* Get the credentials. */
299 if (gname != NULL)
300 gss_log(3, "acquiring credentials for %s",
301 (char *)gnamebuf.value);
302 else {
303 /* XXXDCL does this even make any sense? */
304 gss_log(3, "acquiring credentials for ?");
307 if (initiate)
308 usage = GSS_C_INITIATE;
309 else
310 usage = GSS_C_ACCEPT;
312 gret = gss_acquire_cred(&minor, gname, GSS_C_INDEFINITE,
313 &mech_oid_set, usage, cred, NULL, &lifetime);
315 if (gret != GSS_S_COMPLETE) {
316 gss_log(3, "failed to acquire %s credentials for %s: %s",
317 initiate ? "initiate" : "accept",
318 (gname != NULL) ? (char *)gnamebuf.value : "?",
319 gss_error_tostring(gret, minor, buf, sizeof(buf)));
320 if (gname != NULL)
321 check_config((char *)array);
322 result = ISC_R_FAILURE;
323 goto cleanup;
326 gss_log(4, "acquired %s credentials for %s",
327 initiate ? "initiate" : "accept",
328 (gname != NULL) ? (char *)gnamebuf.value : "?");
330 log_cred(*cred);
331 result = ISC_R_SUCCESS;
333 cleanup:
334 if (gname != NULL) {
335 gret = gss_release_name(&minor, &gname);
336 if (gret != GSS_S_COMPLETE)
337 gss_log(3, "failed gss_release_name: %s",
338 gss_error_tostring(gret, minor, buf,
339 sizeof(buf)));
342 return (result);
343 #else
344 REQUIRE(cred != NULL && *cred == NULL);
346 UNUSED(name);
347 UNUSED(initiate);
348 UNUSED(cred);
350 return (ISC_R_NOTIMPLEMENTED);
351 #endif
354 isc_boolean_t
355 dst_gssapi_identitymatchesrealmkrb5(dns_name_t *signer, dns_name_t *name,
356 dns_name_t *realm)
358 #ifdef GSSAPI
359 char sbuf[DNS_NAME_FORMATSIZE];
360 char nbuf[DNS_NAME_FORMATSIZE];
361 char rbuf[DNS_NAME_FORMATSIZE];
362 char *sname;
363 char *rname;
364 isc_buffer_t buffer;
365 isc_result_t result;
368 * It is far, far easier to write the names we are looking at into
369 * a string, and do string operations on them.
371 isc_buffer_init(&buffer, sbuf, sizeof(sbuf));
372 result = dns_name_toprincipal(signer, &buffer);
373 RUNTIME_CHECK(result == ISC_R_SUCCESS);
374 isc_buffer_putuint8(&buffer, 0);
375 if (name != NULL)
376 dns_name_format(name, nbuf, sizeof(nbuf));
377 dns_name_format(realm, rbuf, sizeof(rbuf));
380 * Find the realm portion. This is the part after the @. If it
381 * does not exist, we don't have something we like, so we fail our
382 * compare.
384 rname = strchr(sbuf, '@');
385 if (rname == NULL)
386 return (isc_boolean_false);
387 *rname = '\0';
388 rname++;
391 * Find the host portion of the signer's name. We do this by
392 * searching for the first / character. We then check to make
393 * certain the instance name is "host"
395 * This will work for
396 * host/example.com@EXAMPLE.COM
398 sname = strchr(sbuf, '/');
399 if (sname == NULL)
400 return (isc_boolean_false);
401 *sname = '\0';
402 sname++;
403 if (strcmp(sbuf, "host") != 0)
404 return (isc_boolean_false);
407 * Now, we do a simple comparison between the name and the realm.
409 if (name != NULL) {
410 if ((strcasecmp(sname, nbuf) == 0)
411 && (strcmp(rname, rbuf) == 0))
412 return (isc_boolean_true);
413 } else {
414 if (strcmp(rname, rbuf) == 0)
415 return (isc_boolean_true);
418 return (isc_boolean_false);
419 #else
420 UNUSED(signer);
421 UNUSED(name);
422 UNUSED(realm);
423 return (isc_boolean_false);
424 #endif
427 isc_boolean_t
428 dst_gssapi_identitymatchesrealmms(dns_name_t *signer, dns_name_t *name,
429 dns_name_t *realm)
431 #ifdef GSSAPI
432 char sbuf[DNS_NAME_FORMATSIZE];
433 char nbuf[DNS_NAME_FORMATSIZE];
434 char rbuf[DNS_NAME_FORMATSIZE];
435 char *sname;
436 char *nname;
437 char *rname;
438 isc_buffer_t buffer;
439 isc_result_t result;
442 * It is far, far easier to write the names we are looking at into
443 * a string, and do string operations on them.
445 isc_buffer_init(&buffer, sbuf, sizeof(sbuf));
446 result = dns_name_toprincipal(signer, &buffer);
447 RUNTIME_CHECK(result == ISC_R_SUCCESS);
448 isc_buffer_putuint8(&buffer, 0);
449 if (name != NULL)
450 dns_name_format(name, nbuf, sizeof(nbuf));
451 dns_name_format(realm, rbuf, sizeof(rbuf));
454 * Find the realm portion. This is the part after the @. If it
455 * does not exist, we don't have something we like, so we fail our
456 * compare.
458 rname = strchr(sbuf, '@');
459 if (rname == NULL)
460 return (isc_boolean_false);
461 sname = strchr(sbuf, '$');
462 if (sname == NULL)
463 return (isc_boolean_false);
466 * Verify that the $ and @ follow one another.
468 if (rname - sname != 1)
469 return (isc_boolean_false);
472 * Find the host portion of the signer's name. Zero out the $ so
473 * it terminates the signer's name, and skip past the @ for
474 * the realm.
476 * All service principals in Microsoft format seem to be in
477 * machinename$@EXAMPLE.COM
478 * format.
480 rname++;
481 *sname = '\0';
482 sname = sbuf;
485 * Find the first . in the target name, and make it the end of
486 * the string. The rest of the name has to match the realm.
488 if (name != NULL) {
489 nname = strchr(nbuf, '.');
490 if (nname == NULL)
491 return (isc_boolean_false);
492 *nname++ = '\0';
496 * Now, we do a simple comparison between the name and the realm.
498 if (name != NULL) {
499 if ((strcasecmp(sname, nbuf) == 0)
500 && (strcmp(rname, rbuf) == 0)
501 && (strcasecmp(nname, rbuf) == 0))
502 return (isc_boolean_true);
503 } else {
504 if (strcmp(rname, rbuf) == 0)
505 return (isc_boolean_true);
509 return (isc_boolean_false);
510 #else
511 UNUSED(signer);
512 UNUSED(name);
513 UNUSED(realm);
514 return (isc_boolean_false);
515 #endif
518 isc_result_t
519 dst_gssapi_releasecred(gss_cred_id_t *cred) {
520 #ifdef GSSAPI
521 OM_uint32 gret, minor;
522 char buf[1024];
524 REQUIRE(cred != NULL && *cred != NULL);
526 gret = gss_release_cred(&minor, cred);
527 if (gret != GSS_S_COMPLETE) {
528 /* Log the error, but still free the credential's memory */
529 gss_log(3, "failed releasing credential: %s",
530 gss_error_tostring(gret, minor, buf, sizeof(buf)));
532 *cred = NULL;
534 return(ISC_R_SUCCESS);
535 #else
536 UNUSED(cred);
538 return (ISC_R_NOTIMPLEMENTED);
539 #endif
542 #ifdef GSSAPI
544 * Format a gssapi error message info into a char ** on the given memory
545 * context. This is used to return gssapi error messages back up the
546 * call chain for reporting to the user.
548 static void
549 gss_err_message(isc_mem_t *mctx, isc_uint32_t major, isc_uint32_t minor,
550 char **err_message)
552 char buf[1024];
553 char *estr;
555 if (err_message == NULL || mctx == NULL) {
556 /* the caller doesn't want any error messages */
557 return;
560 estr = gss_error_tostring(major, minor, buf, sizeof(buf));
561 if (estr != NULL)
562 (*err_message) = isc_mem_strdup(mctx, estr);
564 #endif
566 isc_result_t
567 dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
568 isc_buffer_t *outtoken, gss_ctx_id_t *gssctx,
569 isc_mem_t *mctx, char **err_message)
571 #ifdef GSSAPI
572 isc_region_t r;
573 isc_buffer_t namebuf;
574 gss_name_t gname;
575 OM_uint32 gret, minor, ret_flags, flags;
576 gss_buffer_desc gintoken, *gintokenp, gouttoken = GSS_C_EMPTY_BUFFER;
577 isc_result_t result;
578 gss_buffer_desc gnamebuf;
579 unsigned char array[DNS_NAME_MAXTEXT + 1];
581 /* Client must pass us a valid gss_ctx_id_t here */
582 REQUIRE(gssctx != NULL);
583 REQUIRE(mctx != NULL);
585 isc_buffer_init(&namebuf, array, sizeof(array));
586 name_to_gbuffer(name, &namebuf, &gnamebuf);
588 /* Get the name as a GSS name */
589 gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname);
590 if (gret != GSS_S_COMPLETE) {
591 gss_err_message(mctx, gret, minor, err_message);
592 result = ISC_R_FAILURE;
593 goto out;
596 if (intoken != NULL) {
597 /* Don't call gss_release_buffer for gintoken! */
598 REGION_TO_GBUFFER(*intoken, gintoken);
599 gintokenp = &gintoken;
600 } else {
601 gintokenp = NULL;
605 * Note that we don't set GSS_C_SEQUENCE_FLAG as Windows DNS
606 * servers don't like it.
608 flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG;
610 gret = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, gssctx,
611 gname, GSS_SPNEGO_MECHANISM, flags,
612 0, NULL, gintokenp,
613 NULL, &gouttoken, &ret_flags, NULL);
615 if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED) {
616 gss_err_message(mctx, gret, minor, err_message);
617 if (err_message != NULL && *err_message != NULL)
618 gss_log(3, "Failure initiating security context: %s",
619 *err_message);
620 else
621 gss_log(3, "Failure initiating security context");
623 result = ISC_R_FAILURE;
624 goto out;
628 * XXXSRA Not handled yet: RFC 3645 3.1.1: check ret_flags
629 * MUTUAL and INTEG flags, fail if either not set.
633 * RFC 2744 states the a valid output token has a non-zero length.
635 if (gouttoken.length != 0U) {
636 GBUFFER_TO_REGION(gouttoken, r);
637 RETERR(isc_buffer_copyregion(outtoken, &r));
638 (void)gss_release_buffer(&minor, &gouttoken);
641 if (gret == GSS_S_COMPLETE)
642 result = ISC_R_SUCCESS;
643 else
644 result = DNS_R_CONTINUE;
646 out:
647 (void)gss_release_name(&minor, &gname);
648 return (result);
649 #else
650 UNUSED(name);
651 UNUSED(intoken);
652 UNUSED(outtoken);
653 UNUSED(gssctx);
654 UNUSED(mctx);
655 UNUSED(err_message);
657 return (ISC_R_NOTIMPLEMENTED);
658 #endif
661 isc_result_t
662 dst_gssapi_acceptctx(gss_cred_id_t cred,
663 const char *gssapi_keytab,
664 isc_region_t *intoken, isc_buffer_t **outtoken,
665 gss_ctx_id_t *ctxout, dns_name_t *principal,
666 isc_mem_t *mctx)
668 #ifdef GSSAPI
669 isc_region_t r;
670 isc_buffer_t namebuf;
671 gss_buffer_desc gnamebuf = GSS_C_EMPTY_BUFFER, gintoken,
672 gouttoken = GSS_C_EMPTY_BUFFER;
673 OM_uint32 gret, minor;
674 gss_ctx_id_t context = GSS_C_NO_CONTEXT;
675 gss_name_t gname = NULL;
676 isc_result_t result;
677 char buf[1024];
679 REQUIRE(outtoken != NULL && *outtoken == NULL);
681 REGION_TO_GBUFFER(*intoken, gintoken);
683 if (*ctxout == NULL)
684 context = GSS_C_NO_CONTEXT;
685 else
686 context = *ctxout;
688 if (gssapi_keytab != NULL) {
689 #if defined(ISC_PLATFORM_GSSAPI_KRB5_HEADER) || defined(WIN32)
690 gret = gsskrb5_register_acceptor_identity(gssapi_keytab);
691 if (gret != GSS_S_COMPLETE) {
692 gss_log(3, "failed "
693 "gsskrb5_register_acceptor_identity(%s): %s",
694 gssapi_keytab,
695 gss_error_tostring(gret, 0, buf, sizeof(buf)));
696 return (DNS_R_INVALIDTKEY);
698 #else
700 * Minimize memory leakage by only setting KRB5_KTNAME
701 * if it needs to change.
703 const char *old = getenv("KRB5_KTNAME");
704 if (old == NULL || strcmp(old, gssapi_keytab) != 0) {
705 char *kt = malloc(strlen(gssapi_keytab) + 13);
706 if (kt == NULL)
707 return (ISC_R_NOMEMORY);
708 sprintf(kt, "KRB5_KTNAME=%s", gssapi_keytab);
709 if (putenv(kt) != 0)
710 return (ISC_R_NOMEMORY);
712 #endif
715 log_cred(cred);
717 gret = gss_accept_sec_context(&minor, &context, cred, &gintoken,
718 GSS_C_NO_CHANNEL_BINDINGS, &gname,
719 NULL, &gouttoken, NULL, NULL, NULL);
721 result = ISC_R_FAILURE;
723 switch (gret) {
724 case GSS_S_COMPLETE:
725 result = ISC_R_SUCCESS;
726 break;
727 case GSS_S_CONTINUE_NEEDED:
728 result = DNS_R_CONTINUE;
729 break;
730 case GSS_S_DEFECTIVE_TOKEN:
731 case GSS_S_DEFECTIVE_CREDENTIAL:
732 case GSS_S_BAD_SIG:
733 case GSS_S_DUPLICATE_TOKEN:
734 case GSS_S_OLD_TOKEN:
735 case GSS_S_NO_CRED:
736 case GSS_S_CREDENTIALS_EXPIRED:
737 case GSS_S_BAD_BINDINGS:
738 case GSS_S_NO_CONTEXT:
739 case GSS_S_BAD_MECH:
740 case GSS_S_FAILURE:
741 result = DNS_R_INVALIDTKEY;
742 /* fall through */
743 default:
744 gss_log(3, "failed gss_accept_sec_context: %s",
745 gss_error_tostring(gret, minor, buf, sizeof(buf)));
746 return (result);
749 if (gouttoken.length > 0U) {
750 RETERR(isc_buffer_allocate(mctx, outtoken,
751 (unsigned int)gouttoken.length));
752 GBUFFER_TO_REGION(gouttoken, r);
753 RETERR(isc_buffer_copyregion(*outtoken, &r));
754 (void)gss_release_buffer(&minor, &gouttoken);
757 if (gret == GSS_S_COMPLETE) {
758 gret = gss_display_name(&minor, gname, &gnamebuf, NULL);
759 if (gret != GSS_S_COMPLETE) {
760 gss_log(3, "failed gss_display_name: %s",
761 gss_error_tostring(gret, minor,
762 buf, sizeof(buf)));
763 RETERR(ISC_R_FAILURE);
767 * Compensate for a bug in Solaris8's implementation
768 * of gss_display_name(). Should be harmless in any
769 * case, since principal names really should not
770 * contain null characters.
772 if (gnamebuf.length > 0U &&
773 ((char *)gnamebuf.value)[gnamebuf.length - 1] == '\0')
774 gnamebuf.length--;
776 gss_log(3, "gss-api source name (accept) is %.*s",
777 (int)gnamebuf.length, (char *)gnamebuf.value);
779 GBUFFER_TO_REGION(gnamebuf, r);
780 isc_buffer_init(&namebuf, r.base, r.length);
781 isc_buffer_add(&namebuf, r.length);
783 RETERR(dns_name_fromtext(principal, &namebuf, dns_rootname,
784 0, NULL));
786 if (gnamebuf.length != 0U) {
787 gret = gss_release_buffer(&minor, &gnamebuf);
788 if (gret != GSS_S_COMPLETE)
789 gss_log(3, "failed gss_release_buffer: %s",
790 gss_error_tostring(gret, minor, buf,
791 sizeof(buf)));
795 *ctxout = context;
797 out:
798 if (gname != NULL) {
799 gret = gss_release_name(&minor, &gname);
800 if (gret != GSS_S_COMPLETE)
801 gss_log(3, "failed gss_release_name: %s",
802 gss_error_tostring(gret, minor, buf,
803 sizeof(buf)));
806 return (result);
807 #else
808 UNUSED(cred);
809 UNUSED(gssapi_keytab);
810 UNUSED(intoken);
811 UNUSED(outtoken);
812 UNUSED(ctxout);
813 UNUSED(principal);
814 UNUSED(mctx);
816 return (ISC_R_NOTIMPLEMENTED);
817 #endif
820 isc_result_t
821 dst_gssapi_deletectx(isc_mem_t *mctx, gss_ctx_id_t *gssctx)
823 #ifdef GSSAPI
824 OM_uint32 gret, minor;
825 char buf[1024];
827 UNUSED(mctx);
829 REQUIRE(gssctx != NULL && *gssctx != NULL);
831 /* Delete the context from the GSS provider */
832 gret = gss_delete_sec_context(&minor, gssctx, GSS_C_NO_BUFFER);
833 if (gret != GSS_S_COMPLETE) {
834 /* Log the error, but still free the context's memory */
835 gss_log(3, "Failure deleting security context %s",
836 gss_error_tostring(gret, minor, buf, sizeof(buf)));
838 return(ISC_R_SUCCESS);
839 #else
840 UNUSED(mctx);
841 UNUSED(gssctx);
842 return (ISC_R_NOTIMPLEMENTED);
843 #endif
846 char *
847 gss_error_tostring(isc_uint32_t major, isc_uint32_t minor,
848 char *buf, size_t buflen) {
849 #ifdef GSSAPI
850 gss_buffer_desc msg_minor = GSS_C_EMPTY_BUFFER,
851 msg_major = GSS_C_EMPTY_BUFFER;
852 OM_uint32 msg_ctx, minor_stat;
854 /* Handle major status */
855 msg_ctx = 0;
856 (void)gss_display_status(&minor_stat, major, GSS_C_GSS_CODE,
857 GSS_C_NULL_OID, &msg_ctx, &msg_major);
859 /* Handle minor status */
860 msg_ctx = 0;
861 (void)gss_display_status(&minor_stat, minor, GSS_C_MECH_CODE,
862 GSS_C_NULL_OID, &msg_ctx, &msg_minor);
864 snprintf(buf, buflen, "GSSAPI error: Major = %s, Minor = %s.",
865 (char *)msg_major.value, (char *)msg_minor.value);
867 if (msg_major.length != 0U)
868 (void)gss_release_buffer(&minor_stat, &msg_major);
869 if (msg_minor.length != 0U)
870 (void)gss_release_buffer(&minor_stat, &msg_minor);
871 return(buf);
872 #else
873 snprintf(buf, buflen, "GSSAPI error: Major = %u, Minor = %u.",
874 major, minor);
876 return (buf);
877 #endif
880 void
881 gss_log(int level, const char *fmt, ...) {
882 va_list ap;
884 va_start(ap, fmt);
885 isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL,
886 DNS_LOGMODULE_TKEY, ISC_LOG_DEBUG(level), fmt, ap);
887 va_end(ap);
890 /*! \file */