Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc4f4d32ce4b1a90e7e68ed631842990 > files > 5

bind-9.3.6-25.P1.el5_11.9.src.rpm

diff -up bind-9.3.6-P1/lib/dns/dst_api.c.gssapi bind-9.3.6-P1/lib/dns/dst_api.c
--- bind-9.3.6-P1/lib/dns/dst_api.c.gssapi	2006-01-05 00:50:20.000000000 +0100
+++ bind-9.3.6-P1/lib/dns/dst_api.c	2009-02-23 13:00:10.000000000 +0100
@@ -69,10 +69,6 @@ static dst_key_t *	get_key_struct(dns_na
 				       unsigned int bits,
 				       dns_rdataclass_t rdclass,
 				       isc_mem_t *mctx);
-static isc_result_t	read_public_key(const char *filename,
-					int type,
-					isc_mem_t *mctx,
-					dst_key_t **keyp);
 static isc_result_t	write_public_key(const dst_key_t *key, int type,
 					 const char *directory);
 static isc_result_t	buildfilename(dns_name_t *name,
@@ -151,9 +147,7 @@ dst_lib_init(isc_mem_t *mctx, isc_entrop
 #endif
 	RETERR(dst__openssldh_init(&dst_t_func[DST_ALG_DH]));
 #endif /* OPENSSL */
-#ifdef GSSAPI
 	RETERR(dst__gssapi_init(&dst_t_func[DST_ALG_GSSAPI]));
-#endif
 	dst_initialized = ISC_TRUE;
 	return (ISC_R_SUCCESS);
 
@@ -392,7 +386,16 @@ dst_key_fromnamedfile(const char *filena
 	REQUIRE(mctx != NULL);
 	REQUIRE(keyp != NULL && *keyp == NULL);
 
-	result = read_public_key(filename, type, mctx, &pubkey);
+	newfilenamelen = strlen(filename) + 5;
+	newfilename = isc_mem_get(mctx, newfilenamelen);
+	if (newfilename == NULL)
+		return (ISC_R_NOMEMORY);
+	result = addsuffix(newfilename, newfilenamelen, filename, ".key");
+	INSIST(result == ISC_R_SUCCESS);
+
+	result = dst_key_read_public(newfilename, type, mctx, &pubkey);
+	isc_mem_put(mctx, newfilename, newfilenamelen);
+	newfilename = NULL;
 	if (result != ISC_R_SUCCESS)
 		return (result);
 
@@ -590,6 +593,14 @@ dst_key_privatefrombuffer(dst_key_t *key
 	return (result);
 }
 
+gss_ctx_id_t
+dst_key_getgssctx(const dst_key_t *key)
+{
+	REQUIRE(key != NULL);
+
+	return (key->opaque);
+}
+
 isc_result_t
 dst_key_fromgssapi(dns_name_t *name, void *opaque, isc_mem_t *mctx,
 		   dst_key_t **keyp)
@@ -825,9 +836,9 @@ get_key_struct(dns_name_t *name, unsigne
 /*
  * Reads a public key from disk
  */
-static isc_result_t
-read_public_key(const char *filename, int type,
-		isc_mem_t *mctx, dst_key_t **keyp)
+isc_result_t
+dst_key_read_public(const char *filename, int type,
+		    isc_mem_t *mctx, dst_key_t **keyp)
 {
 	u_char rdatabuf[DST_KEY_MAXSIZE];
 	isc_buffer_t b;
@@ -837,21 +848,12 @@ read_public_key(const char *filename, in
 	isc_result_t ret;
 	dns_rdata_t rdata = DNS_RDATA_INIT;
 	unsigned int opt = ISC_LEXOPT_DNSMULTILINE;
-	char *newfilename;
-	unsigned int newfilenamelen;
 	dns_rdataclass_t rdclass = dns_rdataclass_in;
 	isc_lexspecials_t specials;
 	isc_uint32_t ttl;
 	isc_result_t result;
 	dns_rdatatype_t keytype;
 
-	newfilenamelen = strlen(filename) + 5;
-	newfilename = isc_mem_get(mctx, newfilenamelen);
-	if (newfilename == NULL)
-		return (ISC_R_NOMEMORY);
-	ret = addsuffix(newfilename, newfilenamelen, filename, ".key");
-	INSIST(ret == ISC_R_SUCCESS);
-
 	/*
 	 * Open the file and read its formatted contents
 	 * File format:
@@ -870,7 +872,7 @@ read_public_key(const char *filename, in
 	isc_lex_setspecials(lex, specials);
 	isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE);
 
-	ret = isc_lex_openfile(lex, newfilename);
+	ret = isc_lex_openfile(lex, filename);
 	if (ret != ISC_R_SUCCESS)
 		goto cleanup;
 
@@ -942,8 +944,6 @@ read_public_key(const char *filename, in
  cleanup:
 	if (lex != NULL)
 		isc_lex_destroy(&lex);
-	isc_mem_put(mctx, newfilename, newfilenamelen);
-
 	return (ret);
 }
 
diff -up bind-9.3.6-P1/lib/dns/gssapictx.c.gssapi bind-9.3.6-P1/lib/dns/gssapictx.c
--- bind-9.3.6-P1/lib/dns/gssapictx.c.gssapi	2004-12-09 05:07:17.000000000 +0100
+++ bind-9.3.6-P1/lib/dns/gssapictx.c	2009-02-23 13:00:10.000000000 +0100
@@ -39,15 +39,30 @@
 #include <dns/result.h>
 #include <dns/types.h>
 #include <dns/keyvalues.h>
+#include <dns/log.h>
 
 #include <dst/gssapi.h>
 #include <dst/result.h>
 
 #include "dst_internal.h"
 
-#ifdef GSSAPI
-
-#include <gssapi/gssapi.h>
+static unsigned char krb5_mech_oid_bytes[] = {
+	0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02
+};
+
+static unsigned char spnego_mech_oid_bytes[] = {
+	0x2b, 0x06, 0x01, 0x05, 0x05, 0x02
+};
+
+static gss_OID_desc mech_oid_set_array[] = {
+	{ sizeof(krb5_mech_oid_bytes), krb5_mech_oid_bytes },
+	{ sizeof(spnego_mech_oid_bytes), spnego_mech_oid_bytes },
+};
+
+static gss_OID_set_desc mech_oid_set = {
+	sizeof(mech_oid_set_array) / sizeof(*mech_oid_set_array),
+	mech_oid_set_array
+};
 
 #define RETERR(x) do { \
 	result = (x); \
@@ -91,8 +106,60 @@ name_to_gbuffer(dns_name_t *name, isc_bu
 	REGION_TO_GBUFFER(r, *gbuffer);
 }
 
+static void
+log_cred(const gss_cred_id_t cred) {
+	OM_uint32 gret, minor, lifetime;
+	gss_name_t gname;
+	gss_buffer_desc gbuffer;
+	gss_cred_usage_t usage;
+	const char *usage_text;
+	char buf[1024];
+
+	gret = gss_inquire_cred(&minor, cred, &gname, &lifetime, &usage, NULL);
+	if (gret != GSS_S_COMPLETE) {
+		gss_log(3, "failed gss_inquire_cred: %s",
+			gss_error_tostring(gret, minor, buf, sizeof(buf)));
+		return;
+	}
+
+	gret = gss_display_name(&minor, gname, &gbuffer, NULL);
+	if (gret != GSS_S_COMPLETE)
+		gss_log(3, "failed gss_display_name: %s",
+			gss_error_tostring(gret, minor, buf, sizeof(buf)));
+	else {
+		switch (usage) {
+		case GSS_C_BOTH:
+			usage_text = "GSS_C_BOTH";
+			break;
+		case GSS_C_INITIATE:
+			usage_text = "GSS_C_INITIATE";
+			break;
+		case GSS_C_ACCEPT:
+			usage_text = "GSS_C_ACCEPT";
+			break;
+		default:
+			usage_text = "???";
+		}
+		gss_log(3, "gss cred: \"%s\", %s, %lu", (char *)gbuffer.value,
+			usage_text, (unsigned long)lifetime);
+	}
+
+	if (gret == GSS_S_COMPLETE) {
+		gret = gss_release_buffer(&minor, &gbuffer);
+		if (gret != GSS_S_COMPLETE)
+			gss_log(3, "failed gss_release_buffer: %s",
+				gss_error_tostring(gret, minor, buf,
+						   sizeof(buf)));
+	}
+
+	gret = gss_release_name(&minor, &gname);
+	if (gret != GSS_S_COMPLETE)
+		gss_log(3, "failed gss_release_name: %s",
+			gss_error_tostring(gret, minor, buf, sizeof(buf)));
+}
+
 isc_result_t
-dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate, void **cred) {
+dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate, gss_cred_id_t *cred) {
 	isc_buffer_t namebuf;
 	gss_name_t gname;
 	gss_buffer_desc gnamebuf;
@@ -101,6 +168,7 @@ dst_gssapi_acquirecred(dns_name_t *name,
 	gss_OID_set mechs;
 	OM_uint32 lifetime;
 	gss_cred_usage_t usage;
+	char buf[1024];
 
 	REQUIRE(cred != NULL && *cred == NULL);
 
@@ -109,68 +177,267 @@ dst_gssapi_acquirecred(dns_name_t *name,
 		name_to_gbuffer(name, &namebuf, &gnamebuf);
 		gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID,
 				       &gname);
-		if (gret != GSS_S_COMPLETE)
+		if (gret != GSS_S_COMPLETE) {
+			gss_log(3, "failed gss_import_name: %s",
+				gss_error_tostring(gret, minor, buf,
+						   sizeof(buf)));
 			return (ISC_R_FAILURE);
+		}
 	} else
 		gname = NULL;
 
+	/* Get the credentials. */
+	if (gname != NULL)
+		gss_log(3, "acquiring credentials for %s",
+			(char *)gnamebuf.value);
+	else
+		/* XXXDCL does this even make any sense? */
+		gss_log(3, "acquiring credentials for ?");
+
 	if (initiate)
 		usage = GSS_C_INITIATE;
 	else
 		usage = GSS_C_ACCEPT;
 
 	gret = gss_acquire_cred(&minor, gname, GSS_C_INDEFINITE,
-				GSS_C_NO_OID_SET, usage,
-				cred, &mechs, &lifetime);
-	if (gret != GSS_S_COMPLETE)
+				&mech_oid_set,
+				usage, cred, &mechs, &lifetime);
+
+	if (gret != GSS_S_COMPLETE) {
+		gss_log(3, "failed to acquire %s credentials for %s: %s",
+			initiate ? "initiate" : "accept",
+			(char *)gnamebuf.value,
+			gss_error_tostring(gret, minor, buf, sizeof(buf)));
 		return (ISC_R_FAILURE);
+	}
+
+	gss_log(4, "acquired %s credentials for %s",
+		initiate ? "initiate" : "accept",
+		(char *)gnamebuf.value);
+
+	log_cred(*cred);
+
 	return (ISC_R_SUCCESS);
 }
 
+isc_boolean_t
+dst_gssapi_identitymatchesrealmkrb5(dns_name_t *signer, dns_name_t *name,
+				    dns_name_t *realm)
+{
+	char sbuf[DNS_NAME_FORMATSIZE];
+	char nbuf[DNS_NAME_FORMATSIZE];
+	char rbuf[DNS_NAME_FORMATSIZE];
+	char *sname;
+	char *rname;
+
+	/*
+	 * It is far, far easier to write the names we are looking at into
+	 * a string, and do string operations on them.
+	 */
+	dns_name_format(signer, sbuf, sizeof(sbuf));
+	if (name != NULL)
+		dns_name_format(name, nbuf, sizeof(nbuf));
+	dns_name_format(realm, rbuf, sizeof(rbuf));
+
+	/*
+	 * Find the realm portion.  This is the part after the @.  If it
+	 * does not exist, we don't have something we like, so we fail our
+	 * compare.
+	*/
+	rname = strstr(sbuf, "\\@");
+	if (rname == NULL)
+		return (isc_boolean_false);
+	*rname = '\0';
+	rname += 2;
+
+	/*
+	 * Find the host portion of the signer's name.  We do this by
+	 * searching for the first / character.  We then check to make
+	 * certain the instance name is "host"
+	 *
+	 * This will work for
+	 *    host/example.com@EXAMPLE.COM
+	 */
+	sname = strchr(sbuf, '/');
+	if (sname == NULL)
+		return (isc_boolean_false);
+	*sname = '\0';
+	sname++;
+	if (strcmp(sbuf, "host") != 0)
+		return (isc_boolean_false);
+
+	/*
+	 * Now, we do a simple comparison between the name and the realm.
+	 */
+	if (name != NULL) {
+		if ((strcasecmp(sname, nbuf) == 0)
+		    && (strcmp(rname, rbuf) == 0))
+			return (isc_boolean_true);
+	} else {
+		if (strcmp(rname, rbuf) == 0)
+			return (isc_boolean_true);
+	}
+
+	return (isc_boolean_false);
+}
+
+isc_boolean_t
+dst_gssapi_identitymatchesrealmms(dns_name_t *signer, dns_name_t *name,
+				  dns_name_t *realm)
+{
+	char sbuf[DNS_NAME_FORMATSIZE];
+	char nbuf[DNS_NAME_FORMATSIZE];
+	char rbuf[DNS_NAME_FORMATSIZE];
+	char *sname;
+	char *nname;
+	char *rname;
+
+	/*
+	 * It is far, far easier to write the names we are looking at into
+	 * a string, and do string operations on them.
+	 */
+	dns_name_format(signer, sbuf, sizeof(sbuf));
+	if (name != NULL)
+		dns_name_format(name, nbuf, sizeof(nbuf));
+	dns_name_format(realm, rbuf, sizeof(rbuf));
+
+	/*
+	 * Find the realm portion.  This is the part after the @.  If it
+	 * does not exist, we don't have something we like, so we fail our
+	 * compare.
+	 */
+	rname = strstr(sbuf, "\\@");
+	if (rname == NULL)
+		return (isc_boolean_false);
+	sname = strstr(sbuf, "\\$");
+	if (sname == NULL)
+		return (isc_boolean_false);
+
+	/*
+	 * Verify that the $ and @ follow one another.
+	 */
+	if (rname - sname != 2)
+		return (isc_boolean_false);
+
+	/*
+	 * Find the host portion of the signer's name.  Zero out the $ so
+	 * it terminates the signer's name, and skip past the @ for
+	 * the realm.
+	 *
+	 * All service principals in Microsoft format seem to be in
+	 *    machinename$@EXAMPLE.COM
+	 * format.
+	 */
+	*rname = '\0';
+	rname += 2;
+	*sname = '\0';
+	sname = sbuf;
+
+	/*
+	 * Find the first . in the target name, and make it the end of
+	 * the string.   The rest of the name has to match the realm.
+	 */
+	if (name != NULL) {
+		nname = strchr(nbuf, '.');
+		if (nname == NULL)
+			return (isc_boolean_false);
+		*nname++ = '\0';
+	}
+
+	/*
+	 * Now, we do a simple comparison between the name and the realm.
+	 */
+	if (name != NULL) {
+		if ((strcasecmp(sname, nbuf) == 0)
+		    && (strcmp(rname, rbuf) == 0)
+		    && (strcasecmp(nname, rbuf) == 0))
+			return (isc_boolean_true);
+	} else {
+		if (strcmp(rname, rbuf) == 0)
+			return (isc_boolean_true);
+	}
+
+	return (isc_boolean_false);
+}
+
+isc_result_t
+dst_gssapi_releasecred(gss_cred_id_t *cred) {
+	OM_uint32 gret, minor;
+	char buf[1024];
+
+	REQUIRE(cred != NULL && *cred != NULL);
+       
+	gret = gss_release_cred(&minor, cred);
+	if (gret != GSS_S_COMPLETE) {
+		/* Log the error, but still free the credential's memory */
+		gss_log(3, "failed releasing credential: %s",
+			gss_error_tostring(gret, minor, buf, sizeof(buf)));
+	}
+	*cred = NULL;
+
+	return(ISC_R_SUCCESS);
+}
+
 isc_result_t
-dst_gssapi_initctx(dns_name_t *name, void *cred,
-		   isc_region_t *intoken, isc_buffer_t *outtoken,
-		   void **context)
+dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
+		   isc_buffer_t *outtoken, gss_ctx_id_t *gssctx)
 {
 	isc_region_t r;
 	isc_buffer_t namebuf;
 	gss_buffer_desc gnamebuf, gintoken, *gintokenp, gouttoken;
 	OM_uint32 gret, minor, flags, ret_flags;
-	gss_OID mech_type, ret_mech_type;
-	OM_uint32 lifetime;
 	gss_name_t gname;
 	isc_result_t result;
 	unsigned char array[DNS_NAME_MAXTEXT + 1];
+	char buf[1024];
+
+	/* Client must pass us a valid gss_ctx_id_t here */
+	REQUIRE(gssctx != NULL);
 
 	isc_buffer_init(&namebuf, array, sizeof(array));
 	name_to_gbuffer(name, &namebuf, &gnamebuf);
+
+	/* Get the name as a GSS name */
 	gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname);
-	if (gret != GSS_S_COMPLETE)
-		return (ISC_R_FAILURE);
+	if (gret != GSS_S_COMPLETE) {
+		result = ISC_R_FAILURE;
+		goto out;
+	}
 
 	if (intoken != NULL) {
+		/* Don't call gss_release_buffer for gintoken! */
 		REGION_TO_GBUFFER(*intoken, gintoken);
 		gintokenp = &gintoken;
 	} else
 		gintokenp = NULL;
 
-	if (*context == NULL)
-		*context = GSS_C_NO_CONTEXT;
 	flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG |
-		GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG;
-	mech_type = GSS_C_NO_OID;
+		GSS_C_SEQUENCE_FLAG | GSS_C_INTEG_FLAG;
 
-	gret = gss_init_sec_context(&minor, cred, context, gname,
-				    mech_type, flags, 0,
-				    GSS_C_NO_CHANNEL_BINDINGS, gintokenp,
-				    &ret_mech_type, &gouttoken, &ret_flags,
-				    &lifetime);
-	if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED)
-		return (ISC_R_FAILURE);
+	gret = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, gssctx,
+				    gname, GSS_SPNEGO_MECHANISM, flags,
+				    0, NULL, gintokenp,
+				    NULL, &gouttoken, &ret_flags, NULL);
+
+	if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED) {
+		gss_log(3, "Failure initiating security context");
+		gss_log(3, "%s", gss_error_tostring(gret, minor,
+						    buf, sizeof(buf)));
+		result = ISC_R_FAILURE;
+		goto out;
+	}
+
+	/*
+	 * XXXSRA Not handled yet: RFC 3645 3.1.1: check ret_flags
+	 * MUTUAL and INTEG flags, fail if either not set.
+	 */
 
 	GBUFFER_TO_REGION(gouttoken, r);
 	RETERR(isc_buffer_copyregion(outtoken, &r));
 
