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.
This commit is contained in:
Ondřej Surý 2026-03-18 00:28:04 +01:00
parent 443b73b565
commit 5e10fdc295
No known key found for this signature in database
GPG key ID: 2820F37E873DEA41

View file

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