Add function to check if a DNSKEY record is in use

Add a function that checks whether a DNSKEY, CDNSKEY, or CDS record
belongs to a key that is being used for signing.

(cherry picked from commit 3b6e9a5fa7)
This commit is contained in:
Matthijs Mekking 2023-03-03 14:19:14 +01:00 committed by Mark Andrews
parent 0d36d98791
commit 88734ac7cf
2 changed files with 198 additions and 0 deletions

View file

@ -392,6 +392,24 @@ dns_zone_unlock_keyfiles(dns_zone_t *zone);
*\li 'zone' to be a valid zone.
*/
isc_result_t
dns_zone_dnskey_inuse(dns_zone_t *zone, dns_rdata_t *rdata, bool *inuse);
/*%<
* Check if the DNSKEY record 'rdata' is used by 'zone' for zone signing.
* Store the result in 'inuse'.
*
* Require:
*\li 'zone' to be a valid zone.
*\li 'rdata' to represent a DNSKEY, CDNSKEY, or CDS record.
*
* Returns:
*\li #ISC_R_SUCCESS
*\li Any error result from dns_dnssec_keyfromrdata, dns_rdata_tostruct,
* dns_dnssec_make_dnskey, dns_ds_buildrdata, or
* dns_dnssec_findmatchingkeys.
*
*/
isc_result_t
dns_zone_load(dns_zone_t *zone, bool newonly);

View file

@ -16329,6 +16329,186 @@ update_log_cb(void *arg, dns_zone_t *zone, int level, const char *message) {
dns_zone_log(zone, level, "%s", message);
}
static isc_result_t
dnskey_inuse(dns_zone_t *zone, dns_rdata_t *rdata, isc_mem_t *mctx,
dns_dnsseckeylist_t *keylist, bool *inuse) {
isc_result_t result;
dst_key_t *dstkey = NULL;
result = dns_dnssec_keyfromrdata(dns_zone_getorigin(zone), rdata, mctx,
&dstkey);
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_ERROR,
"dns_dnssec_keyfromrdata() failed: %s",
isc_result_totext(result));
return (result);
}
for (dns_dnsseckey_t *k = ISC_LIST_HEAD(*keylist); k != NULL;
k = ISC_LIST_NEXT(k, link))
{
if (dst_key_pubcompare(k->key, dstkey, false)) {
*inuse = true;
break;
}
}
dst_key_free(&dstkey);
return (ISC_R_SUCCESS);
}
static isc_result_t
cdnskey_inuse(dns_zone_t *zone, dns_rdata_t *rdata,
dns_dnsseckeylist_t *keylist, bool *inuse) {
isc_result_t result;
dns_rdata_cdnskey_t cdnskey;
result = dns_rdata_tostruct(rdata, &cdnskey, NULL);
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_ERROR,
"dns_rdata_tostruct(cdnskey) failed: %s",
isc_result_totext(result));
return (result);
}
for (dns_dnsseckey_t *k = ISC_LIST_HEAD(*keylist); k != NULL;
k = ISC_LIST_NEXT(k, link))
{
dns_rdata_t cdnskeyrdata = DNS_RDATA_INIT;
unsigned char keybuf[DST_KEY_MAXSIZE];
result = dns_dnssec_make_dnskey(k->key, keybuf, sizeof(keybuf),
&cdnskeyrdata);
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_ERROR,
"dns_dnssec_make_dnskey() failed: %s",
isc_result_totext(result));
return (result);
}
cdnskeyrdata.type = dns_rdatatype_cdnskey;
if (dns_rdata_compare(rdata, &cdnskeyrdata) == 0) {
*inuse = true;
break;
}
}
return (ISC_R_SUCCESS);
}
static isc_result_t
cds_inuse(dns_zone_t *zone, dns_rdata_t *rdata, dns_dnsseckeylist_t *keylist,
bool *inuse) {
isc_result_t result;
dns_rdata_ds_t cds;
result = dns_rdata_tostruct(rdata, &cds, NULL);
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_ERROR,
"dns_rdata_tostruct(cds) failed: %s",
isc_result_totext(result));
return (result);
}
for (dns_dnsseckey_t *k = ISC_LIST_HEAD(*keylist); k != NULL;
k = ISC_LIST_NEXT(k, link))
{
dns_rdata_t dnskey = DNS_RDATA_INIT;
dns_rdata_t cdsrdata = DNS_RDATA_INIT;
unsigned char keybuf[DST_KEY_MAXSIZE];
unsigned char cdsbuf[DNS_DS_BUFFERSIZE];
if (dst_key_id(k->key) != cds.key_tag ||
dst_key_alg(k->key) != cds.algorithm)
{
continue;
}
result = dns_dnssec_make_dnskey(k->key, keybuf, sizeof(keybuf),
&dnskey);
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_ERROR,
"dns_dnssec_make_dnskey() failed: %s",
isc_result_totext(result));
return (result);
}
result = dns_ds_buildrdata(dns_zone_getorigin(zone), &dnskey,
cds.digest_type, cdsbuf, &cdsrdata);
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_ERROR,
"dns_ds_buildrdata(keytag=%d, algo=%d, "
"digest=%d) failed: %s",
cds.key_tag, cds.algorithm,
cds.digest_type,
isc_result_totext(result));
return (result);
}
cdsrdata.type = dns_rdatatype_cds;
if (dns_rdata_compare(rdata, &cdsrdata) == 0) {
*inuse = true;
break;
}
}
return (ISC_R_SUCCESS);
}
isc_result_t
dns_zone_dnskey_inuse(dns_zone_t *zone, dns_rdata_t *rdata, bool *inuse) {
dns_dnsseckeylist_t keylist;
dns_dnsseckey_t *key = NULL;
isc_result_t result = ISC_R_SUCCESS;
isc_stdtime_t now = 0;
isc_mem_t *mctx;
REQUIRE(DNS_ZONE_VALID(zone));
REQUIRE(dns_rdatatype_iskeymaterial(rdata->type));
mctx = zone->mctx;
isc_stdtime_get(&now);
ISC_LIST_INIT(keylist);
*inuse = false;
dns_zone_lock_keyfiles(zone);
result = dns_dnssec_findmatchingkeys(dns_zone_getorigin(zone),
dns_zone_getkeydirectory(zone),
now, mctx, &keylist);
dns_zone_unlock_keyfiles(zone);
if (result == ISC_R_NOTFOUND) {
return (ISC_R_SUCCESS);
} else if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_ERROR,
"dns_dnssec_findmatchingkeys() failed: %s",
isc_result_totext(result));
return (result);
}
switch (rdata->type) {
case dns_rdatatype_dnskey:
result = dnskey_inuse(zone, rdata, mctx, &keylist, inuse);
break;
case dns_rdatatype_cdnskey:
result = cdnskey_inuse(zone, rdata, &keylist, inuse);
break;
case dns_rdatatype_cds:
result = cds_inuse(zone, rdata, &keylist, inuse);
break;
default:
UNREACHABLE();
break;
}
while (!ISC_LIST_EMPTY(keylist)) {
key = ISC_LIST_HEAD(keylist);
ISC_LIST_UNLINK(keylist, key, link);
dns_dnsseckey_destroy(mctx, &key);
}
return (result);
}
static isc_result_t
sync_secure_journal(dns_zone_t *zone, dns_zone_t *raw, dns_journal_t *journal,
uint32_t start, uint32_t end, dns_difftuple_t **soatuplep,