+	(void)gss_release_name(&minor, &gname);
+	(void)gss_release_buffer(&minor, &gouttoken);
+
 	if (gret == GSS_S_COMPLETE)
 		return (ISC_R_SUCCESS);
 	else
@@ -181,82 +448,169 @@ dst_gssapi_initctx(dns_name_t *name, voi
 }
 
 isc_result_t
-dst_gssapi_acceptctx(dns_name_t *name, void *cred,
-		     isc_region_t *intoken, isc_buffer_t *outtoken,
-		     void **context)
+dst_gssapi_acceptctx(gss_cred_id_t cred,
+		     isc_region_t *intoken, isc_buffer_t **outtoken,
+		     gss_ctx_id_t *ctxout, dns_name_t *principal,
+		     isc_mem_t *mctx)
 {
 	isc_region_t r;
 	isc_buffer_t namebuf;
 	gss_buffer_desc gnamebuf, gintoken, gouttoken;
-	OM_uint32 gret, minor, flags;
-	gss_OID mech_type;
-	OM_uint32 lifetime;
-	gss_cred_id_t delegated_cred;
-	gss_name_t gname;
+	OM_uint32 gret, minor;
+	gss_ctx_id_t context = GSS_C_NO_CONTEXT;
+	gss_name_t gname = NULL;
 	isc_result_t result;
-	unsigned char array[DNS_NAME_MAXTEXT + 1];
+	char buf[1024];
 
-	isc_buffer_init(&namebuf, array, sizeof(array));
-	name_to_gbuffer(name, &namebuf, &gnamebuf);
-	gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname);
-	if (gret != GSS_S_COMPLETE)
-		return (ISC_R_FAILURE);
+	REQUIRE(outtoken != NULL && *outtoken == NULL);
+
+	log_cred(cred);
 
 	REGION_TO_GBUFFER(*intoken, gintoken);
 
-	if (*context == NULL)
-		*context = GSS_C_NO_CONTEXT;
+	if (*ctxout == NULL)
+		context = GSS_C_NO_CONTEXT;
+	else
+		context = *ctxout;
 
-	gret = gss_accept_sec_context(&minor, context, cred, &gintoken,
-				      GSS_C_NO_CHANNEL_BINDINGS, gname,
-				      &mech_type, &gouttoken, &flags,
-				      &lifetime, &delegated_cred);
-	if (gret != GSS_S_COMPLETE)
-		return (ISC_R_FAILURE);
+	gret = gss_accept_sec_context(&minor, &context, cred, &gintoken,
+				      GSS_C_NO_CHANNEL_BINDINGS, &gname,
+				      NULL, &gouttoken, NULL, NULL, NULL);
+
+	result = ISC_R_FAILURE;
+
+	switch (gret) {
+	case GSS_S_COMPLETE:
+		result = ISC_R_SUCCESS;
+		break;
+	case GSS_S_CONTINUE_NEEDED:
+		result = DNS_R_CONTINUE;
+		break;
+	case GSS_S_DEFECTIVE_TOKEN:
+	case GSS_S_DEFECTIVE_CREDENTIAL:
+	case GSS_S_BAD_SIG:
+	case GSS_S_DUPLICATE_TOKEN:
+	case GSS_S_OLD_TOKEN:
+	case GSS_S_NO_CRED:
+	case GSS_S_CREDENTIALS_EXPIRED:
+	case GSS_S_BAD_BINDINGS:
+	case GSS_S_NO_CONTEXT:
+	case GSS_S_BAD_MECH:
+	case GSS_S_FAILURE:
+		result = DNS_R_INVALIDTKEY;
+		/* fall through */
+	default:
+		gss_log(3, "failed gss_accept_sec_context: %s",
+			gss_error_tostring(gret, minor, buf, sizeof(buf)));
+		return (result);
+	}
 
-	GBUFFER_TO_REGION(gouttoken, r);
-	RETERR(isc_buffer_copyregion(outtoken, &r));
+	if (gouttoken.length > 0) {
+		RETERR(isc_buffer_allocate(mctx, outtoken, gouttoken.length));
+		GBUFFER_TO_REGION(gouttoken, r);
+		RETERR(isc_buffer_copyregion(*outtoken, &r));
+	}
 
