Home | History | Annotate | Line # | Download | only in dns
      1  1.11  christos /*	$NetBSD: gssapictx.c,v 1.12 2026/01/29 18:37:49 christos Exp $	*/
      2   1.1  christos 
      3   1.1  christos /*
      4   1.1  christos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5   1.1  christos  *
      6   1.9  christos  * SPDX-License-Identifier: MPL-2.0
      7   1.9  christos  *
      8   1.1  christos  * This Source Code Form is subject to the terms of the Mozilla Public
      9   1.1  christos  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10   1.6  christos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11   1.1  christos  *
     12   1.1  christos  * See the COPYRIGHT file distributed with this work for additional
     13   1.1  christos  * information regarding copyright ownership.
     14   1.1  christos  */
     15   1.1  christos 
     16   1.1  christos #include <ctype.h>
     17   1.3  christos #include <inttypes.h>
     18   1.3  christos #include <stdbool.h>
     19   1.1  christos #include <stdlib.h>
     20   1.1  christos #include <string.h>
     21  1.10  christos #include <time.h>
     22  1.10  christos 
     23  1.10  christos #if HAVE_GSSAPI_GSSAPI_H
     24  1.10  christos #include <gssapi/gssapi.h>
     25  1.10  christos #elif HAVE_GSSAPI_H
     26  1.10  christos #include <gssapi.h>
     27  1.10  christos #endif
     28  1.10  christos 
     29  1.10  christos #if HAVE_GSSAPI_GSSAPI_KRB5_H
     30  1.10  christos #include <gssapi/gssapi_krb5.h>
     31  1.10  christos #elif HAVE_GSSAPI_KRB5_H
     32  1.10  christos #include <gssapi_krb5.h>
     33  1.10  christos #endif
     34  1.10  christos 
     35  1.10  christos #if HAVE_KRB5_KRB5_H
     36  1.10  christos #include <krb5/krb5.h>
     37  1.10  christos #elif HAVE_KRB5_H
     38  1.10  christos #include <krb5.h>
     39  1.10  christos #endif
     40   1.1  christos 
     41   1.1  christos #include <isc/buffer.h>
     42   1.1  christos #include <isc/dir.h>
     43   1.1  christos #include <isc/file.h>
     44   1.1  christos #include <isc/lex.h>
     45   1.1  christos #include <isc/mem.h>
     46   1.1  christos #include <isc/once.h>
     47   1.1  christos #include <isc/random.h>
     48  1.10  christos #include <isc/result.h>
     49   1.1  christos #include <isc/string.h>
     50   1.1  christos #include <isc/time.h>
     51   1.1  christos #include <isc/util.h>
     52   1.1  christos 
     53   1.1  christos #include <dns/fixedname.h>
     54   1.5  christos #include <dns/keyvalues.h>
     55   1.5  christos #include <dns/log.h>
     56   1.1  christos #include <dns/name.h>
     57   1.1  christos #include <dns/rdata.h>
     58   1.1  christos #include <dns/rdataclass.h>
     59   1.1  christos #include <dns/types.h>
     60   1.1  christos 
     61   1.1  christos #include <dst/gssapi.h>
     62   1.1  christos 
     63   1.1  christos #include "dst_internal.h"
     64   1.1  christos 
     65  1.10  christos #if HAVE_GSSAPI
     66   1.1  christos 
     67   1.8  christos #ifndef GSS_KRB5_MECHANISM
     68   1.5  christos static unsigned char krb5_mech_oid_bytes[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7,
     69   1.5  christos 					       0x12, 0x01, 0x02, 0x02 };
     70   1.8  christos static gss_OID_desc __gss_krb5_mechanism_oid_desc = {
     71   1.8  christos 	sizeof(krb5_mech_oid_bytes), krb5_mech_oid_bytes
     72   1.8  christos };
     73   1.8  christos #define GSS_KRB5_MECHANISM (&__gss_krb5_mechanism_oid_desc)
     74   1.8  christos #endif /* ifndef GSS_KRB5_MECHANISM */
     75   1.1  christos 
     76   1.8  christos #ifndef GSS_SPNEGO_MECHANISM
     77   1.5  christos static unsigned char spnego_mech_oid_bytes[] = { 0x2b, 0x06, 0x01,
     78   1.5  christos 						 0x05, 0x05, 0x02 };
     79   1.8  christos static gss_OID_desc __gss_spnego_mechanism_oid_desc = {
     80   1.8  christos 	sizeof(spnego_mech_oid_bytes), spnego_mech_oid_bytes
     81   1.1  christos };
     82   1.8  christos #define GSS_SPNEGO_MECHANISM (&__gss_spnego_mechanism_oid_desc)
     83   1.8  christos #endif /* ifndef GSS_SPNEGO_MECHANISM */
     84   1.1  christos 
     85   1.5  christos #define REGION_TO_GBUFFER(r, gb)          \
     86   1.5  christos 	do {                              \
     87   1.1  christos 		(gb).length = (r).length; \
     88   1.5  christos 		(gb).value = (r).base;    \
     89   1.7    rillig 	} while (0)
     90   1.1  christos 
     91   1.5  christos #define GBUFFER_TO_REGION(gb, r)                        \
     92   1.5  christos 	do {                                            \
     93   1.5  christos 		(r).length = (unsigned int)(gb).length; \
     94   1.5  christos 		(r).base = (gb).value;                  \
     95   1.7    rillig 	} while (0)
     96   1.1  christos 
     97   1.9  christos static void
     98   1.1  christos name_to_gbuffer(const dns_name_t *name, isc_buffer_t *buffer,
     99   1.5  christos 		gss_buffer_desc *gbuffer) {
    100   1.1  christos 	dns_name_t tname;
    101   1.1  christos 	const dns_name_t *namep;
    102   1.1  christos 	isc_region_t r;
    103   1.1  christos 	isc_result_t result;
    104   1.1  christos 
    105   1.5  christos 	if (!dns_name_isabsolute(name)) {
    106   1.1  christos 		namep = name;
    107   1.5  christos 	} else {
    108   1.1  christos 		unsigned int labels;
    109   1.1  christos 		dns_name_init(&tname, NULL);
    110   1.1  christos 		labels = dns_name_countlabels(name);
    111   1.1  christos 		dns_name_getlabelsequence(name, 0, labels - 1, &tname);
    112   1.1  christos 		namep = &tname;
    113   1.1  christos 	}
    114   1.1  christos 
    115  1.11  christos 	result = dns_name_totext(
    116  1.11  christos 		namep, DNS_NAME_OMITFINALDOT | DNS_NAME_PRINCIPAL, buffer);
    117   1.1  christos 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
    118   1.1  christos 	isc_buffer_putuint8(buffer, 0);
    119   1.1  christos 	isc_buffer_usedregion(buffer, &r);
    120   1.1  christos 	REGION_TO_GBUFFER(r, *gbuffer);
    121   1.1  christos }
    122   1.1  christos 
    123   1.1  christos static void
    124  1.10  christos log_cred(const gss_cred_id_t cred) {
    125   1.1  christos 	OM_uint32 gret, minor, lifetime;
    126   1.1  christos 	gss_name_t gname;
    127   1.1  christos 	gss_buffer_desc gbuffer;
    128   1.1  christos 	gss_cred_usage_t usage;
    129   1.1  christos 	const char *usage_text;
    130   1.1  christos 	char buf[1024];
    131   1.1  christos 
    132  1.10  christos 	gret = gss_inquire_cred(&minor, cred, &gname, &lifetime, &usage, NULL);
    133   1.1  christos 	if (gret != GSS_S_COMPLETE) {
    134   1.1  christos 		gss_log(3, "failed gss_inquire_cred: %s",
    135   1.1  christos 			gss_error_tostring(gret, minor, buf, sizeof(buf)));
    136   1.1  christos 		return;
    137   1.1  christos 	}
    138   1.1  christos 
    139   1.1  christos 	gret = gss_display_name(&minor, gname, &gbuffer, NULL);
    140   1.5  christos 	if (gret != GSS_S_COMPLETE) {
    141   1.1  christos 		gss_log(3, "failed gss_display_name: %s",
    142   1.1  christos 			gss_error_tostring(gret, minor, buf, sizeof(buf)));
    143   1.5  christos 	} else {
    144   1.1  christos 		switch (usage) {
    145   1.1  christos 		case GSS_C_BOTH:
    146   1.1  christos 			usage_text = "GSS_C_BOTH";
    147   1.1  christos 			break;
    148   1.1  christos 		case GSS_C_INITIATE:
    149   1.1  christos 			usage_text = "GSS_C_INITIATE";
    150   1.1  christos 			break;
    151   1.1  christos 		case GSS_C_ACCEPT:
    152   1.1  christos 			usage_text = "GSS_C_ACCEPT";
    153   1.1  christos 			break;
    154   1.1  christos 		default:
    155   1.1  christos 			usage_text = "???";
    156   1.1  christos 		}
    157   1.1  christos 		gss_log(3, "gss cred: \"%s\", %s, %lu", (char *)gbuffer.value,
    158   1.1  christos 			usage_text, (unsigned long)lifetime);
    159   1.1  christos 	}
    160   1.1  christos 
    161   1.1  christos 	if (gret == GSS_S_COMPLETE) {
    162   1.1  christos 		if (gbuffer.length != 0U) {
    163   1.1  christos 			gret = gss_release_buffer(&minor, &gbuffer);
    164   1.5  christos 			if (gret != GSS_S_COMPLETE) {
    165   1.1  christos 				gss_log(3, "failed gss_release_buffer: %s",
    166   1.1  christos 					gss_error_tostring(gret, minor, buf,
    167   1.1  christos 							   sizeof(buf)));
    168   1.5  christos 			}
    169   1.1  christos 		}
    170   1.1  christos 	}
    171   1.1  christos 
    172   1.1  christos 	gret = gss_release_name(&minor, &gname);
    173   1.5  christos 	if (gret != GSS_S_COMPLETE) {
    174   1.1  christos 		gss_log(3, "failed gss_release_name: %s",
    175   1.1  christos 			gss_error_tostring(gret, minor, buf, sizeof(buf)));
    176   1.5  christos 	}
    177   1.1  christos }
    178   1.1  christos 
    179   1.1  christos /*
    180   1.1  christos  * check for the most common configuration errors.
    181   1.1  christos  *
    182   1.1  christos  * The errors checked for are:
    183   1.1  christos  *   - tkey-gssapi-credential doesn't start with DNS/
    184   1.1  christos  *   - the default realm in /etc/krb5.conf and the
    185   1.1  christos  *     tkey-gssapi-credential bind config option don't match
    186   1.1  christos  *
    187   1.1  christos  * Note that if tkey-gssapi-keytab is set then these configure checks
    188   1.1  christos  * are not performed, and runtime errors from gssapi are used instead
    189   1.1  christos  */
    190   1.1  christos static void
    191   1.1  christos check_config(const char *gss_name) {
    192   1.1  christos 	const char *p;
    193   1.1  christos 	krb5_context krb5_ctx;
    194   1.1  christos 	char *krb5_realm_name = NULL;
    195   1.1  christos 
    196   1.1  christos 	if (strncasecmp(gss_name, "DNS/", 4) != 0) {
    197   1.5  christos 		gss_log(ISC_LOG_ERROR,
    198   1.5  christos 			"tkey-gssapi-credential (%s) "
    199   1.5  christos 			"should start with 'DNS/'",
    200   1.5  christos 			gss_name);
    201   1.1  christos 		return;
    202   1.1  christos 	}
    203   1.1  christos 
    204   1.1  christos 	if (krb5_init_context(&krb5_ctx) != 0) {
    205   1.1  christos 		gss_log(ISC_LOG_ERROR, "Unable to initialise krb5 context");
    206   1.1  christos 		return;
    207   1.1  christos 	}
    208   1.1  christos 	if (krb5_get_default_realm(krb5_ctx, &krb5_realm_name) != 0) {
    209   1.1  christos 		gss_log(ISC_LOG_ERROR, "Unable to get krb5 default realm");
    210   1.1  christos 		krb5_free_context(krb5_ctx);
    211   1.1  christos 		return;
    212   1.1  christos 	}
    213   1.1  christos 	p = strchr(gss_name, '@');
    214   1.1  christos 	if (p == NULL) {
    215   1.5  christos 		gss_log(ISC_LOG_ERROR,
    216   1.5  christos 			"badly formatted "
    217   1.5  christos 			"tkey-gssapi-credentials (%s)",
    218   1.5  christos 			gss_name);
    219   1.1  christos 		krb5_free_context(krb5_ctx);
    220   1.1  christos 		return;
    221   1.1  christos 	}
    222   1.1  christos 	if (strcasecmp(p + 1, krb5_realm_name) != 0) {
    223   1.5  christos 		gss_log(ISC_LOG_ERROR,
    224   1.5  christos 			"default realm from krb5.conf (%s) "
    225   1.1  christos 			"does not match tkey-gssapi-credential (%s)",
    226   1.1  christos 			krb5_realm_name, gss_name);
    227   1.1  christos 		krb5_free_context(krb5_ctx);
    228   1.1  christos 		return;
    229   1.1  christos 	}
    230   1.1  christos 	krb5_free_context(krb5_ctx);
    231   1.1  christos }
    232   1.8  christos 
    233   1.8  christos static OM_uint32
    234   1.8  christos mech_oid_set_create(OM_uint32 *minor, gss_OID_set *mech_oid_set) {
    235   1.8  christos 	OM_uint32 gret;
    236   1.8  christos 
    237   1.8  christos 	gret = gss_create_empty_oid_set(minor, mech_oid_set);
    238   1.8  christos 	if (gret != GSS_S_COMPLETE) {
    239  1.11  christos 		return gret;
    240   1.8  christos 	}
    241   1.8  christos 
    242   1.8  christos 	gret = gss_add_oid_set_member(minor, GSS_KRB5_MECHANISM, mech_oid_set);
    243   1.8  christos 	if (gret != GSS_S_COMPLETE) {
    244   1.8  christos 		goto release;
    245   1.8  christos 	}
    246   1.8  christos 
    247   1.8  christos 	gret = gss_add_oid_set_member(minor, GSS_SPNEGO_MECHANISM,
    248   1.8  christos 				      mech_oid_set);
    249   1.8  christos 	if (gret != GSS_S_COMPLETE) {
    250   1.8  christos 		goto release;
    251   1.8  christos 	}
    252   1.8  christos 
    253   1.8  christos release:
    254   1.8  christos 	REQUIRE(gss_release_oid_set(minor, mech_oid_set) == GSS_S_COMPLETE);
    255   1.8  christos 
    256  1.11  christos 	return gret;
    257   1.8  christos }
    258   1.8  christos 
    259   1.8  christos static void
    260   1.8  christos mech_oid_set_release(gss_OID_set *mech_oid_set) {
    261   1.8  christos 	OM_uint32 minor;
    262   1.8  christos 
    263   1.8  christos 	REQUIRE(gss_release_oid_set(&minor, mech_oid_set) == GSS_S_COMPLETE);
    264   1.8  christos }
    265   1.1  christos 
    266   1.1  christos isc_result_t
    267   1.3  christos dst_gssapi_acquirecred(const dns_name_t *name, bool initiate,
    268   1.8  christos 		       dns_gss_cred_id_t *cred) {
    269   1.1  christos 	isc_result_t result;
    270   1.1  christos 	isc_buffer_t namebuf;
    271   1.1  christos 	gss_name_t gname;
    272   1.1  christos 	gss_buffer_desc gnamebuf;
    273   1.1  christos 	unsigned char array[DNS_NAME_MAXTEXT + 1];
    274   1.1  christos 	OM_uint32 gret, minor;
    275   1.1  christos 	OM_uint32 lifetime;
    276   1.1  christos 	gss_cred_usage_t usage;
    277   1.1  christos 	char buf[1024];
    278  1.10  christos 	gss_OID_set mech_oid_set;
    279   1.1  christos 
    280   1.1  christos 	REQUIRE(cred != NULL && *cred == NULL);
    281   1.1  christos 
    282   1.1  christos 	/*
    283   1.1  christos 	 * XXXSRA In theory we could use GSS_C_NT_HOSTBASED_SERVICE
    284   1.1  christos 	 * here when we're in the acceptor role, which would let us
    285   1.1  christos 	 * default the hostname and use a compiled in default service
    286   1.1  christos 	 * name of "DNS", giving one less thing to configure in
    287   1.1  christos 	 * named.conf.	Unfortunately, this creates a circular
    288   1.1  christos 	 * dependency due to DNS-based realm lookup in at least one
    289   1.1  christos 	 * GSSAPI implementation (Heimdal).  Oh well.
    290   1.1  christos 	 */
    291   1.1  christos 	if (name != NULL) {
    292   1.1  christos 		isc_buffer_init(&namebuf, array, sizeof(array));
    293   1.1  christos 		name_to_gbuffer(name, &namebuf, &gnamebuf);
    294   1.5  christos 		gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname);
    295   1.1  christos 		if (gret != GSS_S_COMPLETE) {
    296   1.1  christos 			check_config((char *)array);
    297   1.1  christos 
    298   1.1  christos 			gss_log(3, "failed gss_import_name: %s",
    299   1.1  christos 				gss_error_tostring(gret, minor, buf,
    300   1.1  christos 						   sizeof(buf)));
    301  1.11  christos 			return ISC_R_FAILURE;
    302   1.1  christos 		}
    303   1.5  christos 	} else {
    304   1.1  christos 		gname = NULL;
    305   1.5  christos 	}
    306   1.1  christos 
    307   1.1  christos 	/* Get the credentials. */
    308   1.5  christos 	if (gname != NULL) {
    309   1.1  christos 		gss_log(3, "acquiring credentials for %s",
    310   1.1  christos 			(char *)gnamebuf.value);
    311   1.5  christos 	} else {
    312   1.1  christos 		/* XXXDCL does this even make any sense? */
    313   1.1  christos 		gss_log(3, "acquiring credentials for ?");
    314   1.1  christos 	}
    315   1.1  christos 
    316   1.5  christos 	if (initiate) {
    317   1.1  christos 		usage = GSS_C_INITIATE;
    318   1.5  christos 	} else {
    319   1.1  christos 		usage = GSS_C_ACCEPT;
    320   1.5  christos 	}
    321   1.1  christos 
    322   1.8  christos 	gret = mech_oid_set_create(&minor, &mech_oid_set);
    323   1.8  christos 	if (gret != GSS_S_COMPLETE) {
    324   1.8  christos 		gss_log(3, "failed to create OID_set: %s",
    325   1.8  christos 			gss_error_tostring(gret, minor, buf, sizeof(buf)));
    326  1.11  christos 		return ISC_R_FAILURE;
    327   1.8  christos 	}
    328   1.8  christos 
    329   1.8  christos 	gret = gss_acquire_cred(&minor, gname, GSS_C_INDEFINITE, mech_oid_set,
    330   1.8  christos 				usage, (gss_cred_id_t *)cred, NULL, &lifetime);
    331   1.1  christos 
    332   1.1  christos 	if (gret != GSS_S_COMPLETE) {
    333   1.1  christos 		gss_log(3, "failed to acquire %s credentials for %s: %s",
    334   1.1  christos 			initiate ? "initiate" : "accept",
    335   1.1  christos 			(gname != NULL) ? (char *)gnamebuf.value : "?",
    336   1.1  christos 			gss_error_tostring(gret, minor, buf, sizeof(buf)));
    337   1.5  christos 		if (gname != NULL) {
    338   1.1  christos 			check_config((char *)array);
    339   1.5  christos 		}
    340   1.1  christos 		result = ISC_R_FAILURE;
    341   1.1  christos 		goto cleanup;
    342   1.1  christos 	}
    343   1.1  christos 
    344   1.1  christos 	gss_log(4, "acquired %s credentials for %s",
    345   1.1  christos 		initiate ? "initiate" : "accept",
    346   1.1  christos 		(gname != NULL) ? (char *)gnamebuf.value : "?");
    347   1.1  christos 
    348   1.1  christos 	log_cred(*cred);
    349   1.1  christos 	result = ISC_R_SUCCESS;
    350   1.1  christos 
    351   1.1  christos cleanup:
    352   1.8  christos 	mech_oid_set_release(&mech_oid_set);
    353   1.8  christos 
    354   1.1  christos 	if (gname != NULL) {
    355   1.1  christos 		gret = gss_release_name(&minor, &gname);
    356   1.5  christos 		if (gret != GSS_S_COMPLETE) {
    357   1.1  christos 			gss_log(3, "failed gss_release_name: %s",
    358   1.1  christos 				gss_error_tostring(gret, minor, buf,
    359   1.1  christos 						   sizeof(buf)));
    360   1.5  christos 		}
    361   1.1  christos 	}
    362   1.1  christos 
    363  1.11  christos 	return result;
    364   1.1  christos }
    365   1.1  christos 
    366   1.3  christos bool
    367   1.1  christos dst_gssapi_identitymatchesrealmkrb5(const dns_name_t *signer,
    368   1.1  christos 				    const dns_name_t *name,
    369   1.5  christos 				    const dns_name_t *realm, bool subdomain) {
    370   1.1  christos 	char sbuf[DNS_NAME_FORMATSIZE];
    371   1.1  christos 	char rbuf[DNS_NAME_FORMATSIZE];
    372   1.1  christos 	char *sname;
    373   1.1  christos 	char *rname;
    374   1.1  christos 	isc_buffer_t buffer;
    375   1.1  christos 	isc_result_t result;
    376   1.1  christos 
    377   1.1  christos 	/*
    378   1.1  christos 	 * It is far, far easier to write the names we are looking at into
    379   1.1  christos 	 * a string, and do string operations on them.
    380   1.1  christos 	 */
    381   1.1  christos 	isc_buffer_init(&buffer, sbuf, sizeof(sbuf));
    382  1.11  christos 	result = dns_name_totext(
    383  1.11  christos 		signer, DNS_NAME_OMITFINALDOT | DNS_NAME_PRINCIPAL, &buffer);
    384   1.1  christos 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
    385   1.1  christos 	isc_buffer_putuint8(&buffer, 0);
    386   1.1  christos 	dns_name_format(realm, rbuf, sizeof(rbuf));
    387   1.1  christos 
    388   1.1  christos 	/*
    389   1.1  christos 	 * Find the realm portion.  This is the part after the @.  If it
    390   1.1  christos 	 * does not exist, we don't have something we like, so we fail our
    391   1.1  christos 	 * compare.
    392   1.1  christos 	 */
    393   1.1  christos 	rname = strchr(sbuf, '@');
    394   1.3  christos 	if (rname == NULL) {
    395  1.11  christos 		return false;
    396   1.3  christos 	}
    397   1.1  christos 	*rname = '\0';
    398   1.1  christos 	rname++;
    399   1.1  christos 
    400   1.3  christos 	if (strcmp(rname, rbuf) != 0) {
    401  1.11  christos 		return false;
    402   1.3  christos 	}
    403   1.3  christos 
    404   1.1  christos 	/*
    405   1.1  christos 	 * Find the host portion of the signer's name.	We do this by
    406   1.1  christos 	 * searching for the first / character.  We then check to make
    407   1.1  christos 	 * certain the instance name is "host"
    408   1.1  christos 	 *
    409   1.1  christos 	 * This will work for
    410   1.1  christos 	 *    host/example.com@EXAMPLE.COM
    411   1.1  christos 	 */
    412   1.1  christos 	sname = strchr(sbuf, '/');
    413   1.3  christos 	if (sname == NULL) {
    414  1.11  christos 		return false;
    415   1.3  christos 	}
    416   1.1  christos 	*sname = '\0';
    417   1.1  christos 	sname++;
    418   1.3  christos 	if (strcmp(sbuf, "host") != 0) {
    419  1.11  christos 		return false;
    420   1.3  christos 	}
    421   1.1  christos 
    422   1.1  christos 	/*
    423   1.3  christos 	 * If name is non NULL check that it matches against the
    424   1.3  christos 	 * machine name as expected.
    425   1.1  christos 	 */
    426   1.1  christos 	if (name != NULL) {
    427   1.3  christos 		dns_fixedname_t fixed;
    428   1.3  christos 		dns_name_t *machine;
    429   1.3  christos 
    430   1.3  christos 		machine = dns_fixedname_initname(&fixed);
    431  1.11  christos 		result = dns_name_fromstring(machine, sname, dns_rootname, 0,
    432  1.11  christos 					     NULL);
    433   1.3  christos 		if (result != ISC_R_SUCCESS) {
    434  1.11  christos 			return false;
    435   1.3  christos 		}
    436   1.3  christos 		if (subdomain) {
    437  1.11  christos 			return dns_name_issubdomain(name, machine);
    438   1.3  christos 		}
    439  1.11  christos 		return dns_name_equal(name, machine);
    440   1.1  christos 	}
    441   1.1  christos 
    442  1.11  christos 	return true;
    443   1.1  christos }
    444   1.1  christos 
    445   1.3  christos bool
    446   1.1  christos dst_gssapi_identitymatchesrealmms(const dns_name_t *signer,
    447   1.1  christos 				  const dns_name_t *name,
    448   1.5  christos 				  const dns_name_t *realm, bool subdomain) {
    449   1.1  christos 	char sbuf[DNS_NAME_FORMATSIZE];
    450   1.1  christos 	char rbuf[DNS_NAME_FORMATSIZE];
    451   1.1  christos 	char *sname;
    452   1.1  christos 	char *rname;
    453   1.1  christos 	isc_buffer_t buffer;
    454   1.1  christos 	isc_result_t result;
    455   1.1  christos 
    456   1.1  christos 	/*
    457   1.1  christos 	 * It is far, far easier to write the names we are looking at into
    458   1.1  christos 	 * a string, and do string operations on them.
    459   1.1  christos 	 */
    460   1.1  christos 	isc_buffer_init(&buffer, sbuf, sizeof(sbuf));
    461  1.11  christos 	result = dns_name_totext(
    462  1.11  christos 		signer, DNS_NAME_OMITFINALDOT | DNS_NAME_PRINCIPAL, &buffer);
    463   1.1  christos 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
    464   1.1  christos 	isc_buffer_putuint8(&buffer, 0);
    465   1.1  christos 	dns_name_format(realm, rbuf, sizeof(rbuf));
    466   1.1  christos 
    467   1.1  christos 	/*
    468   1.1  christos 	 * Find the realm portion.  This is the part after the @.  If it
    469   1.1  christos 	 * does not exist, we don't have something we like, so we fail our
    470   1.1  christos 	 * compare.
    471   1.1  christos 	 */
    472   1.1  christos 	rname = strchr(sbuf, '@');
    473   1.3  christos 	if (rname == NULL) {
    474  1.11  christos 		return false;
    475   1.3  christos 	}
    476   1.1  christos 	sname = strchr(sbuf, '$');
    477   1.3  christos 	if (sname == NULL) {
    478  1.11  christos 		return false;
    479   1.3  christos 	}
    480   1.1  christos 
    481   1.1  christos 	/*
    482   1.1  christos 	 * Verify that the $ and @ follow one another.
    483   1.1  christos 	 */
    484   1.3  christos 	if (rname - sname != 1) {
    485  1.11  christos 		return false;
    486   1.3  christos 	}
    487   1.1  christos 
    488   1.1  christos 	/*
    489   1.1  christos 	 * Find the host portion of the signer's name.	Zero out the $ so
    490   1.1  christos 	 * it terminates the signer's name, and skip past the @ for
    491   1.1  christos 	 * the realm.
    492   1.1  christos 	 *
    493   1.1  christos 	 * All service principals in Microsoft format seem to be in
    494   1.1  christos 	 *    machinename$@EXAMPLE.COM
    495   1.1  christos 	 * format.
    496   1.1  christos 	 */
    497   1.1  christos 	rname++;
    498   1.1  christos 	*sname = '\0';
    499   1.1  christos 
    500   1.3  christos 	if (strcmp(rname, rbuf) != 0) {
    501  1.11  christos 		return false;
    502   1.1  christos 	}
    503   1.1  christos 
    504   1.1  christos 	/*
    505   1.3  christos 	 * Now, we check that the realm matches (case sensitive) and that
    506   1.3  christos 	 * 'name' matches against 'machinename' qualified with 'realm'.
    507   1.1  christos 	 */
    508   1.1  christos 	if (name != NULL) {
    509   1.3  christos 		dns_fixedname_t fixed;
    510   1.3  christos 		dns_name_t *machine;
    511   1.3  christos 
    512   1.3  christos 		machine = dns_fixedname_initname(&fixed);
    513  1.11  christos 		result = dns_name_fromstring(machine, sbuf, realm, 0, NULL);
    514   1.3  christos 		if (result != ISC_R_SUCCESS) {
    515  1.11  christos 			return false;
    516   1.3  christos 		}
    517   1.3  christos 		if (subdomain) {
    518  1.11  christos 			return dns_name_issubdomain(name, machine);
    519   1.3  christos 		}
    520  1.11  christos 		return dns_name_equal(name, machine);
    521   1.1  christos 	}
    522   1.1  christos 
    523  1.11  christos 	return true;
    524   1.1  christos }
    525   1.1  christos 
    526   1.1  christos isc_result_t
    527   1.8  christos dst_gssapi_releasecred(dns_gss_cred_id_t *cred) {
    528   1.1  christos 	OM_uint32 gret, minor;
    529   1.1  christos 	char buf[1024];
    530   1.1  christos 
    531   1.1  christos 	REQUIRE(cred != NULL && *cred != NULL);
    532   1.1  christos 
    533   1.8  christos 	gret = gss_release_cred(&minor, (gss_cred_id_t *)cred);
    534   1.1  christos 	if (gret != GSS_S_COMPLETE) {
    535   1.1  christos 		/* Log the error, but still free the credential's memory */
    536   1.1  christos 		gss_log(3, "failed releasing credential: %s",
    537   1.1  christos 			gss_error_tostring(gret, minor, buf, sizeof(buf)));
    538   1.1  christos 	}
    539   1.1  christos 	*cred = NULL;
    540   1.1  christos 
    541  1.11  christos 	return ISC_R_SUCCESS;
    542   1.1  christos }
    543   1.1  christos 
    544   1.1  christos /*
    545   1.1  christos  * Format a gssapi error message info into a char ** on the given memory
    546   1.1  christos  * context. This is used to return gssapi error messages back up the
    547   1.1  christos  * call chain for reporting to the user.
    548   1.1  christos  */
    549   1.1  christos static void
    550   1.3  christos gss_err_message(isc_mem_t *mctx, uint32_t major, uint32_t minor,
    551   1.5  christos 		char **err_message) {
    552   1.1  christos 	char buf[1024];
    553   1.1  christos 	char *estr;
    554   1.1  christos 
    555   1.1  christos 	if (err_message == NULL || mctx == NULL) {
    556   1.1  christos 		/* the caller doesn't want any error messages */
    557   1.1  christos 		return;
    558   1.1  christos 	}
    559   1.1  christos 
    560   1.1  christos 	estr = gss_error_tostring(major, minor, buf, sizeof(buf));
    561   1.5  christos 	if (estr != NULL) {
    562   1.1  christos 		(*err_message) = isc_mem_strdup(mctx, estr);
    563   1.5  christos 	}
    564   1.1  christos }
    565   1.1  christos 
    566   1.1  christos isc_result_t
    567   1.1  christos dst_gssapi_initctx(const dns_name_t *name, isc_buffer_t *intoken,
    568   1.8  christos 		   isc_buffer_t *outtoken, dns_gss_ctx_id_t *gssctx,
    569   1.5  christos 		   isc_mem_t *mctx, char **err_message) {
    570   1.1  christos 	isc_region_t r;
    571   1.1  christos 	isc_buffer_t namebuf;
    572   1.1  christos 	gss_name_t gname;
    573   1.1  christos 	OM_uint32 gret, minor, ret_flags, flags;
    574   1.1  christos 	gss_buffer_desc gintoken, *gintokenp, gouttoken = GSS_C_EMPTY_BUFFER;
    575   1.1  christos 	isc_result_t result;
    576   1.1  christos 	gss_buffer_desc gnamebuf;
    577   1.1  christos 	unsigned char array[DNS_NAME_MAXTEXT + 1];
    578   1.1  christos 
    579   1.1  christos 	/* Client must pass us a valid gss_ctx_id_t here */
    580   1.1  christos 	REQUIRE(gssctx != NULL);
    581   1.1  christos 	REQUIRE(mctx != NULL);
    582   1.1  christos 
    583   1.1  christos 	isc_buffer_init(&namebuf, array, sizeof(array));
    584   1.1  christos 	name_to_gbuffer(name, &namebuf, &gnamebuf);
    585   1.1  christos 
    586   1.1  christos 	/* Get the name as a GSS name */
    587   1.1  christos 	gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname);
    588   1.1  christos 	if (gret != GSS_S_COMPLETE) {
    589   1.1  christos 		gss_err_message(mctx, gret, minor, err_message);
    590  1.12  christos 		CHECK(ISC_R_FAILURE);
    591   1.1  christos 	}
    592   1.1  christos 
    593   1.1  christos 	if (intoken != NULL) {
    594   1.1  christos 		/* Don't call gss_release_buffer for gintoken! */
    595   1.1  christos 		REGION_TO_GBUFFER(*intoken, gintoken);
    596   1.1  christos 		gintokenp = &gintoken;
    597   1.1  christos 	} else {
    598   1.1  christos 		gintokenp = NULL;
    599   1.1  christos 	}
    600   1.1  christos 
    601   1.1  christos 	/*
    602   1.1  christos 	 * Note that we don't set GSS_C_SEQUENCE_FLAG as Windows DNS
    603   1.1  christos 	 * servers don't like it.
    604   1.1  christos 	 */
    605   1.1  christos 	flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG;
    606   1.1  christos 
    607   1.8  christos 	gret = gss_init_sec_context(
    608   1.8  christos 		&minor, GSS_C_NO_CREDENTIAL, (gss_ctx_id_t *)gssctx, gname,
    609   1.8  christos 		GSS_SPNEGO_MECHANISM, flags, 0, NULL, gintokenp, NULL,
    610   1.8  christos 		&gouttoken, &ret_flags, NULL);
    611   1.1  christos 
    612   1.1  christos 	if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED) {
    613   1.1  christos 		gss_err_message(mctx, gret, minor, err_message);
    614   1.5  christos 		if (err_message != NULL && *err_message != NULL) {
    615   1.1  christos 			gss_log(3, "Failure initiating security context: %s",
    616   1.1  christos 				*err_message);
    617   1.5  christos 		} else {
    618   1.1  christos 			gss_log(3, "Failure initiating security context");
    619   1.5  christos 		}
    620   1.1  christos 
    621  1.12  christos 		CHECK(ISC_R_FAILURE);
    622   1.1  christos 	}
    623   1.1  christos 
    624   1.1  christos 	/*
    625   1.1  christos 	 * XXXSRA Not handled yet: RFC 3645 3.1.1: check ret_flags
    626   1.1  christos 	 * MUTUAL and INTEG flags, fail if either not set.
    627   1.1  christos 	 */
    628   1.1  christos 
    629   1.1  christos 	/*
    630   1.1  christos 	 * RFC 2744 states the a valid output token has a non-zero length.
    631   1.1  christos 	 */
    632   1.1  christos 	if (gouttoken.length != 0U) {
    633   1.1  christos 		GBUFFER_TO_REGION(gouttoken, r);
    634  1.12  christos 		CHECK(isc_buffer_copyregion(outtoken, &r));
    635   1.1  christos 	}
    636   1.1  christos 
    637   1.5  christos 	if (gret == GSS_S_COMPLETE) {
    638   1.1  christos 		result = ISC_R_SUCCESS;
    639   1.5  christos 	} else {
    640   1.1  christos 		result = DNS_R_CONTINUE;
    641   1.5  christos 	}
    642   1.1  christos 
    643  1.12  christos cleanup:
    644   1.5  christos 	if (gouttoken.length != 0U) {
    645   1.1  christos 		(void)gss_release_buffer(&minor, &gouttoken);
    646   1.5  christos 	}
    647   1.1  christos 	(void)gss_release_name(&minor, &gname);
    648  1.11  christos 	return result;
    649   1.1  christos }
    650   1.1  christos 
    651   1.1  christos isc_result_t
    652   1.8  christos dst_gssapi_acceptctx(dns_gss_cred_id_t cred, const char *gssapi_keytab,
    653   1.1  christos 		     isc_region_t *intoken, isc_buffer_t **outtoken,
    654   1.8  christos 		     dns_gss_ctx_id_t *ctxout, dns_name_t *principal,
    655   1.5  christos 		     isc_mem_t *mctx) {
    656   1.1  christos 	isc_region_t r;
    657   1.1  christos 	isc_buffer_t namebuf;
    658   1.1  christos 	gss_buffer_desc gnamebuf = GSS_C_EMPTY_BUFFER, gintoken,
    659   1.1  christos 			gouttoken = GSS_C_EMPTY_BUFFER;
    660   1.1  christos 	OM_uint32 gret, minor;
    661   1.1  christos 	gss_ctx_id_t context = GSS_C_NO_CONTEXT;
    662   1.1  christos 	gss_name_t gname = NULL;
    663   1.1  christos 	isc_result_t result;
    664   1.1  christos 	char buf[1024];
    665   1.1  christos 
    666   1.1  christos 	REQUIRE(outtoken != NULL && *outtoken == NULL);
    667   1.1  christos 
    668   1.1  christos 	REGION_TO_GBUFFER(*intoken, gintoken);
    669   1.1  christos 
    670   1.5  christos 	if (*ctxout == NULL) {
    671   1.1  christos 		context = GSS_C_NO_CONTEXT;
    672   1.5  christos 	} else {
    673   1.1  christos 		context = *ctxout;
    674   1.5  christos 	}
    675   1.1  christos 
    676   1.1  christos 	if (gssapi_keytab != NULL) {
    677  1.10  christos #if HAVE_GSSAPI_GSSAPI_KRB5_H || HAVE_GSSAPI_KRB5_H
    678   1.1  christos 		gret = gsskrb5_register_acceptor_identity(gssapi_keytab);
    679   1.1  christos 		if (gret != GSS_S_COMPLETE) {
    680   1.5  christos 			gss_log(3,
    681   1.5  christos 				"failed "
    682   1.1  christos 				"gsskrb5_register_acceptor_identity(%s): %s",
    683   1.1  christos 				gssapi_keytab,
    684   1.1  christos 				gss_error_tostring(gret, 0, buf, sizeof(buf)));
    685  1.11  christos 			return DNS_R_INVALIDTKEY;
    686   1.1  christos 		}
    687  1.10  christos #else
    688   1.1  christos 		/*
    689   1.1  christos 		 * Minimize memory leakage by only setting KRB5_KTNAME
    690   1.1  christos 		 * if it needs to change.
    691   1.1  christos 		 */
    692   1.1  christos 		const char *old = getenv("KRB5_KTNAME");
    693   1.1  christos 		if (old == NULL || strcmp(old, gssapi_keytab) != 0) {
    694   1.1  christos 			size_t size;
    695   1.1  christos 			char *kt;
    696   1.1  christos 
    697   1.1  christos 			size = strlen(gssapi_keytab) + 13;
    698   1.1  christos 			kt = malloc(size);
    699   1.5  christos 			if (kt == NULL) {
    700  1.11  christos 				return ISC_R_NOMEMORY;
    701   1.5  christos 			}
    702   1.1  christos 			snprintf(kt, size, "KRB5_KTNAME=%s", gssapi_keytab);
    703   1.5  christos 			if (putenv(kt) != 0) {
    704  1.11  christos 				return ISC_R_NOMEMORY;
    705   1.5  christos 			}
    706   1.1  christos 		}
    707  1.10  christos #endif
    708   1.1  christos 	}
    709   1.1  christos 
    710   1.1  christos 	log_cred(cred);
    711   1.1  christos 
    712   1.1  christos 	gret = gss_accept_sec_context(&minor, &context, cred, &gintoken,
    713   1.5  christos 				      GSS_C_NO_CHANNEL_BINDINGS, &gname, NULL,
    714   1.5  christos 				      &gouttoken, NULL, NULL, NULL);
    715   1.1  christos 
    716   1.1  christos 	result = ISC_R_FAILURE;
    717   1.1  christos 
    718   1.1  christos 	switch (gret) {
    719   1.1  christos 	case GSS_S_COMPLETE:
    720   1.1  christos 	case GSS_S_CONTINUE_NEEDED:
    721   1.1  christos 		break;
    722   1.1  christos 	case GSS_S_DEFECTIVE_TOKEN:
    723   1.1  christos 	case GSS_S_DEFECTIVE_CREDENTIAL:
    724   1.1  christos 	case GSS_S_BAD_SIG:
    725   1.1  christos 	case GSS_S_DUPLICATE_TOKEN:
    726   1.1  christos 	case GSS_S_OLD_TOKEN:
    727   1.1  christos 	case GSS_S_NO_CRED:
    728   1.1  christos 	case GSS_S_CREDENTIALS_EXPIRED:
    729   1.1  christos 	case GSS_S_BAD_BINDINGS:
    730   1.1  christos 	case GSS_S_NO_CONTEXT:
    731   1.1  christos 	case GSS_S_BAD_MECH:
    732   1.1  christos 	case GSS_S_FAILURE:
    733   1.1  christos 		result = DNS_R_INVALIDTKEY;
    734   1.5  christos 	/* fall through */
    735   1.1  christos 	default:
    736   1.1  christos 		gss_log(3, "failed gss_accept_sec_context: %s",
    737   1.1  christos 			gss_error_tostring(gret, minor, buf, sizeof(buf)));
    738   1.8  christos 		if (gouttoken.length > 0U) {
    739   1.8  christos 			(void)gss_release_buffer(&minor, &gouttoken);
    740   1.8  christos 		}
    741  1.11  christos 		return result;
    742   1.1  christos 	}
    743   1.1  christos 
    744   1.1  christos 	if (gouttoken.length > 0U) {
    745   1.5  christos 		isc_buffer_allocate(mctx, outtoken,
    746   1.5  christos 				    (unsigned int)gouttoken.length);
    747   1.1  christos 		GBUFFER_TO_REGION(gouttoken, r);
    748  1.12  christos 		CHECK(isc_buffer_copyregion(*outtoken, &r));
    749   1.1  christos 		(void)gss_release_buffer(&minor, &gouttoken);
    750   1.1  christos 	}
    751   1.1  christos 
    752   1.1  christos 	if (gret == GSS_S_COMPLETE) {
    753   1.1  christos 		gret = gss_display_name(&minor, gname, &gnamebuf, NULL);
    754   1.1  christos 		if (gret != GSS_S_COMPLETE) {
    755   1.1  christos 			gss_log(3, "failed gss_display_name: %s",
    756   1.5  christos 				gss_error_tostring(gret, minor, buf,
    757   1.5  christos 						   sizeof(buf)));
    758  1.12  christos 			CHECK(ISC_R_FAILURE);
    759   1.1  christos 		}
    760   1.1  christos 
    761   1.1  christos 		/*
    762   1.1  christos 		 * Compensate for a bug in Solaris8's implementation
    763   1.1  christos 		 * of gss_display_name().  Should be harmless in any
    764   1.1  christos 		 * case, since principal names really should not
    765   1.1  christos 		 * contain null characters.
    766   1.1  christos 		 */
    767   1.1  christos 		if (gnamebuf.length > 0U &&
    768   1.1  christos 		    ((char *)gnamebuf.value)[gnamebuf.length - 1] == '\0')
    769   1.5  christos 		{
    770   1.1  christos 			gnamebuf.length--;
    771   1.5  christos 		}
    772   1.1  christos 
    773   1.1  christos 		gss_log(3, "gss-api source name (accept) is %.*s",
    774   1.1  christos 			(int)gnamebuf.length, (char *)gnamebuf.value);
    775   1.1  christos 
    776   1.1  christos 		GBUFFER_TO_REGION(gnamebuf, r);
    777   1.1  christos 		isc_buffer_init(&namebuf, r.base, r.length);
    778   1.1  christos 		isc_buffer_add(&namebuf, r.length);
    779   1.1  christos 
    780  1.12  christos 		CHECK(dns_name_fromtext(principal, &namebuf, dns_rootname, 0,
    781  1.12  christos 					NULL));
    782   1.1  christos 
    783   1.1  christos 		if (gnamebuf.length != 0U) {
    784   1.1  christos 			gret = gss_release_buffer(&minor, &gnamebuf);
    785   1.5  christos 			if (gret != GSS_S_COMPLETE) {
    786   1.1  christos 				gss_log(3, "failed gss_release_buffer: %s",
    787   1.1  christos 					gss_error_tostring(gret, minor, buf,
    788   1.1  christos 							   sizeof(buf)));
    789   1.5  christos 			}
    790   1.1  christos 		}
    791   1.5  christos 	} else {
    792   1.1  christos 		result = DNS_R_CONTINUE;
    793   1.5  christos 	}
    794   1.1  christos 
    795   1.1  christos 	*ctxout = context;
    796   1.1  christos 
    797  1.12  christos cleanup:
    798   1.1  christos 	if (gname != NULL) {
    799   1.1  christos 		gret = gss_release_name(&minor, &gname);
    800   1.5  christos 		if (gret != GSS_S_COMPLETE) {
    801   1.1  christos 			gss_log(3, "failed gss_release_name: %s",
    802   1.1  christos 				gss_error_tostring(gret, minor, buf,
    803   1.1  christos 						   sizeof(buf)));
    804   1.5  christos 		}
    805   1.1  christos 	}
    806   1.1  christos 
    807  1.11  christos 	return result;
    808   1.1  christos }
    809   1.1  christos 
    810   1.1  christos isc_result_t
    811   1.8  christos dst_gssapi_deletectx(isc_mem_t *mctx, dns_gss_ctx_id_t *gssctx) {
    812   1.1  christos 	OM_uint32 gret, minor;
    813   1.1  christos 	char buf[1024];
    814   1.1  christos 
    815   1.1  christos 	UNUSED(mctx);
    816   1.1  christos 
    817   1.1  christos 	REQUIRE(gssctx != NULL && *gssctx != NULL);
    818   1.1  christos 
    819   1.1  christos 	/* Delete the context from the GSS provider */
    820   1.8  christos 	gret = gss_delete_sec_context(&minor, (gss_ctx_id_t *)gssctx,
    821   1.8  christos 				      GSS_C_NO_BUFFER);
    822   1.1  christos 	if (gret != GSS_S_COMPLETE) {
    823   1.1  christos 		/* Log the error, but still free the context's memory */
    824   1.1  christos 		gss_log(3, "Failure deleting security context %s",
    825   1.1  christos 			gss_error_tostring(gret, minor, buf, sizeof(buf)));
    826   1.1  christos 	}
    827  1.11  christos 	return ISC_R_SUCCESS;
    828   1.1  christos }
    829   1.1  christos 
    830   1.1  christos char *
    831   1.5  christos gss_error_tostring(uint32_t major, uint32_t minor, char *buf, size_t buflen) {
    832   1.1  christos 	gss_buffer_desc msg_minor = GSS_C_EMPTY_BUFFER,
    833   1.1  christos 			msg_major = GSS_C_EMPTY_BUFFER;
    834   1.1  christos 	OM_uint32 msg_ctx, minor_stat;
    835   1.1  christos 
    836   1.1  christos 	/* Handle major status */
    837   1.1  christos 	msg_ctx = 0;
    838   1.1  christos 	(void)gss_display_status(&minor_stat, major, GSS_C_GSS_CODE,
    839   1.1  christos 				 GSS_C_NULL_OID, &msg_ctx, &msg_major);
    840   1.1  christos 
    841   1.1  christos 	/* Handle minor status */
    842   1.1  christos 	msg_ctx = 0;
    843   1.1  christos 	(void)gss_display_status(&minor_stat, minor, GSS_C_MECH_CODE,
    844   1.1  christos 				 GSS_C_NULL_OID, &msg_ctx, &msg_minor);
    845   1.1  christos 
    846   1.1  christos 	snprintf(buf, buflen, "GSSAPI error: Major = %s, Minor = %s.",
    847   1.5  christos 		 (char *)msg_major.value, (char *)msg_minor.value);
    848   1.1  christos 
    849   1.5  christos 	if (msg_major.length != 0U) {
    850   1.1  christos 		(void)gss_release_buffer(&minor_stat, &msg_major);
    851   1.5  christos 	}
    852   1.5  christos 	if (msg_minor.length != 0U) {
    853   1.1  christos 		(void)gss_release_buffer(&minor_stat, &msg_minor);
    854   1.5  christos 	}
    855  1.11  christos 	return buf;
    856   1.8  christos }
    857   1.8  christos 
    858  1.10  christos #else
    859   1.8  christos 
    860   1.8  christos isc_result_t
    861   1.8  christos dst_gssapi_acquirecred(const dns_name_t *name, bool initiate,
    862   1.8  christos 		       dns_gss_cred_id_t *cred) {
    863   1.8  christos 	REQUIRE(cred != NULL && *cred == NULL);
    864   1.8  christos 
    865   1.8  christos 	UNUSED(name);
    866   1.8  christos 	UNUSED(initiate);
    867   1.8  christos 	UNUSED(cred);
    868   1.8  christos 
    869  1.11  christos 	return ISC_R_NOTIMPLEMENTED;
    870   1.8  christos }
    871   1.8  christos 
    872   1.8  christos bool
    873   1.8  christos dst_gssapi_identitymatchesrealmkrb5(const dns_name_t *signer,
    874   1.8  christos 				    const dns_name_t *name,
    875   1.8  christos 				    const dns_name_t *realm, bool subdomain) {
    876   1.8  christos 	UNUSED(signer);
    877   1.8  christos 	UNUSED(name);
    878   1.8  christos 	UNUSED(realm);
    879   1.8  christos 	UNUSED(subdomain);
    880  1.10  christos 
    881  1.11  christos 	return false;
    882   1.8  christos }
    883   1.8  christos 
    884   1.8  christos bool
    885   1.8  christos dst_gssapi_identitymatchesrealmms(const dns_name_t *signer,
    886   1.8  christos 				  const dns_name_t *name,
    887   1.8  christos 				  const dns_name_t *realm, bool subdomain) {
    888   1.8  christos 	UNUSED(signer);
    889   1.8  christos 	UNUSED(name);
    890   1.8  christos 	UNUSED(realm);
    891   1.8  christos 	UNUSED(subdomain);
    892  1.10  christos 
    893  1.11  christos 	return false;
    894   1.8  christos }
    895   1.8  christos 
    896   1.8  christos isc_result_t
    897   1.8  christos dst_gssapi_releasecred(dns_gss_cred_id_t *cred) {
    898   1.8  christos 	UNUSED(cred);
    899   1.8  christos 
    900  1.11  christos 	return ISC_R_NOTIMPLEMENTED;
    901   1.8  christos }
    902   1.8  christos 
    903   1.8  christos isc_result_t
    904   1.8  christos dst_gssapi_initctx(const dns_name_t *name, isc_buffer_t *intoken,
    905   1.8  christos 		   isc_buffer_t *outtoken, dns_gss_ctx_id_t *gssctx,
    906   1.8  christos 		   isc_mem_t *mctx, char **err_message) {
    907   1.8  christos 	UNUSED(name);
    908   1.8  christos 	UNUSED(intoken);
    909   1.8  christos 	UNUSED(outtoken);
    910   1.8  christos 	UNUSED(gssctx);
    911   1.8  christos 	UNUSED(mctx);
    912   1.8  christos 	UNUSED(err_message);
    913   1.8  christos 
    914  1.11  christos 	return ISC_R_NOTIMPLEMENTED;
    915   1.8  christos }
    916   1.8  christos 
    917   1.8  christos isc_result_t
    918   1.8  christos dst_gssapi_acceptctx(dns_gss_cred_id_t cred, const char *gssapi_keytab,
    919   1.8  christos 		     isc_region_t *intoken, isc_buffer_t **outtoken,
    920   1.8  christos 		     dns_gss_ctx_id_t *ctxout, dns_name_t *principal,
    921   1.8  christos 		     isc_mem_t *mctx) {
    922   1.8  christos 	UNUSED(cred);
    923   1.8  christos 	UNUSED(gssapi_keytab);
    924   1.8  christos 	UNUSED(intoken);
    925   1.8  christos 	UNUSED(outtoken);
    926   1.8  christos 	UNUSED(ctxout);
    927   1.8  christos 	UNUSED(principal);
    928   1.8  christos 	UNUSED(mctx);
    929   1.8  christos 
    930  1.11  christos 	return ISC_R_NOTIMPLEMENTED;
    931   1.8  christos }
    932   1.8  christos 
    933   1.8  christos isc_result_t
    934   1.8  christos dst_gssapi_deletectx(isc_mem_t *mctx, dns_gss_ctx_id_t *gssctx) {
    935   1.8  christos 	UNUSED(mctx);
    936   1.8  christos 	UNUSED(gssctx);
    937  1.11  christos 	return ISC_R_NOTIMPLEMENTED;
    938   1.8  christos }
    939   1.8  christos 
    940   1.8  christos char *
    941   1.8  christos gss_error_tostring(uint32_t major, uint32_t minor, char *buf, size_t buflen) {
    942   1.5  christos 	snprintf(buf, buflen, "GSSAPI error: Major = %u, Minor = %u.", major,
    943   1.5  christos 		 minor);
    944   1.1  christos 
    945  1.11  christos 	return buf;
    946   1.8  christos }
    947  1.10  christos 
    948  1.10  christos #endif
    949   1.1  christos 
    950   1.1  christos void
    951   1.1  christos gss_log(int level, const char *fmt, ...) {
    952   1.1  christos 	va_list ap;
    953   1.1  christos 
    954   1.1  christos 	va_start(ap, fmt);
    955   1.5  christos 	isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_TKEY,
    956   1.5  christos 		       ISC_LOG_DEBUG(level), fmt, ap);
    957   1.1  christos 	va_end(ap);
    958   1.1  christos }
    959