DSes with best digest + INSECURE on unsupportd alg

Adaptations to function ds_authenticates_keys.

With multiple DSes, only the ones with the highest (supported)
digest type will be used to authenticate DNSKEYs.

NO_SUPPORTED_ALGORITHMS will be returned if there were
DSes for a key in the DNSKEY set, but none of them has a supported
digest or algorithm.  This leads to dnssec_status INSECURE.
This commit is contained in:
Willem Toorop 2015-07-08 12:21:04 +02:00
parent a5bacfefcf
commit 2918c8b472
1 changed files with 73 additions and 20 deletions

View File

@ -506,7 +506,6 @@ typedef struct getdns_rrset {
uint16_t rr_type; uint16_t rr_type;
uint8_t *pkt; uint8_t *pkt;
size_t pkt_len; size_t pkt_len;
uint8_t name_spc[1];
} getdns_rrset; } getdns_rrset;
typedef struct rrtype_iter { typedef struct rrtype_iter {
@ -1616,11 +1615,15 @@ static int ds_authenticates_keys(getdns_rrset *ds_set, getdns_rrset *dnskey_set)
rrtype_iter ds_spc, *ds; rrtype_iter ds_spc, *ds;
uint16_t keytag; uint16_t keytag;
uint8_t *nc_name; uint8_t *nc_name;
ldns_rr *dnskey_l; ldns_rr *dnskey_l, *ds_gen_l, *ds_l;
ldns_rr *ds_l = NULL; size_t valid_dsses = 0, supported_dsses = 0;
uint8_t max_supported_digest = 0;
int max_supported_result = 0;
assert(ds_set->rr_type == GETDNS_RRTYPE_DS); assert(ds_set->rr_type == GETDNS_RRTYPE_DS);
assert(dnskey_set->rr_type == GETDNS_RRTYPE_DNSKEY); assert(dnskey_set->rr_type == GETDNS_RRTYPE_DNSKEY);
/* The ds_set is already authenticated! */
if (!_dname_equal(ds_set->name, dnskey_set->name)) if (!_dname_equal(ds_set->name, dnskey_set->name))
return 0; return 0;
@ -1640,45 +1643,95 @@ static int ds_authenticates_keys(getdns_rrset *ds_set, getdns_rrset *dnskey_set)
for ( ds = rrtype_iter_init(&ds_spc, ds_set) for ( ds = rrtype_iter_init(&ds_spc, ds_set)
; ds ; ds = rrtype_iter_next(ds)) { ; ds ; ds = rrtype_iter_next(ds)) {
if (/* Space for keytag & signer in rrsig rdata? */ if (/* Space for keytag, algorithm & digest type? */
ds->rr_i.nxt < ds->rr_i.rr_type + 13 ds->rr_i.nxt < ds->rr_i.rr_type + 14
/* Does algorithm match? */ /* Does algorithm match? */
|| ds->rr_i.rr_type[12] != dnskey->rr_i.rr_type[13] || ds->rr_i.rr_type[12] != dnskey->rr_i.rr_type[13]
/* Does the keytag match? */ /* Does the keytag match? */
|| gldns_read_uint16(ds->rr_i.rr_type+10)!=keytag) || gldns_read_uint16(ds->rr_i.rr_type+10)!=keytag)
continue;
valid_dsses++;
if (/* Algorithm is not RSAMD5 (deprecated) */
ds->rr_i.rr_type[12] == GLDNS_RSAMD5
/* Algorithm is supported */
|| !ldns_key_algo_supported(ds->rr_i.rr_type[12]))
continue; continue;
if (!dnskey_l) if (!dnskey_l)
if (!(dnskey_l = rr2ldns_rr(&dnskey->rr_i))) if (!(dnskey_l = rr2ldns_rr(&dnskey->rr_i)))
continue; continue;
if (!(ds_l = rr2ldns_rr(&ds->rr_i))) /* Unfortunately there is no ldns_ds_digest_supported()
* function. The only way to check if a digest type is
* supported, is by trying to hashing the key with the
* given digest type.
*/
if (!(ds_gen_l = ldns_key_rr2ds(dnskey_l,
ds->rr_i.rr_type[13])))
/* Hash algorithm not supported */
continue; continue;
if (!ldns_rr_compare_ds(ds_l, dnskey_l)) { supported_dsses++;
ldns_rr_free(ds_l);
ds_l = NULL; /* The result of the best digest type counts!
* We'll assume higher is better for now.
* So, continue with next DS if...
*/
if (/* we already had a better digest earlier */
ds->rr_i.rr_type[13] < max_supported_digest
/* or we had the same digest and it already gave
* a match (to a key in dnskey_set which
* authenticated the dnskey_set).
*/
|| ( ds->rr_i.rr_type[13] == max_supported_digest
&& max_supported_result)) {
ldns_rr_free(ds_gen_l);
continue; continue;
} }
ldns_rr_free(dnskey_l); max_supported_digest = ds->rr_i.rr_type[13];
max_supported_result = 0;
if (dnskey_signed_rrset(dnskey, dnskey_set, &nc_name) if (!(ds_l = rr2ldns_rr(&ds->rr_i))) {
&& !nc_name /* No DNSKEY's on wildcards! */) { ldns_rr_free(ds_gen_l);
continue;
}
debug_sec_print_rrset( if (ldns_rr_compare(ds_l, ds_gen_l) != 0) {
"keyset authenticated: ", dnskey_set); /* No match */
return SIGNATURE_VERIFIED | keytag; ldns_rr_free(ds_l);
ldns_rr_free(ds_gen_l);
continue;
}
/* Match! */
ldns_rr_free(ds_l);
ldns_rr_free(ds_gen_l);
if (!dnskey_signed_rrset(dnskey, dnskey_set, &nc_name)
|| nc_name /* No DNSKEY's on wildcards! */) {
debug_sec_print_rrset("keyset did not "
"authenticate: ", dnskey_set);
continue;
} }
debug_sec_print_rrset( debug_sec_print_rrset(
"keyset failed authentication: ", dnskey_set); "keyset authenticated: ", dnskey_set);
max_supported_result = SIGNATURE_VERIFIED | keytag;
return 0;
} }
ldns_rr_free(dnskey_l); ldns_rr_free(dnskey_l);
} }
return 0; if (valid_dsses && !supported_dsses)
return NO_SUPPORTED_ALGORITHMS;
else
return max_supported_result;
} }
static int nsec_covers_name( static int nsec_covers_name(
@ -2553,7 +2606,7 @@ static void append_rrs2val_chain_list(getdns_context *ctxt,
rrsig->rr_i.nxt < rrsig->rr_i.rr_type + 28 rrsig->rr_i.nxt < rrsig->rr_i.rr_type + 28
/* We have a signer and it doesn't match? */ /* We have a signer and it doesn't match? */
|| (signer && || ((signer & 0xFFFF) &&
gldns_read_uint16(rrsig->rr_i.rr_type + 26) gldns_read_uint16(rrsig->rr_i.rr_type + 26)
!= (signer & 0xFFFF)) != (signer & 0xFFFF))