-	return (ISC_R_SUCCESS);
+	if (gret == GSS_S_COMPLETE) {
+		gret = gss_display_name(&minor, gname, &gnamebuf, NULL);
+		if (gret != GSS_S_COMPLETE) {
+			gss_log(3, "failed gss_display_name: %s",
+				gss_error_tostring(gret, minor,
+						   buf, sizeof(buf)));
+			RETERR(ISC_R_FAILURE);
+		}
+
+		/*
+		 * Compensate for a bug in Solaris8's implementation
+		 * of gss_display_name().  Should be harmless in any
+		 * case, since principal names really should not
+		 * contain null characters.
+		 */
+		if (gnamebuf.length > 0 &&
+		    ((char *)gnamebuf.value)[gnamebuf.length - 1] == '\0')
+			gnamebuf.length--;
+
+		gss_log(3, "gss-api source name (accept) is %.*s",
+			(int)gnamebuf.length, (char *)gnamebuf.value);
+
+		GBUFFER_TO_REGION(gnamebuf, r);
+		isc_buffer_init(&namebuf, r.base, r.length);
+		isc_buffer_add(&namebuf, r.length);
 
- out:
-	return (result);
-}
+		RETERR(dns_name_fromtext(principal, &namebuf, dns_rootname,
+					 ISC_FALSE, NULL));
 
-#else
+		gret = gss_release_buffer(&minor, &gnamebuf);
+		if (gret != GSS_S_COMPLETE)
+			gss_log(3, "failed gss_release_buffer: %s",
+				gss_error_tostring(gret, minor, buf,
+						   sizeof(buf)));
+	}
 
-isc_result_t
-dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate, void **cred) {
-	UNUSED(name);
-	UNUSED(initiate);
-	UNUSED(cred);
-	return (ISC_R_NOTIMPLEMENTED);
+	*ctxout = context;
+
+out:
+	if (gname != NULL) {
+		gret = gss_release_name(&minor, &gname);
+		if (gret != GSS_S_COMPLETE)
+			gss_log(3, "failed gss_release_name: %s",
+				gss_error_tostring(gret, minor, buf,
+						   sizeof(buf)));
+	}
+
+	return (result);
 }
 
 isc_result_t
-dst_gssapi_initctx(dns_name_t *name, void *cred,
-		   isc_region_t *intoken, isc_buffer_t *outtoken,
-		   void **context)
-{
-	UNUSED(name);
-	UNUSED(cred);
-	UNUSED(intoken);
-	UNUSED(outtoken);
-	UNUSED(context);
-	return (ISC_R_NOTIMPLEMENTED);
+dst_gssapi_deletectx(isc_mem_t *mctx, gss_ctx_id_t *gssctx)
+{
+        OM_uint32 gret, minor;
+        char buf[1024];
+
+        UNUSED(mctx);
+
+        REQUIRE(gssctx != NULL && *gssctx != NULL);
+
+        /* Delete the context from the GSS provider */
+        gret = gss_delete_sec_context(&minor, gssctx, GSS_C_NO_BUFFER);
+        if (gret != GSS_S_COMPLETE) {
+                /* Log the error, but still free the context's memory */
+                gss_log(3, "Failure deleting security context %s",
+                        gss_error_tostring(gret, minor, buf, sizeof(buf)));
+        }
+        return(ISC_R_SUCCESS);
 }
 
-isc_result_t
-dst_gssapi_acceptctx(dns_name_t *name, void *cred,
-		     isc_region_t *intoken, isc_buffer_t *outtoken,
-		     void **context)
-{
-	UNUSED(name);
-	UNUSED(cred);
-	UNUSED(intoken);
-	UNUSED(outtoken);
-	UNUSED(context);
-	return (ISC_R_NOTIMPLEMENTED);
+char *
+gss_error_tostring(isc_uint32_t major, isc_uint32_t minor,
+		   char *buf, size_t buflen) {
+	gss_buffer_desc msg_minor, msg_major;
+	OM_uint32 msg_ctx, minor_stat;
+
+	/* Handle major status */
+	msg_ctx = 0;
+	(void)gss_display_status(&minor_stat, major, GSS_C_GSS_CODE,
+				 GSS_C_NULL_OID, &msg_ctx, &msg_major);
+
+	/* Handle minor status */
+	msg_ctx = 0;
+	(void)gss_display_status(&minor_stat, minor, GSS_C_MECH_CODE,
+				 GSS_C_NULL_OID, &msg_ctx, &msg_minor);
+
+	snprintf(buf, buflen, "GSSAPI error: Major = %s, Minor = %s.",
+		(char *)msg_major.value, (char *)msg_minor.value);
+
+	(void)gss_release_buffer(&minor_stat, &msg_major);
+	(void)gss_release_buffer(&minor_stat, &msg_minor);
+	return(buf);
 }
 
-#endif
+void
+gss_log(int level, const char *fmt, ...) {
+	va_list ap;
+
+	va_start(ap, fmt);
+	isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+		       DNS_LOGMODULE_TKEY, ISC_LOG_DEBUG(level), fmt, ap);
+	va_end(ap);
+}
diff -up bind-9.3.6-P1/lib/dns/gssapi_link.c.gssapi bind-9.3.6-P1/lib/dns/gssapi_link.c
--- bind-9.3.6-P1/lib/dns/gssapi_link.c.gssapi	2004-12-09 05:07:17.000000000 +0100
+++ bind-9.3.6-P1/lib/dns/gssapi_link.c	2009-02-23 13:00:10.000000000 +0100
@@ -19,8 +19,6 @@
  * $Id: gssapi_link.c,v 1.1.4.1 2004/12/09 04:07:17 marka Exp $
  */
 
-#ifdef GSSAPI
-
 #include <config.h>
 
 #include <isc/buffer.h>
@@ -33,7 +31,7 @@
 #include "dst_internal.h"
 #include "dst_parse.h"
 
-#include <gssapi/gssapi.h>
+#include <dst/gssapi.h>
 
 #define INITIAL_BUFFER_SIZE 1024
 #define BUFFER_EXTRA 1024
@@ -44,49 +42,62 @@
 		(gb).value = (r).base;	\
 	} while (0)
 
-typedef struct gssapi_ctx {
+struct dst_gssapi_signverifyctx {
 	isc_buffer_t *buffer;
-	gss_ctx_id_t *context_id;
-} gssapi_ctx_t;
+};
 
+/*%
+ * Allocate a temporary "context" for use in gathering data for signing
+ * or verifying.
+ */
 
 static isc_result_t
