From: Ondřej Surý Date: Tue, 17 Mar 2026 23:28:04 +0000 (+0100) Subject: Implement RFC 3645 Section 4.1.1 key expiry check in TKEY X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5e10fdc29552d601adfea2ef63b7121877fdee74;p=thirdparty%2Fbind9.git Implement RFC 3645 Section 4.1.1 key expiry check in TKEY Check for existing non-expired TSIG keys before accepting a new GSS-API negotiation. Per RFC 3645 Section 4.1.1: - If a key exists and has not expired, reject with BADNAME - If a key exists but has expired, delete it and start fresh Previously, an expired GSS key would permanently block re-negotiation for that name until the server was restarted. Use BADKEY rather than BADNAME to avoid creating an oracle for key name enumeration by unauthenticated attackers. --- diff --git a/lib/dns/tkey.c b/lib/dns/tkey.c index f09edc2a9c4..42deb423a2b 100644 --- a/lib/dns/tkey.c +++ b/lib/dns/tkey.c @@ -178,15 +178,6 @@ process_gsstkey(dns_message_t *msg, dns_name_t *name, dns_rdata_tkey_t *tkeyin, 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 - */ - result = dns_tsigkey_find(&tsigkey, name, &tkeyin->algorithm, ring); - if (result == ISC_R_SUCCESS) { - gss_ctx = dst_key_getgssctx(tsigkey->key); - } - intoken = (isc_region_t){ tkeyin->key, tkeyin->keylen }; result = dst_gssapi_acceptctx(tctx->gssapi_keytab, &intoken, &outtoken, &gss_ctx, principal, tctx->mctx); @@ -430,10 +421,26 @@ dns_tkey_processquery(dns_message_t *msg, dns_tkeyctx_t *tctx, result = dns_tsigkey_find(&tsigkey, keyname, NULL, ring); if (result == ISC_R_SUCCESS) { - tkeyout.error = dns_tsigerror_badname; - dns_tsigkey_detach(&tsigkey); - break; - } else if (result == ISC_R_NOTFOUND) { + /* + * RFC 3645 Section 4.1.1: if the key exists + * but has expired, delete it and allow + * re-negotiation. We use BADKEY rather than + * BADNAME to avoid creating an oracle for key + * name enumeration. + */ + if (tsigkey->inception != tsigkey->expire && + tsigkey->expire <= isc_stdtime_now()) + { + dns_tsigkey_delete(tsigkey); + dns_tsigkey_detach(&tsigkey); + result = ISC_R_NOTFOUND; + } else { + tkeyout.error = dns_tsigerror_badkey; + dns_tsigkey_detach(&tsigkey); + break; + } + } + if (result == ISC_R_NOTFOUND || tsigkey == NULL) { CHECK(process_gsstkey(msg, keyname, &tkeyin, tctx, &tkeyout, ring)); break;