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);