-gssapi_createctx(dst_key_t *key, dst_context_t *dctx) {
-	gssapi_ctx_t *ctx;
+gssapi_create_signverify_ctx(dst_key_t *key, dst_context_t *dctx) {
+	dst_gssapi_signverifyctx_t *ctx;
 	isc_result_t result;
 
 	UNUSED(key);
 
-	ctx = isc_mem_get(dctx->mctx, sizeof(gssapi_ctx_t));
+	ctx = isc_mem_get(dctx->mctx, sizeof(dst_gssapi_signverifyctx_t));
 	if (ctx == NULL)
 		return (ISC_R_NOMEMORY);
 	ctx->buffer = NULL;
 	result = isc_buffer_allocate(dctx->mctx, &ctx->buffer,
 				     INITIAL_BUFFER_SIZE);
 	if (result != ISC_R_SUCCESS) {
-		isc_mem_put(dctx->mctx, ctx, sizeof(gssapi_ctx_t));
+		isc_mem_put(dctx->mctx, ctx, sizeof(dst_gssapi_signverifyctx_t));
 		return (result);
 	}
-	ctx->context_id = key->opaque;
+
 	dctx->opaque = ctx;
+
 	return (ISC_R_SUCCESS);
 }
 
+/*%
+ * Destroy the temporary sign/verify context.
+ */
 static void
-gssapi_destroyctx(dst_context_t *dctx) {
-	gssapi_ctx_t *ctx = dctx->opaque;
+gssapi_destroy_signverify_ctx(dst_context_t *dctx) {
+	dst_gssapi_signverifyctx_t *ctx = dctx->opaque;
 
 	if (ctx != NULL) {
 		if (ctx->buffer != NULL)
 			isc_buffer_free(&ctx->buffer);
-		isc_mem_put(dctx->mctx, ctx, sizeof(gssapi_ctx_t));
+		isc_mem_put(dctx->mctx, ctx, sizeof(dst_gssapi_signverifyctx_t));
 		dctx->opaque = NULL;
 	}
 }
 
+/*%
+ * Add data to our running buffer of data we will be signing or verifying.
+ * This code will see if the new data will fit in our existing buffer, and
+ * copy it in if it will.  If not, it will attempt to allocate a larger
+ * buffer and copy old+new into it, and free the old buffer.
+ */
 static isc_result_t
 gssapi_adddata(dst_context_t *dctx, const isc_region_t *data) {
-	gssapi_ctx_t *ctx = dctx->opaque;
+	dst_gssapi_signverifyctx_t *ctx = dctx->opaque;
 	isc_buffer_t *newbuffer = NULL;
 	isc_region_t r;
 	unsigned int length;
@@ -112,48 +123,120 @@ gssapi_adddata(dst_context_t *dctx, cons
 	return (ISC_R_SUCCESS);
 }
 
+/*%
+ * Sign.
+ */
 static isc_result_t
 gssapi_sign(dst_context_t *dctx, isc_buffer_t *sig) {
-	gssapi_ctx_t *ctx = dctx->opaque;
+	dst_gssapi_signverifyctx_t *ctx = dctx->opaque;
 	isc_region_t message;
 	gss_buffer_desc gmessage, gsig;
 	OM_uint32 minor, gret;
+	gss_ctx_id_t gssctx = dctx->key->opaque;
+	char buf[1024];
 
+	/*
+	 * Convert the data we wish to sign into a structure gssapi can
+	 * understand.
+	 */
 	isc_buffer_usedregion(ctx->buffer, &message);
 	REGION_TO_GBUFFER(message, gmessage);
 
-	gret = gss_get_mic(&minor, ctx->context_id,
-			   GSS_C_QOP_DEFAULT, &gmessage, &gsig);
-	if (gret != 0)
+	/*
+	 * Generate the signature.
+	 */
+	gret = gss_get_mic(&minor, gssctx, GSS_C_QOP_DEFAULT, &gmessage,
+			   &gsig);
+
+	/*
+	 * If it did not complete, we log the result and return a generic
+	 * failure code.
+	 */
+	if (gret != GSS_S_COMPLETE) {
+		gss_log(3, "GSS sign error: %s",
+			gss_error_tostring(gret, minor, buf, sizeof(buf)));
 		return (ISC_R_FAILURE);
+	}
 
+	/*
+	 * If it will not fit in our allocated buffer, return that we need
+	 * more space.
+	 */
 	if (gsig.length > isc_buffer_availablelength(sig)) {
 		gss_release_buffer(&minor, &gsig);
 		return (ISC_R_NOSPACE);
 	}
 
+	/*
+	 * Copy the output into our buffer space, and release the gssapi
+	 * allocated space.
+	 */
 	isc_buffer_putmem(sig, gsig.value, gsig.length);
-
 	gss_release_buffer(&minor, &gsig);
 
 	return (ISC_R_SUCCESS);
 }
 
+/*%
+ * Verify.
+ */
 static isc_result_t
 gssapi_verify(dst_context_t *dctx, const isc_region_t *sig) {
-	gssapi_ctx_t *ctx = dctx->opaque;
-	isc_region_t message;
+	dst_gssapi_signverifyctx_t *ctx = dctx->opaque;
+	isc_region_t message, r;
 	gss_buffer_desc gmessage, gsig;
 	OM_uint32 minor, gret;
-
+	gss_ctx_id_t gssctx = dctx->key->opaque;
+	unsigned char *buf;
+	char err[1024];
+
+	/*
+	 * Convert the data we wish to sign into a structure gssapi can
+	 * understand.
+	 */
 	isc_buffer_usedregion(ctx->buffer, &message);
 	REGION_TO_GBUFFER(message, gmessage);
 
-	REGION_TO_GBUFFER(*sig, gsig);
-
-	gret = gss_verify_mic(&minor, ctx->context_id, &gmessage, &gsig, NULL);
-	if (gret != 0)
+	/*
+	 * XXXMLG
+	 * It seem that gss_verify_mic() modifies the signature buffer,
+	 * at least on Heimdal's implementation.  Copy it here to an allocated
+	 * buffer.
+	 */
+	buf = isc_mem_allocate(dst__memory_pool, sig->length);
+	if (buf == NULL)
 		return (ISC_R_FAILURE);
+	memcpy(buf, sig->base, sig->length);
+	r.base = buf;
+	r.length = sig->length;
+	REGION_TO_GBUFFER(r, gsig);
+
+	/*
+	 * Verify the data.
+	 */
+	gret = gss_verify_mic(&minor, gssctx, &gmessage, &gsig, NULL);
+
+	isc_mem_free(dst__memory_pool, buf);
+
+	/*
+	 * Convert return codes into something useful to us.
+	 */
+	if (gret != GSS_S_COMPLETE) {
+		gss_log(3, "GSS verify error: %s",
+			gss_error_tostring(gret, minor, err, sizeof(err)));
+		if (gret == GSS_S_DEFECTIVE_TOKEN ||
+		    gret == GSS_S_BAD_SIG ||
+		    gret == GSS_S_DUPLICATE_TOKEN ||
+		    gret == GSS_S_OLD_TOKEN ||
+		    gret == GSS_S_UNSEQ_TOKEN ||
+		    gret == GSS_S_GAP_TOKEN ||
+		    gret == GSS_S_CONTEXT_EXPIRED ||
+		    gret == GSS_S_NO_CONTEXT ||
+		    gret == GSS_S_FAILURE)
+			return (DST_R_VERIFYFAILURE);
+		else
+			return (ISC_R_FAILURE);
+	}
 
 	return (ISC_R_SUCCESS);
 }
@@ -184,13 +267,14 @@ gssapi_isprivate(const dst_key_t *key) {
 
 static void
 gssapi_destroy(dst_key_t *key) {
-	UNUSED(key);
-	/* No idea */
+	REQUIRE(key != NULL);
+	dst_gssapi_deletectx(key->mctx, (gss_ctx_id_t *)&key->opaque);
+	key->opaque = NULL;
 }
 
 static dst_func_t gssapi_functions = {
-	gssapi_createctx,
-	gssapi_destroyctx,
+	gssapi_create_signverify_ctx,
+	gssapi_destroy_signverify_ctx,
 	gssapi_adddata,
 	gssapi_sign,
 	gssapi_verify,
@@ -215,6 +299,3 @@ dst__gssapi_init(dst_func_t **funcp) {
 	return (ISC_R_SUCCESS);
 }
 
-#else
-int  gssapi_link_unneeded = 1;
-#endif
diff -up bind-9.3.6-P1/lib/dns/include/dns/ssu.h.gssapi bind-9.3.6-P1/lib/dns/include/dns/ssu.h
--- bind-9.3.6-P1/lib/dns/include/dns/ssu.h.gssapi	2004-03-08 10:04:39.000000000 +0100
+++ bind-9.3.6-P1/lib/dns/include/dns/ssu.h	2009-02-23 13:00:10.000000000 +0100
@@ -26,10 +26,16 @@
 
 ISC_LANG_BEGINDECLS
 
-#define DNS_SSUMATCHTYPE_NAME 0
-#define DNS_SSUMATCHTYPE_SUBDOMAIN 1
-#define DNS_SSUMATCHTYPE_WILDCARD 2
-#define DNS_SSUMATCHTYPE_SELF 3
+#define DNS_SSUMATCHTYPE_NAME		0
+#define DNS_SSUMATCHTYPE_SUBDOMAIN	1
+#define DNS_SSUMATCHTYPE_WILDCARD	2
+#define DNS_SSUMATCHTYPE_SELF		3
+#define DNS_SSUMATCHTYPE_SELFKRB5	6
+#define DNS_SSUMATCHTYPE_SELFMS		7
+#define DNS_SSUMATCHTYPE_SUBDOMAINMS	8
+#define DNS_SSUMATCHTYPE_SUBDOMAINKRB5	9
+#define DNS_SSUMATCHTYPE_MAX		9
+
 
 isc_result_t
 dns_ssutable_create(isc_mem_t *mctx, dns_ssutable_t **table);
diff -up bind-9.3.6-P1/lib/dns/include/dns/tkey.h.gssapi bind-9.3.6-P1/lib/dns/include/dns/tkey.h
--- bind-9.3.6-P1/lib/dns/include/dns/tkey.h.gssapi	2004-03-06 09:14:00.000000000 +0100
+++ bind-9.3.6-P1/lib/dns/include/dns/tkey.h	2009-02-23 13:00:10.000000000 +0100
@@ -23,6 +23,7 @@
 #include <isc/lang.h>
 
 #include <dns/types.h>
+#include <dns/message.h>
 
 #include <dst/dst.h>
 
@@ -38,7 +39,7 @@ ISC_LANG_BEGINDECLS
 struct dns_tkeyctx {
 	dst_key_t *dhkey;
 	dns_name_t *domain;
-	void *gsscred;
+	gss_cred_id_t gsscred;
 	isc_mem_t *mctx;
 	isc_entropy_t *ectx;
 };
@@ -117,11 +118,26 @@ dns_tkey_builddhquery(dns_message_t *msg
  */
 
 isc_result_t
-dns_tkey_buildgssquery(dns_message_t *msg, dns_name_t *name,
-		       dns_name_t *gname, void *cred,
-		       isc_uint32_t lifetime, void **context);
-/*
- * XXX
+dns_tkey_buildgssquery(dns_message_t *msg, dns_name_t *name, dns_name_t *gname,
+		       isc_buffer_t *intoken, isc_uint32_t lifetime,
+		       gss_ctx_id_t *context, isc_boolean_t win2k);
+/*%<
+ *     Builds a query containing a TKEY that will generate a GSSAPI context.
+ *     The key is requested to have the specified lifetime (in seconds).
+ *
+ *     Requires:
+ *\li          'msg'     is a valid message
+ *\li          'name'    is a valid name
+ *\li          'gname'   is a valid name
+ *\li          'context' is a pointer to a valid gss_ctx_id_t
+ *                       (which may have the value GSS_C_NO_CONTEXT)
+ *\li          'win2k'   when true says to turn on some hacks to work
+ *                       with the non-standard GSS-TSIG of Windows 2000
+ *
+ *     Returns:
+ *\li          ISC_R_SUCCESS   msg was successfully updated to include the
+ *                             query to be sent
+ *\li          other           an error occurred while building the message
  */
 
 isc_result_t
@@ -165,8 +181,9 @@ dns_tkey_processdhresponse(dns_message_t
 
 isc_result_t
 dns_tkey_processgssresponse(dns_message_t *qmsg, dns_message_t *rmsg,
-			    dns_name_t *gname, void *cred, void **context,
-			    dns_tsigkey_t **outkey, dns_tsig_keyring_t *ring);
+                            dns_name_t *gname, gss_ctx_id_t *context,
+                            isc_buffer_t *outtoken, dns_tsigkey_t **outkey,
+                            dns_tsig_keyring_t *ring);
 /*
  * XXX
  */
@@ -190,6 +207,38 @@ dns_tkey_processdeleteresponse(dns_messa
  *				component of the query or response
  */
 
+isc_result_t
+dns_tkey_gssnegotiate(dns_message_t *qmsg, dns_message_t *rmsg,
+		      dns_name_t *server, gss_ctx_id_t *context,
+		      dns_tsigkey_t **outkey, dns_tsig_keyring_t *ring,
+		      isc_boolean_t win2k);
+
+/*
+ *     Client side negotiation of GSS-TSIG.  Process the respsonse
+ *     to a TKEY, and establish a TSIG key if negotiation was successful.
+ *     Build a response to the input TKEY message.  Can take multiple
+ *     calls to successfully establish the context.
+ *
+ *     Requires:
+ *             'qmsg'    is a valid message, the original TKEY request;
+ *                          it will be filled with the new message to send
+ *             'rmsg'    is a valid message, the incoming TKEY message
+ *             'server'  is the server name
+ *             'context' is the input context handle
+ *             'outkey'  receives the established key, if non-NULL;
+ *                           if non-NULL must point to NULL
+ *             'ring'    is the keyring in which to establish the key,
+ *                           or NULL
+ *             'win2k'   when true says to turn on some hacks to work
+ *                           with the non-standard GSS-TSIG of Windows 2000
+ *
+ *     Returns:
+ *             ISC_R_SUCCESS   context was successfully established
+ *             ISC_R_NOTFOUND  couldn't find a needed part of the query
+ *                                     or response
+ *             DNS_R_CONTINUE  additional context negotiation is required;
+ *                                     send the new qmsg to the server
+ */
 
 ISC_LANG_ENDDECLS
 
diff -up bind-9.3.6-P1/lib/dns/include/dns/tsig.h.gssapi bind-9.3.6-P1/lib/dns/include/dns/tsig.h
--- bind-9.3.6-P1/lib/dns/include/dns/tsig.h.gssapi	2007-08-28 09:19:14.000000000 +0200
+++ bind-9.3.6-P1/lib/dns/include/dns/tsig.h	2009-02-23 13:00:10.000000000 +0100
@@ -47,6 +47,7 @@ LIBDNS_EXTERNAL_DATA extern dns_name_t *
 
 struct dns_tsig_keyring {
 	dns_rbt_t *keys;
+	unsigned int writecount;
 	isc_rwlock_t lock;
 	isc_mem_t *mctx;
 };
@@ -67,7 +68,9 @@ struct dns_tsigkey {
 };
 
 #define dns_tsigkey_identity(tsigkey) \
-	((tsigkey)->generated ? ((tsigkey)->creator) : (&((tsigkey)->name)))
+	((tsigkey) == NULL ? NULL : \
+	  (tsigkey)->generated ? ((tsigkey)->creator) : \
+	  (&((tsigkey)->name)))
 
 ISC_LANG_BEGINDECLS
 
diff -up bind-9.3.6-P1/lib/dns/include/dns/types.h.gssapi bind-9.3.6-P1/lib/dns/include/dns/types.h
--- bind-9.3.6-P1/lib/dns/include/dns/types.h.gssapi	2006-03-02 01:37:20.000000000 +0100
+++ bind-9.3.6-P1/lib/dns/include/dns/types.h	2009-02-23 13:00:10.000000000 +0100
@@ -111,6 +111,8 @@ typedef ISC_LIST(dns_zone_t)			dns_zonel
 typedef struct dns_zonemgr			dns_zonemgr_t;
 typedef struct dns_zt				dns_zt_t;
 
+typedef struct dst_gssapi_signverifyctx dst_gssapi_signverifyctx_t;
+
 typedef enum {
 	dns_fwdpolicy_none = 0,
 	dns_fwdpolicy_first = 1,
diff -up bind-9.3.6-P1/lib/dns/include/dst/dst.h.gssapi bind-9.3.6-P1/lib/dns/include/dst/dst.h
--- bind-9.3.6-P1/lib/dns/include/dst/dst.h.gssapi	2004-12-09 05:07:19.000000000 +0100
+++ bind-9.3.6-P1/lib/dns/include/dst/dst.h	2009-02-23 13:00:10.000000000 +0100
@@ -24,6 +24,8 @@
 
 #include <dns/types.h>
 
+#include <dst/gssapi.h>
+
 ISC_LANG_BEGINDECLS
 
 /***
@@ -254,6 +256,27 @@ dst_key_fromnamedfile(const char *filena
  */
 
 isc_result_t
+dst_key_read_public(const char *filename, int type,
+		    isc_mem_t *mctx, dst_key_t **keyp);
+/*%<
+ * Reads a public key from permanent storage.  The key must be a public key.
+ *
+ * Requires:
+ * \li "filename" is not NULL
+ * \li "type" is DST_TYPE_KEY look for a KEY record otherwise DNSKEY
+ * \li "mctx" is a valid memory context
+ * \li "keyp" is not NULL and "*keyp" is NULL.
+ * 
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li DST_R_BADKEYTYPE if the key type is not the expected one
+ * \li ISC_R_UNEXPECTEDTOKEN if the file can not be parsed as a public key
+ *
+ * Ensures:
+ * \li If successful, *keyp will contain a valid key.
+ */
+
+isc_result_t
 dst_key_tofile(const dst_key_t *key, int type, const char *directory);
 /*
  * Writes a key to permanent storage.  The key can either be a public or
@@ -368,10 +391,22 @@ dst_key_privatefrombuffer(dst_key_t *key
  *	If successful, key will contain a valid private key.
  */
 
+gss_ctx_id_t
+dst_key_getgssctx(const dst_key_t *key);
+/*%<
+ * Returns the opaque key data.
+ * Be cautions when using this value unless you know what you are doing.
+ *
+ * Requires:
+ *\li  "key" is not NULL.
+ *
+ * Returns:
+ *\li  gssctx key data, possibly NULL.
+ */
 
 isc_result_t
 dst_key_fromgssapi(dns_name_t *name, void *opaque, isc_mem_t *mctx,
-		                   dst_key_t **keyp);
+		   dst_key_t **keyp);
 /*
  * Converts a GSSAPI opaque context id into a DST key.
  *
diff -up bind-9.3.6-P1/lib/dns/include/dst/gssapi.h.gssapi bind-9.3.6-P1/lib/dns/include/dst/gssapi.h
--- bind-9.3.6-P1/lib/dns/include/dst/gssapi.h.gssapi	2004-12-09 05:07:20.000000000 +0100
+++ bind-9.3.6-P1/lib/dns/include/dst/gssapi.h	2009-02-23 13:00:10.000000000 +0100
@@ -20,9 +20,18 @@
 #ifndef DST_GSSAPI_H
 #define DST_GSSAPI_H 1
 
-#include <isc/lang.h>
 
+#include <isc/lang.h>
 #include <isc/types.h>
+#include <isc/formatcheck.h>
+#include <isc/platform.h>
+#include <isc/types.h>
+#include <dns/types.h>
+#include <gssapi/gssapi.h>
+
+#ifndef GSS_SPNEGO_MECHANISM
+#define GSS_SPNEGO_MECHANISM ((void*)0)
+#endif
 
 ISC_LANG_BEGINDECLS
 
@@ -35,20 +44,153 @@ ISC_LANG_BEGINDECLS
  ***/
 
 isc_result_t
-dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate, void **cred);
+dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate,
+		       gss_cred_id_t *cred);
+/*
+ *     Acquires GSS credentials.
+ *
+ *     Requires:
+ *     'name'      is a valid name, preferably one known by the GSS provider
+ *     'initiate'  indicates whether the credentials are for initiating or
+ *                 accepting contexts
+ *      'cred'      is a pointer to NULL, which will be allocated with the
+ *                 credential handle.  Call dst_gssapi_releasecred to free
+ *                 the memory.
+ *
+ *     Returns:
+ *             ISC_R_SUCCESS msg was successfully updated to include the
+ *                                   query to be sent
+ *             other             an error occurred while building the message
+ */
+
+isc_result_t
+dst_gssapi_releasecred(gss_cred_id_t *cred);
+/*
+ *     Releases GSS credentials.  Calling this function does release the
+ *  memory allocated for the credential in dst_gssapi_acquirecred()
+ *
+ *     Requires:
+ *      'mctx'  is a valid memory context
+ *      'cred'  is a pointer to the credential to be released
+ *
+ *     Returns:
+ *             ISC_R_SUCCESS   credential was released successfully
+ *             other           an error occurred while releaseing
+ *                             the credential
+ */
+
+isc_result_t
+dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
+		   isc_buffer_t *outtoken, gss_ctx_id_t *gssctx);
+/*
+ *     Initiates a GSS context.
+ *
+ *     Requires:
+ *     'name'     is a valid name, preferably one known by the GSS
+ *     provider
+ *     'intoken'  is a token received from the acceptor, or NULL if
+ *                there isn't one
+ *     'outtoken' is a buffer to receive the token generated by
+ *                gss_init_sec_context() to be sent to the acceptor
+ *      'context'  is a pointer to a valid gss_ctx_id_t
+ *                 (which may have the value GSS_C_NO_CONTEXT)
+ *
+ *     Returns:
+ *             ISC_R_SUCCESS   msg was successfully updated to include the
+ *                             query to be sent
+ *             other           an error occurred while building the message
+ */
 
 isc_result_t
-dst_gssapi_initctx(dns_name_t *name, void *cred,
-		   isc_region_t *intoken, isc_buffer_t *outtoken,
-		   void **context);
+dst_gssapi_acceptctx(gss_cred_id_t cred,
+		     isc_region_t *intoken, isc_buffer_t **outtoken,
+		     gss_ctx_id_t *context, dns_name_t *principal,
+		     isc_mem_t *mctx);
+/*
+ *     Accepts a GSS context.
+ *
+ *     Requires:
+ *     'mctx'     is a valid memory context
+ *      'cred'     is the acceptor's valid GSS credential handle
+ *     'intoken'  is a token received from the initiator
+ *     'outtoken' is a pointer a buffer pointer used to return the token
+ *                generated by gss_accept_sec_context() to be sent to the
+ *                initiator
+ *      'context'  is a valid pointer to receive the generated context handle.
+ *                 On the initial call, it should be a pointer to NULL, which
+ *                will be allocated as a gss_ctx_id_t.  Subsequent calls
+ *                should pass in the handle generated on the first call.
+ *                Call dst_gssapi_releasecred to delete the context and free
+ *                the memory.
+ *
+ *     Requires:
+ *             'outtoken' to != NULL && *outtoken == NULL.
+ *
+ *     Returns:
+ *             ISC_R_SUCCESS   msg was successfully updated to include the
+ *                             query to be sent
+ *             other           an error occurred while building the message
+ */
 
 isc_result_t
-dst_gssapi_acceptctx(dns_name_t *name, void *cred,
-		     isc_region_t *intoken, isc_buffer_t *outtoken,
-		     void **context);
+dst_gssapi_deletectx(isc_mem_t *mctx, gss_ctx_id_t *gssctx);
+/*
+ *     Destroys a GSS context.  This function deletes the context from the GSS
+ *     provider and then frees the memory used by the context pointer.
+ *
+ *     Requires:
+ *      'mctx'    is a valid memory context
+ *     'context' is a valid GSS context
+ *
+ *     Returns:
+ *             ISC_R_SUCCESS
+ */
+
+
+void
+gss_log(int level, const char *fmt, ...)
+ISC_FORMAT_PRINTF(2, 3);
+/*
+ * Loging function for GSS.
+ *  
+ *  Requires
+ *      'level' is the log level to be used, as an integer
+ *      'fmt'   is a printf format specifier
+ */
+
+char *
+gss_error_tostring(isc_uint32_t major, isc_uint32_t minor,
+		   char *buf, size_t buflen);
+/*
+ *     Render a GSS major status/minor status pair into a string
+ *
+ *     Requires:
+ *      'major' is a GSS major status code
+ *     'minor' is a GSS minor status code
+ *
+ *     Returns:
+ *             A string containing the text representation of the error codes.
+ *             Users should copy the string if they wish to keep it.
+ */
+
+isc_boolean_t
+dst_gssapi_identitymatchesrealmkrb5(dns_name_t *signer, dns_name_t *name,
+				    dns_name_t *realm);
+/*
+ *     Compare a "signer" (in the format of a Kerberos-format Kerberos5
+ *     printipal: host/example.com@EXAMPLE.COM) to the realm name stored
+ *     in "name" (which represents the realm name).
+ *
+ */
 
+isc_boolean_t
+dst_gssapi_identitymatchesrealmms(dns_name_t *signer, dns_name_t *name,
+				  dns_name_t *realm);
 /*
- * XXX
+ *     Compare a "signer" (in the format of a Kerberos-format Kerberos5
+ *     printipal: host/example.com@EXAMPLE.COM) to the realm name stored
+ *     in "name" (which represents the realm name).
+ *
  */
 
 ISC_LANG_ENDDECLS
diff -up bind-9.3.6-P1/lib/dns/include/dst/Makefile.in.gssapi bind-9.3.6-P1/lib/dns/include/dst/Makefile.in
--- bind-9.3.6-P1/lib/dns/include/dst/Makefile.in.gssapi	2004-12-09 05:07:19.000000000 +0100
+++ bind-9.3.6-P1/lib/dns/include/dst/Makefile.in	2009-02-23 13:00:10.000000000 +0100
@@ -21,7 +21,7 @@ top_srcdir =	@top_srcdir@
 
 @BIND9_VERSION@
 
-HEADERS =	dst.h lib.h result.h
+HEADERS =	dst.h gssapi.h lib.h result.h
 
 SUBDIRS =
 TARGETS =
diff -up bind-9.3.6-P1/lib/dns/Makefile.in.gssapi bind-9.3.6-P1/lib/dns/Makefile.in
--- bind-9.3.6-P1/lib/dns/Makefile.in.gssapi	2009-02-23 13:00:10.000000000 +0100
+++ bind-9.3.6-P1/lib/dns/Makefile.in	2009-02-23 13:00:10.000000000 +0100
@@ -114,7 +114,7 @@ libdns.la: ${OBJS}
 	${LIBTOOL_MODE_LINK} \
 		${CC} ${ALL_CFLAGS} ${LDFLAGS} -o libdns.la -rpath ${libdir} \
 		-version-info ${LIBINTERFACE}:${LIBREVISION}:${LIBAGE} \
-		${OBJS} ${ISCLIBS} @DNS_CRYPTO_LIBS@ ${LIBS}
+		${OBJS} ${ISCLIBS} @DNS_CRYPTO_LIBS@ ${LIBS} -lgssapi_krb5
 
 timestamp: libdns.@A@
 	touch timestamp
diff -up bind-9.3.6-P1/lib/dns/ssu.c.gssapi bind-9.3.6-P1/lib/dns/ssu.c
--- bind-9.3.6-P1/lib/dns/ssu.c.gssapi	2004-03-08 10:04:32.000000000 +0100
+++ bind-9.3.6-P1/lib/dns/ssu.c	2009-02-23 13:00:10.000000000 +0100
@@ -30,6 +30,8 @@
 #include <dns/name.h>
 #include <dns/ssu.h>
 
+#include <dst/gssapi.h>
+
 #define SSUTABLEMAGIC		ISC_MAGIC('S', 'S', 'U', 'T')
 #define VALID_SSUTABLE(table)	ISC_MAGIC_VALID(table, SSUTABLEMAGIC)
 
@@ -160,7 +162,7 @@ dns_ssutable_addrule(dns_ssutable_t *tab
 	REQUIRE(VALID_SSUTABLE(table));
 	REQUIRE(dns_name_isabsolute(identity));
 	REQUIRE(dns_name_isabsolute(name));
-	REQUIRE(matchtype <= DNS_SSUMATCHTYPE_SELF);
+	REQUIRE(matchtype <= DNS_SSUMATCHTYPE_MAX);
 	if (matchtype == DNS_SSUMATCHTYPE_WILDCARD)
 		REQUIRE(dns_name_iswildcard(name));
 	if (ntypes > 0)
@@ -256,37 +258,68 @@ dns_ssutable_checkrules(dns_ssutable_t *
 
 	if (signer == NULL)
 		return (ISC_FALSE);
-	rule = ISC_LIST_HEAD(table->rules);
-		rule = ISC_LIST_NEXT(rule, link);
+
 	for (rule = ISC_LIST_HEAD(table->rules);
 	     rule != NULL;
 	     rule = ISC_LIST_NEXT(rule, link))
 	{
-		if (dns_name_iswildcard(rule->identity)) {
-			if (!dns_name_matcheswildcard(signer, rule->identity))
-				continue;
-		}
-		else {
-			if (!dns_name_equal(signer, rule->identity))
-				continue;
+		switch (rule->matchtype) {
+		case DNS_SSUMATCHTYPE_NAME:
+		case DNS_SSUMATCHTYPE_SUBDOMAIN:
+		case DNS_SSUMATCHTYPE_WILDCARD:
+		case DNS_SSUMATCHTYPE_SELF:
+			if (dns_name_iswildcard(rule->identity)) {
+				if (!dns_name_matcheswildcard(signer,
+							      rule->identity))
+					continue;
+			} else {
+				if (!dns_name_equal(signer, rule->identity))
+					continue;
+			}
+			break;
 		}
 
-		if (rule->matchtype == DNS_SSUMATCHTYPE_NAME) {
+		switch (rule->matchtype) {
+		case DNS_SSUMATCHTYPE_NAME:
 			if (!dns_name_equal(name, rule->name))
 				continue;
-		}
-		else if (rule->matchtype == DNS_SSUMATCHTYPE_SUBDOMAIN) {
+			break;
+		case DNS_SSUMATCHTYPE_SUBDOMAIN:
 			if (!dns_name_issubdomain(name, rule->name))
 				continue;
-		}
-		else if (rule->matchtype == DNS_SSUMATCHTYPE_WILDCARD) {
+			break;
+		case DNS_SSUMATCHTYPE_WILDCARD:
 			if (!dns_name_matcheswildcard(name, rule->name))
 				continue;
-
-		}
-		else if (rule->matchtype == DNS_SSUMATCHTYPE_SELF) {
+			break;
+		case DNS_SSUMATCHTYPE_SELF:
 			if (!dns_name_equal(signer, name))
 				continue;
+			break;
+		case DNS_SSUMATCHTYPE_SELFKRB5:
+			if (!dst_gssapi_identitymatchesrealmkrb5(signer, name,
+								 rule->identity))
+				continue;
+			break;
+		case DNS_SSUMATCHTYPE_SELFMS:
+			if (!dst_gssapi_identitymatchesrealmms(signer, name,
+							       rule->identity))
+				continue;
+			break;
+		case DNS_SSUMATCHTYPE_SUBDOMAINKRB5:
+			if (!dns_name_issubdomain(name, rule->name))
+				continue;
+			if (!dst_gssapi_identitymatchesrealmkrb5(signer, NULL,
+								 rule->identity))
+				continue;
+			break;
+		case DNS_SSUMATCHTYPE_SUBDOMAINMS:
+			if (!dns_name_issubdomain(name, rule->name))
+				continue;
+			if (!dst_gssapi_identitymatchesrealmms(signer, NULL,
+							       rule->identity))
+				continue;
+			break;
 		}
 
 		if (rule->ntypes == 0) {
diff -up bind-9.3.6-P1/lib/dns/tkey.c.gssapi bind-9.3.6-P1/lib/dns/tkey.c
--- bind-9.3.6-P1/lib/dns/tkey.c.gssapi	2008-01-03 00:45:33.000000000 +0100
+++ bind-9.3.6-P1/lib/dns/tkey.c	2009-02-23 13:10:12.000000000 +0100
@@ -66,6 +66,20 @@ tkey_log(const char *fmt, ...) {
 	va_end(ap);
 }
 
+static void
+_dns_tkey_dumpmessage(dns_message_t *msg) {
+	isc_buffer_t outbuf;
+	unsigned char output[2048];
+	isc_result_t result;
+
+	isc_buffer_init(&outbuf, output, sizeof(output));
+	result = dns_message_totext(msg, &dns_master_style_debug, 0,
+				    &outbuf);
+	/* XXXMLG ignore result */
+	fprintf(stderr, "%.*s\n", (int)isc_buffer_usedlength(&outbuf),
+		(char *)isc_buffer_base(&outbuf));
+}
+
 isc_result_t
 dns_tkeyctx_create(isc_mem_t *mctx, isc_entropy_t *ectx, dns_tkeyctx_t **tctxp)
 {
@@ -107,6 +121,8 @@ dns_tkeyctx_destroy(dns_tkeyctx_t **tctx
 			dns_name_free(tctx->domain, mctx);
 		isc_mem_put(mctx, tctx->domain, sizeof(dns_name_t));
 	}
+	if (tctx->gsscred != NULL)
+		dst_gssapi_releasecred(&tctx->gsscred);
 	isc_entropy_detach(&tctx->ectx);
 	isc_mem_put(mctx, tctx, sizeof(dns_tkeyctx_t));
 	isc_mem_detach(&mctx);
@@ -410,68 +426,108 @@ process_gsstkey(dns_message_t *msg, dns_
 {
 	isc_result_t result = ISC_R_SUCCESS;
 	dst_key_t *dstkey = NULL;
-	void *gssctx = NULL;
+	dns_tsigkey_t *tsigkey = NULL;
+	dns_fixedname_t principal;
 	isc_stdtime_t now;
 	isc_region_t intoken;
-	unsigned char array[1024];
-	isc_buffer_t outtoken;
+	isc_buffer_t *outtoken = NULL;
+	gss_ctx_id_t gss_ctx = NULL;
 
 	UNUSED(namelist);
+	UNUSED(signer);
 
 	if (tctx->gsscred == NULL)
 		return (ISC_R_NOPERM);
 
 	if (!dns_name_equal(&tkeyin->algorithm, DNS_TSIG_GSSAPI_NAME) &&
 	    !dns_name_equal(&tkeyin->algorithm, DNS_TSIG_GSSAPIMS_NAME)) {
+		tkey_log("process_gsstkey(): dns_tsigerror_badalg");    /* XXXSRA */
 		tkeyout->error = dns_tsigerror_badalg;
 		return (ISC_R_SUCCESS);
 	}
 
+	/*
+	 * XXXDCL need to check for key expiry per 4.1.1
+	 * XXXDCL need a way to check fully established, perhaps w/key_flags
+	 */
+
 	intoken.base = tkeyin->key;
 	intoken.length = tkeyin->keylen;
 
-	isc_buffer_init(&outtoken, array, sizeof(array));
-	RETERR(dst_gssapi_acceptctx(name, tctx->gsscred, &intoken,
-				    &outtoken, &gssctx));
+	result = dns_tsigkey_find(&tsigkey, name, &tkeyin->algorithm, ring);
+	if (result == ISC_R_SUCCESS)
+		gss_ctx = dst_key_getgssctx(tsigkey->key);
 
-	dstkey = NULL;
-	RETERR(dst_key_fromgssapi(name, gssctx, msg->mctx, &dstkey));
+	dns_fixedname_init(&principal);
 
-	result = dns_tsigkey_createfromkey(name, &tkeyin->algorithm,
-					   dstkey, ISC_TRUE, signer,
-					   tkeyin->inception, tkeyin->expire,
-					   ring->mctx, ring, NULL);
-#if 1
-	if (result != ISC_R_SUCCESS)
-		goto failure;
-#else
-	if (result == ISC_R_NOTFOUND) {
-		tkeyout->error = dns_tsigerror_badalg;
+	result = dst_gssapi_acceptctx(tctx->gsscred, &intoken,
+				      &outtoken, &gss_ctx,
+				      dns_fixedname_name(&principal),
+				      tctx->mctx);
+
+	if (tsigkey != NULL)
+		dns_tsigkey_detach(&tsigkey);
+
+	if (result == DNS_R_INVALIDTKEY) {
+		tkeyout->error = dns_tsigerror_badkey;
+		tkey_log("process_gsstkey(): dns_tsigerror_badkey");    /* XXXSRA */
 		return (ISC_R_SUCCESS);
-	}
-	if (result != ISC_R_SUCCESS)
+	} else if (result == ISC_R_FAILURE)
 		goto failure;
-#endif
 
-	/* This key is good for a long time */
+	ENSURE(result == DNS_R_CONTINUE || result == ISC_R_SUCCESS);
+	/*
+	 * XXXDCL Section 4.1.3: Limit GSS_S_CONTINUE_NEEDED to 10 times.
+	 */
+
+	if (tsigkey == NULL) {
+		RETERR(dst_key_fromgssapi(name, gss_ctx, msg->mctx, &dstkey));
+		RETERR(dns_tsigkey_createfromkey(name, &tkeyin->algorithm,
+						 dstkey, ISC_TRUE,
+						 dns_fixedname_name(&principal),
+						 tkeyin->inception,
+						 tkeyin->expire,
+						 msg->mctx, ring, NULL));
+	}
+
 	isc_stdtime_get(&now);
 	tkeyout->inception = tkeyin->inception;
 	tkeyout->expire = tkeyin->expire;
-
-	tkeyout->key = isc_mem_get(msg->mctx,
-				   isc_buffer_usedlength(&outtoken));
-	if (tkeyout->key == NULL) {
-		result = ISC_R_NOMEMORY;
+	if (outtoken) {
+		tkeyout->key = isc_mem_get(tkeyout->mctx,
+					   isc_buffer_usedlength(outtoken));
+		if (tkeyout->key == NULL) {
+			result = ISC_R_NOMEMORY;
+			goto failure;
+		}
+		tkeyout->keylen = isc_buffer_usedlength(outtoken);
+		memcpy(tkeyout->key, isc_buffer_base(outtoken),
+		isc_buffer_usedlength(outtoken));
+		isc_buffer_free(&outtoken);
+	} else {
+		tkeyout->key = isc_mem_get(tkeyout->mctx, tkeyin->keylen);
+		if (tkeyout->key == NULL) {
+			result = ISC_R_NOMEMORY;
+			goto failure;
+		}
+		tkeyout->keylen = tkeyin->keylen;
+		memcpy(tkeyout->key, tkeyin->key, tkeyin->keylen);
 		goto failure;
 	}
-	tkeyout->keylen = isc_buffer_usedlength(&outtoken);
-	memcpy(tkeyout->key, isc_buffer_base(&outtoken), tkeyout->keylen);
+
+	tkeyout->error = dns_rcode_noerror;
+	tkey_log("process_gsstkey(): dns_tsigerror_noerror");   /* XXXSRA */
 
 	return (ISC_R_SUCCESS);
 
- failure:
+failure:
 	if (dstkey != NULL)
 		dst_key_free(&dstkey);
+	if (outtoken != NULL)
+		isc_buffer_free(&outtoken);
+
+	tkey_log("process_gsstkey(): %s",
+		isc_result_totext(result));     /* XXXSRA */
 
 	return (result);
 }
@@ -632,7 +688,7 @@ dns_tkey_processquery(dns_message_t *msg
 	if (tkeyin.mode != DNS_TKEYMODE_DELETE) {
 		dns_tsigkey_t *tsigkey = NULL;
 
-		if (tctx->domain == NULL) {
+		if (tctx->domain == NULL && tkeyin.mode != DNS_TKEYMODE_GSSAPI) {
 			tkey_log("dns_tkey_processquery: tkey-domain not set");
 			result = DNS_R_REFUSED;
 			goto failure;
@@ -674,10 +730,18 @@ dns_tkey_processquery(dns_message_t *msg
 			if (result != ISC_R_SUCCESS)
 				goto failure;
 		}
-		result = dns_name_concatenate(keyname, tctx->domain,
-					      keyname, NULL);
-		if (result != ISC_R_SUCCESS)
-			goto failure;
+		if (tkeyin.mode == DNS_TKEYMODE_GSSAPI) {
+			/* Yup.  This is a hack */
+			result = dns_name_concatenate(keyname, dns_rootname,
+						      keyname, NULL);
+			if (result != ISC_R_SUCCESS)
+				goto failure;
+		} else {
+			result = dns_name_concatenate(keyname, tctx->domain,
+						      keyname, NULL);
+			if (result != ISC_R_SUCCESS)
+				goto failure;
+		}
 
 		result = dns_tsigkey_find(&tsigkey, keyname, NULL, ring);
 		if (result == ISC_R_SUCCESS) {
@@ -729,9 +793,9 @@ dns_tkey_processquery(dns_message_t *msg
 	}
 
 	if (tkeyout.key != NULL)
-		isc_mem_put(msg->mctx, tkeyout.key, tkeyout.keylen);
+		isc_mem_put(tkeyout.mctx, tkeyout.key, tkeyout.keylen);
 	if (tkeyout.other != NULL)
-		isc_mem_put(msg->mctx, tkeyout.other, tkeyout.otherlen);
+		isc_mem_put(tkeyout.mctx, tkeyout.other, tkeyout.otherlen);
 	if (result != ISC_R_SUCCESS)
 		goto failure;
 
@@ -759,7 +823,7 @@ dns_tkey_processquery(dns_message_t *msg
 
 static isc_result_t
 buildquery(dns_message_t *msg, dns_name_t *name,
-	   dns_rdata_tkey_t *tkey)
+	   dns_rdata_tkey_t *tkey, isc_boolean_t win2k)
 {
 	dns_name_t *qname = NULL, *aname = NULL;
 	dns_rdataset_t *question = NULL, *tkeyset = NULL;
@@ -780,7 +844,7 @@ buildquery(dns_message_t *msg, dns_name_
 	dns_rdataset_makequestion(question, dns_rdataclass_any,
 				  dns_rdatatype_tkey);
 
-	RETERR(isc_buffer_allocate(msg->mctx, &dynbuf, 512));
+	RETERR(isc_buffer_allocate(msg->mctx, &dynbuf, 2048));
 	RETERR(dns_message_gettemprdata(msg, &rdata));
 	RETERR(dns_rdata_fromstruct(rdata, dns_rdataclass_any,
 				    dns_rdatatype_tkey, tkey, dynbuf));
@@ -808,7 +872,15 @@ buildquery(dns_message_t *msg, dns_name_
 	ISC_LIST_APPEND(aname->list, tkeyset, link);
 
 	dns_message_addname(msg, qname, DNS_SECTION_QUESTION);
-	dns_message_addname(msg, aname, DNS_SECTION_ADDITIONAL);
+
+	/*
+	 * Windows 2000 needs this in the answer section, not the additional
+	 * section where the RFC specifies.
+	 */
+	if (win2k)
+		dns_message_addname(msg, aname, DNS_SECTION_ANSWER);
+	else
+		dns_message_addname(msg, aname, DNS_SECTION_ADDITIONAL);
 
 	return (ISC_R_SUCCESS);
 
@@ -823,6 +895,7 @@ buildquery(dns_message_t *msg, dns_name_
 	}
 	if (dynbuf != NULL)
 		isc_buffer_free(&dynbuf);
+	printf("buildquery error\n");
 	return (result);
 }
 
@@ -869,7 +942,7 @@ dns_tkey_builddhquery(dns_message_t *msg
 	tkey.other = NULL;
 	tkey.otherlen = 0;
 
-	RETERR(buildquery(msg, name, &tkey));
+	RETERR(buildquery(msg, name, &tkey, ISC_FALSE));
 
 	if (nonce == NULL)
 		isc_mem_put(msg->mctx, r.base, 0);
@@ -900,23 +973,25 @@ dns_tkey_builddhquery(dns_message_t *msg
 }
 
 isc_result_t
-dns_tkey_buildgssquery(dns_message_t *msg, dns_name_t *name,
-		       dns_name_t *gname, void *cred,
-		       isc_uint32_t lifetime, void **context)
+dns_tkey_buildgssquery(dns_message_t *msg, dns_name_t *name, dns_name_t *gname,
+		       isc_buffer_t *intoken, isc_uint32_t lifetime,
+		       gss_ctx_id_t *context, isc_boolean_t win2k)
 {
 	dns_rdata_tkey_t tkey;
 	isc_result_t result;
 	isc_stdtime_t now;
 	isc_buffer_t token;
-	unsigned char array[1024];
+	unsigned char array[2048];
+
+	UNUSED(intoken);
 
 	REQUIRE(msg != NULL);
 	REQUIRE(name != NULL);
 	REQUIRE(gname != NULL);
-	REQUIRE(context != NULL && *context == NULL);
+	REQUIRE(context != NULL);
 
 	isc_buffer_init(&token, array, sizeof(array));
-	result = dst_gssapi_initctx(gname, cred, NULL, &token, context);
+	result = dst_gssapi_initctx(gname, NULL, &token, context);
 	if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS)
 		return (result);
 
@@ -925,7 +1000,12 @@ dns_tkey_buildgssquery(dns_message_t *ms
 	ISC_LINK_INIT(&tkey.common, link);
 	tkey.mctx = NULL;
 	dns_name_init(&tkey.algorithm, NULL);
-	dns_name_clone(DNS_TSIG_GSSAPI_NAME, &tkey.algorithm);
+
+	if (win2k)
+		dns_name_clone(DNS_TSIG_GSSAPIMS_NAME, &tkey.algorithm);
+	else
+		dns_name_clone(DNS_TSIG_GSSAPI_NAME, &tkey.algorithm);
+
 	isc_stdtime_get(&now);
 	tkey.inception = now;
 	tkey.expire = now + lifetime;
@@ -936,7 +1016,7 @@ dns_tkey_buildgssquery(dns_message_t *ms
 	tkey.other = NULL;
 	tkey.otherlen = 0;
 
-	RETERR(buildquery(msg, name, &tkey));
+	RETERR(buildquery(msg, name, &tkey, win2k));
 
 	return (ISC_R_SUCCESS);
 
@@ -963,7 +1043,7 @@ dns_tkey_builddeletequery(dns_message_t 
 	tkey.keylen = tkey.otherlen = 0;
 	tkey.key = tkey.other = NULL;
 
-	return (buildquery(msg, &key->name, &tkey));
+	return (buildquery(msg, &key->name, &tkey, ISC_FALSE));
 }
 
 static isc_result_t
@@ -1037,7 +1117,7 @@ dns_tkey_processdhresponse(dns_message_t
 	    rmsg->rcode != dns_rcode_noerror)
 	{
 		tkey_log("dns_tkey_processdhresponse: tkey mode invalid "
-			 "or error set");
+			 "or error set(1)");
 		result = DNS_R_INVALIDTKEY;
 		dns_rdata_freestruct(&qtkey);
 		goto failure;
@@ -1127,18 +1207,19 @@ dns_tkey_processdhresponse(dns_message_t
 
 isc_result_t
 dns_tkey_processgssresponse(dns_message_t *qmsg, dns_message_t *rmsg,
-			    dns_name_t *gname, void *cred, void **context,
-			    dns_tsigkey_t **outkey, dns_tsig_keyring_t *ring)
+			    dns_name_t *gname, gss_ctx_id_t *context,
+			    isc_buffer_t *outtoken, dns_tsigkey_t **outkey,
+			    dns_tsig_keyring_t *ring)
 {
 	dns_rdata_t rtkeyrdata = DNS_RDATA_INIT, qtkeyrdata = DNS_RDATA_INIT;
 	dns_name_t *tkeyname;
 	dns_rdata_tkey_t rtkey, qtkey;
-	isc_buffer_t outtoken;
+	isc_buffer_t intoken;
 	dst_key_t *dstkey = NULL;
-	isc_region_t r;
 	isc_result_t result;
 	unsigned char array[1024];
 
+	REQUIRE(outtoken != NULL);
 	REQUIRE(qmsg != NULL);
 	REQUIRE(rmsg != NULL);
 	REQUIRE(gname != NULL);
@@ -1150,31 +1231,42 @@ dns_tkey_processgssresponse(dns_message_
 	RETERR(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER));
 	RETERR(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL));
 
-	RETERR(find_tkey(qmsg, &tkeyname, &qtkeyrdata,
-			 DNS_SECTION_ADDITIONAL));
+	/*
+	 * Win2k puts the item in the ANSWER section, while the RFC
+	 * specifies it should be in the ADDITIONAL section.  Check first
+	 * where it should be, and then where it may be.
+	 */
+	result = find_tkey(qmsg, &tkeyname, &qtkeyrdata,
+			   DNS_SECTION_ADDITIONAL);
+	if (result == ISC_R_NOTFOUND)
+		result = find_tkey(qmsg, &tkeyname, &qtkeyrdata,
+				   DNS_SECTION_ANSWER);
+	if (result != ISC_R_SUCCESS)
+		goto failure;
+
 	RETERR(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL));
 
 	if (rtkey.error != dns_rcode_noerror ||
 	    rtkey.mode != DNS_TKEYMODE_GSSAPI ||
-	    !dns_name_equal(&rtkey.algorithm, &rtkey.algorithm))
-	{
-		tkey_log("dns_tkey_processdhresponse: tkey mode invalid "
-			 "or error set");
+	    !dns_name_equal(&rtkey.algorithm, &qtkey.algorithm)) {
+		tkey_log("dns_tkey_processgssresponse: tkey mode invalid "
+			 "or error set(2) %d", rtkey.error);
+		_dns_tkey_dumpmessage(qmsg);
+		_dns_tkey_dumpmessage(rmsg);
 		result = DNS_R_INVALIDTKEY;
 		goto failure;
 	}
 
-	isc_buffer_init(&outtoken, array, sizeof(array));
-	r.base = rtkey.key;
-	r.length = rtkey.keylen;
-	RETERR(dst_gssapi_initctx(gname, cred, &r, &outtoken, context));
+	isc_buffer_init(outtoken, array, sizeof(array));
+	isc_buffer_init(&intoken, rtkey.key, rtkey.keylen);
+	RETERR(dst_gssapi_initctx(gname, &intoken, outtoken, context));
 
 	dstkey = NULL;
 	RETERR(dst_key_fromgssapi(dns_rootname, *context, rmsg->mctx,
 				  &dstkey));
 
 	RETERR(dns_tsigkey_createfromkey(tkeyname, DNS_TSIG_GSSAPI_NAME,
-					 dstkey, ISC_TRUE, NULL,
+					 dstkey, ISC_FALSE, NULL,
 					 rtkey.inception, rtkey.expire,
 					 ring->mctx, ring, outkey));
 
@@ -1182,6 +1274,9 @@ dns_tkey_processgssresponse(dns_message_
 	return (result);
 
  failure:
+	/*
+	 * XXXSRA This probably leaks memory from rtkey and qtkey.
+	 */
 	return (result);
 }
 
@@ -1215,7 +1310,7 @@ dns_tkey_processdeleteresponse(dns_messa
 	    rmsg->rcode != dns_rcode_noerror)
 	{
 		tkey_log("dns_tkey_processdeleteresponse: tkey mode invalid "
-			 "or error set");
+			 "or error set(3)");
 		result = DNS_R_INVALIDTKEY;
 		dns_rdata_freestruct(&qtkey);
 		dns_rdata_freestruct(&rtkey);
@@ -1240,3 +1335,84 @@ dns_tkey_processdeleteresponse(dns_messa
  failure:
 	return (result);
 }
+
+isc_result_t
+dns_tkey_gssnegotiate(dns_message_t *qmsg, dns_message_t *rmsg,
+		      dns_name_t *server, gss_ctx_id_t *context,
+		      dns_tsigkey_t **outkey, dns_tsig_keyring_t *ring,
+		      isc_boolean_t win2k)
+{
+	dns_rdata_t rtkeyrdata = DNS_RDATA_INIT, qtkeyrdata = DNS_RDATA_INIT;
+	dns_name_t *tkeyname;
+	dns_rdata_tkey_t rtkey, qtkey;
+	isc_buffer_t intoken, outtoken;
+	dst_key_t *dstkey = NULL;
+	isc_result_t result;
+	unsigned char array[1024];
+
+	REQUIRE(qmsg != NULL);
+	REQUIRE(rmsg != NULL);
+	REQUIRE(server != NULL);
+	if (outkey != NULL)
+		REQUIRE(*outkey == NULL);
+
+	if (rmsg->rcode != dns_rcode_noerror)
+		return (ISC_RESULTCLASS_DNSRCODE + rmsg->rcode);
+
+	RETERR(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER));
+	RETERR(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL));
+
+	if (win2k == ISC_TRUE)
+		RETERR(find_tkey(qmsg, &tkeyname, &qtkeyrdata,
+				 DNS_SECTION_ANSWER));
+	else
+		RETERR(find_tkey(qmsg, &tkeyname, &qtkeyrdata,
+				 DNS_SECTION_ADDITIONAL));
+
+	RETERR(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL));
+
+	if (rtkey.error != dns_rcode_noerror ||
+	    rtkey.mode != DNS_TKEYMODE_GSSAPI ||
+	    !dns_name_equal(&rtkey.algorithm, &qtkey.algorithm))
+	{
+		tkey_log("dns_tkey_processdhresponse: tkey mode invalid "
+			 "or error set(4)");
+		result = DNS_R_INVALIDTKEY;
+		goto failure;
+	}
+
+	isc_buffer_init(&intoken, rtkey.key, rtkey.keylen);
+	isc_buffer_init(&outtoken, array, sizeof(array));
+
+	result = dst_gssapi_initctx(server, &intoken, &outtoken, context);
+	if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS)
+		return (result);
+
+	dstkey = NULL;
+	RETERR(dst_key_fromgssapi(dns_rootname, *context, rmsg->mctx,
+				  &dstkey));
+
+	/*
+	 * XXXSRA This seems confused.  If we got CONTINUE from initctx,
+	 * the GSS negotiation hasn't completed yet, so we can't sign
+	 * anything yet.
+	 */
+
+	RETERR(dns_tsigkey_createfromkey(tkeyname, 
+					 (win2k
+					  ? DNS_TSIG_GSSAPIMS_NAME
+					  : DNS_TSIG_GSSAPI_NAME),
+					 dstkey, ISC_TRUE, NULL,
+					 rtkey.inception, rtkey.expire,
+					 rmsg->mctx, ring, outkey));
+
+	dns_rdata_freestruct(&rtkey);
+	return (result);
+
+failure:
+	/*
+	 * XXXSRA This probably leaks memory from qtkey.
+	 */
+	dns_rdata_freestruct(&rtkey);
+	return (result);
+}
diff -up bind-9.3.6-P1/lib/dns/tsig.c.gssapi bind-9.3.6-P1/lib/dns/tsig.c
--- bind-9.3.6-P1/lib/dns/tsig.c.gssapi	2008-01-24 14:06:47.000000000 +0100
+++ bind-9.3.6-P1/lib/dns/tsig.c	2009-02-23 13:42:58.000000000 +0100
@@ -29,6 +29,7 @@
 #include <isc/string.h>		/* Required for HP/UX (and others?) */
 #include <isc/util.h>
 
+#include <dns/fixedname.h>
 #include <dns/keyvalues.h>
 #include <dns/log.h>
 #include <dns/message.h>
@@ -81,7 +82,7 @@ static dns_name_t gsstsig = {
 
 LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_gssapi_name = &gsstsig;
 
-/* It's nice of Microsoft to conform to their own standard. */
+/* It's "nice" of Microsoft to conform to their own standard. */
 static unsigned char gsstsigms_ndata[] = "\003gss\011microsoft\003com";
 static unsigned char gsstsigms_offsets[] = { 0, 4, 14, 18 };
 
@@ -104,10 +105,16 @@ tsig_log(dns_tsigkey_t *key, int level, 
      ISC_FORMAT_PRINTF(3, 4);
 
 static void
+cleanup_ring(dns_tsig_keyring_t *ring);
+static void
+tsigkey_free(dns_tsigkey_t *key);
+
+static void
 tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...) {
 	va_list ap;
 	char message[4096];
 	char namestr[DNS_NAME_FORMATSIZE];
+	char creatorstr[DNS_NAME_FORMATSIZE];
 
 	if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
 		return;
@@ -115,11 +122,22 @@ tsig_log(dns_tsigkey_t *key, int level, 
 		dns_name_format(&key->name, namestr, sizeof(namestr));
 	else
 		strcpy(namestr, "<null>");
+
+	if (key != NULL && key->generated)
+		dns_name_format(key->creator, creatorstr, sizeof(creatorstr));
+
 	va_start(ap, fmt);
 	vsnprintf(message, sizeof(message), fmt, ap);
 	va_end(ap);
-	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG,
-		      level, "tsig key '%s': %s", namestr, message);
+	if (key != NULL && key->generated)
+		isc_log_write(dns_lctx,
+			      DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG,
+			      level, "tsig key '%s' (%s): %s",
+			      namestr, creatorstr, message);
+	else
+		isc_log_write(dns_lctx,
+			      DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG,
+			      level, "tsig key '%s': %s", namestr, message);
 }
 
 isc_result_t
@@ -203,20 +221,14 @@ dns_tsigkey_createfromkey(dns_name_t *na
 	tkey->key = dstkey;
 	tkey->ring = ring;
 
-	if (ring != NULL) {
-		RWLOCK(&ring->lock, isc_rwlocktype_write);
-		ret = dns_rbt_addname(ring->keys, name, tkey);
-		if (ret != ISC_R_SUCCESS) {
-			RWUNLOCK(&ring->lock, isc_rwlocktype_write);
-			goto cleanup_algorithm;
-		}
-		refs++;
-		RWUNLOCK(&ring->lock, isc_rwlocktype_write);
-	}
-
 	if (key != NULL)
 		refs++;
+	if (ring != NULL)
+		refs++;
 	isc_refcount_init(&tkey->refs, refs);
+	if (ret != ISC_R_SUCCESS)
+		goto cleanup_creator;
+
 	tkey->generated = generated;
 	tkey->inception = inception;
 	tkey->expire = expire;
@@ -225,7 +237,32 @@ dns_tsigkey_createfromkey(dns_name_t *na
 
 	tkey->magic = TSIG_MAGIC;
 
-	if (dstkey != NULL && dst_key_size(dstkey) < 64) {
+	if (ring != NULL) {
+		RWLOCK(&ring->lock, isc_rwlocktype_write);
+		ring->writecount++;
+
+		/*
+		 * Do on the fly cleaning.  Find some nodes we might not
+		 * want around any more.
+		 */
+		if (ring->writecount > 10) {
+			cleanup_ring(ring);
+			ring->writecount = 0;
+		}
+		ret = dns_rbt_addname(ring->keys, name, tkey);
+		if (ret != ISC_R_SUCCESS) {
+			RWUNLOCK(&ring->lock, isc_rwlocktype_write);
+			goto cleanup_refs;
+		}
+		RWUNLOCK(&ring->lock, isc_rwlocktype_write);
+	}
+
+	/*
+	 * Ignore this if it's a GSS key, since the key size is meaningless.
+	 */
+	if (dstkey != NULL && dst_key_size(dstkey) < 64 &&
+	    !dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME) &&
+	    !dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) {
 		char namestr[DNS_NAME_FORMATSIZE];
 		dns_name_format(name, namestr, sizeof(namestr));
 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
@@ -238,6 +275,16 @@ dns_tsigkey_createfromkey(dns_name_t *na
 
 	return (ISC_R_SUCCESS);
 
+ cleanup_refs:
+	tkey->magic = 0;
+	while (refs-- > 0)
+		isc_refcount_decrement(&tkey->refs, NULL);
+	isc_refcount_destroy(&tkey->refs);
+ cleanup_creator:
+	if (tkey->creator != NULL) {
+		dns_name_free(tkey->creator, mctx);
+		isc_mem_put(mctx, tkey->creator, sizeof(dns_name_t));
+	}
  cleanup_algorithm:
 	if (algname_is_allocated(tkey->algorithm)) {
 		if (dns_name_dynamic(tkey->algorithm))
@@ -252,6 +299,68 @@ dns_tsigkey_createfromkey(dns_name_t *na
 	return (ret);
 }
 
+/*
+ * Find a few nodes to destroy if possible.
+ */
+static void
+cleanup_ring(dns_tsig_keyring_t *ring)
+{
+	isc_result_t result;
+	dns_rbtnodechain_t chain;
+	dns_name_t foundname;
+	dns_fixedname_t fixedorigin;
+	dns_name_t *origin;
+	isc_stdtime_t now;
+	dns_rbtnode_t *node;
+	dns_tsigkey_t *tkey;
+
+	/*
+	 * Start up a new iterator each time.
+	 */
+	isc_stdtime_get(&now);
+	dns_name_init(&foundname, NULL);
+	dns_fixedname_init(&fixedorigin);
+	origin = dns_fixedname_name(&fixedorigin);
+
+ again:
+	dns_rbtnodechain_init(&chain, ring->mctx);
+	result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
+					origin);
+	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+		dns_rbtnodechain_invalidate(&chain);
+		return;
+	}
+
+	for (;;) {
+		node = NULL;
+		dns_rbtnodechain_current(&chain, &foundname, origin, &node);
+		tkey = node->data;
+		if (tkey != NULL) {
+			tsig_log(tkey, 2, "tsig expire: generated=%d, refs=%d, expire=%d)",
+				 tkey->generated, isc_refcount_current(&tkey->refs),
+				 now - tkey->expire);
+			if (tkey->generated
+			    && isc_refcount_current(&tkey->refs) == 1
+			    && tkey->inception != tkey->expire
+			    && tkey->expire < now) {
+				tsig_log(tkey, 2, "tsig expire: deleting");
+				/* delete the key */
+				dns_rbtnodechain_invalidate(&chain);
+				(void)dns_rbt_deletename(ring->keys,
+							 &tkey->name,
+							 ISC_FALSE);
+				goto again;
+			}
+		}
+		result = dns_rbtnodechain_next(&chain, &foundname,
+					       origin);
+		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+			dns_rbtnodechain_invalidate(&chain);
+			return;
+		}
+	}
+}
+
 isc_result_t
 dns_tsigkey_create(dns_name_t *name, dns_name_t *algorithm,
 		   unsigned char *secret, int length, isc_boolean_t generated,
@@ -1133,6 +1242,10 @@ dns_tsigkey_find(dns_tsigkey_t **tsigkey
 	REQUIRE(name != NULL);
 	REQUIRE(ring != NULL);
 
+	RWLOCK(&ring->lock, isc_rwlocktype_write);
+	cleanup_ring(ring);
+	RWUNLOCK(&ring->lock, isc_rwlocktype_write);
+
 	isc_stdtime_get(&now);
 	RWLOCK(&ring->lock, isc_rwlocktype_read);
 	key = NULL;
@@ -1190,10 +1303,7 @@ dns_tsigkeyring_create(isc_mem_t *mctx, 
 	result = isc_rwlock_init(&ring->lock, 0, 0);
 	if (result != ISC_R_SUCCESS) {
 		isc_mem_put(mctx, ring, sizeof(dns_tsig_keyring_t));
-		UNEXPECTED_ERROR(__FILE__, __LINE__,
-				 "isc_rwlock_init() failed: %s",
-				 isc_result_totext(result));
-		return (ISC_R_UNEXPECTED);
+		return (result);
 	}
 
 	ring->keys = NULL;
@@ -1206,6 +1316,7 @@ dns_tsigkeyring_create(isc_mem_t *mctx, 
 
 	ring->mctx = NULL;
 	isc_mem_attach(mctx, &ring->mctx);
+	ring->writecount = 0;
 
 	*ringp = ring;
 	return (ISC_R_SUCCESS);