diff options
Diffstat (limited to 'src/resolve/resolved-dns-dnssec.c')
-rw-r--r-- | src/resolve/resolved-dns-dnssec.c | 341 |
1 files changed, 258 insertions, 83 deletions
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index a3aa90e98d..32d4834aa1 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -35,14 +35,11 @@ * * TODO: * - * - Make trust anchor store read additional DS+DNSKEY data from disk * - wildcard zones compatibility (NSEC/NSEC3 wildcard check is missing) * - multi-label zone compatibility * - cname/dname compatibility - * - per-interface DNSSEC setting * - nxdomain on qname - * - retry on failed validation? - * - DSA support? + * - per-interface DNSSEC setting * * */ @@ -52,6 +49,9 @@ /* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */ #define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE) +/* Maximum number of NSEC3 iterations we'll do. */ +#define NSEC3_ITERATIONS_MAX 2048 + /* * The DNSSEC Chain of trust: * @@ -76,9 +76,9 @@ static void initialize_libgcrypt(void) { gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); } -uint16_t dnssec_keytag(DnsResourceRecord *dnskey) { +uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) { const uint8_t *p; - uint32_t sum; + uint32_t sum, f; size_t i; /* The algorithm from RFC 4034, Appendix B. */ @@ -86,8 +86,12 @@ uint16_t dnssec_keytag(DnsResourceRecord *dnskey) { assert(dnskey); assert(dnskey->key->type == DNS_TYPE_DNSKEY); - sum = (uint32_t) dnskey->dnskey.flags + - ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm); + f = (uint32_t) dnskey->dnskey.flags; + + if (mask_revoke) + f &= ~DNSKEY_FLAG_REVOKE; + + sum = f + ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm); p = dnskey->dnskey.key; @@ -113,15 +117,15 @@ static int rr_compare(const void *a, const void *b) { assert(*y); assert((*y)->wire_format); - m = MIN((*x)->wire_format_size, (*y)->wire_format_size); + m = MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(*x), DNS_RESOURCE_RECORD_RDATA_SIZE(*y)); - r = memcmp((*x)->wire_format, (*y)->wire_format, m); + r = memcmp(DNS_RESOURCE_RECORD_RDATA(*x), DNS_RESOURCE_RECORD_RDATA(*y), m); if (r != 0) return r; - if ((*x)->wire_format_size < (*y)->wire_format_size) + if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) < DNS_RESOURCE_RECORD_RDATA_SIZE(*y)) return -1; - else if ((*x)->wire_format_size > (*y)->wire_format_size) + else if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) > DNS_RESOURCE_RECORD_RDATA_SIZE(*y)) return 1; return 0; @@ -237,8 +241,8 @@ static int dnssec_rsa_verify( exponent = (uint8_t*) dnskey->dnskey.key + 3; exponent_size = - ((size_t) (((uint8_t*) dnskey->dnskey.key)[0]) << 8) | - ((size_t) ((uint8_t*) dnskey->dnskey.key)[1]); + ((size_t) (((uint8_t*) dnskey->dnskey.key)[1]) << 8) | + ((size_t) ((uint8_t*) dnskey->dnskey.key)[2]); if (exponent_size < 256) return -EINVAL; @@ -458,7 +462,15 @@ static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) { static int algorithm_to_gcrypt_md(uint8_t algorithm) { - /* Translates a DNSSEC signature algorithm into a gcrypt digest identifier */ + /* Translates a DNSSEC signature algorithm into a gcrypt + * digest identifier. + * + * Note that we implement all algorithms listed as "Must + * implement" and "Recommended to Implement" in RFC6944. We + * don't implement any algorithms that are listed as + * "Optional" or "Must Not Implement". Specifically, we do not + * implement RSAMD5, DSASHA1, DH, DSA-NSEC3-SHA1, and + * GOST-ECC. */ switch (algorithm) { @@ -483,7 +495,7 @@ static int algorithm_to_gcrypt_md(uint8_t algorithm) { int dnssec_verify_rrset( DnsAnswer *a, - DnsResourceKey *key, + const DnsResourceKey *key, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, usec_t realtime, @@ -516,9 +528,6 @@ int dnssec_verify_rrset( if (md_algorithm < 0) return md_algorithm; - if (a->n_rrs > VERIFY_RRS_MAX) - return -E2BIG; - r = dnssec_rrsig_expired(rrsig, realtime); if (r < 0) return r; @@ -543,6 +552,9 @@ int dnssec_verify_rrset( return r; list[n++] = rr; + + if (n > VERIFY_RRS_MAX) + return -E2BIG; } if (n <= 0) @@ -594,12 +606,11 @@ int dnssec_verify_rrset( md_add_uint16(md, rr->key->class); md_add_uint32(md, rrsig->rrsig.original_ttl); - assert(rr->wire_format_rdata_offset <= rr->wire_format_size); - l = rr->wire_format_size - rr->wire_format_rdata_offset; + l = DNS_RESOURCE_RECORD_RDATA_SIZE(rr); assert(l <= 0xFFFF); md_add_uint16(md, (uint16_t) l); - gcry_md_write(md, (uint8_t*) rr->wire_format + rr->wire_format_rdata_offset, l); + gcry_md_write(md, DNS_RESOURCE_RECORD_RDATA(rr), l); } hash = gcry_md_read(md, 0); @@ -643,7 +654,7 @@ finish: return r; } -int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) { +int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) { assert(rrsig); assert(dnskey); @@ -660,12 +671,14 @@ int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnske return 0; if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0) return 0; + if (!revoked_ok && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE)) + return 0; if (dnskey->dnskey.protocol != 3) return 0; if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm) return 0; - if (dnssec_keytag(dnskey) != rrsig->rrsig.key_tag) + if (dnssec_keytag(dnskey, false) != rrsig->rrsig.key_tag) return 0; return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer); @@ -727,7 +740,7 @@ static int dnssec_fix_rrset_ttl(DnsAnswer *a, const DnsResourceKey *key, DnsReso int dnssec_verify_rrset_search( DnsAnswer *a, - DnsResourceKey *key, + const DnsResourceKey *key, DnsAnswer *validated_dnskeys, usec_t realtime, DnssecResult *result) { @@ -766,7 +779,7 @@ int dnssec_verify_rrset_search( continue; /* Is this a DNSKEY RR that matches they key of our RRSIG? */ - r = dnssec_rrsig_match_dnskey(rrsig, dnskey); + r = dnssec_rrsig_match_dnskey(rrsig, dnskey, false); if (r < 0) return r; if (r == 0) @@ -946,7 +959,7 @@ static int digest_to_gcrypt_md(uint8_t algorithm) { } } -int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) { +int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) { char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX]; gcry_md_hd_t md = NULL; size_t hash_size; @@ -964,12 +977,14 @@ int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) { return -EINVAL; if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0) return -EKEYREJECTED; + if (!mask_revoke && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE)) + return -EKEYREJECTED; if (dnskey->dnskey.protocol != 3) return -EKEYREJECTED; if (dnskey->dnskey.algorithm != ds->ds.algorithm) return 0; - if (dnssec_keytag(dnskey) != ds->ds.key_tag) + if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag) return 0; initialize_libgcrypt(); @@ -993,7 +1008,10 @@ int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) { return -EIO; gcry_md_write(md, owner_name, r); - md_add_uint16(md, dnskey->dnskey.flags); + if (mask_revoke) + md_add_uint16(md, dnskey->dnskey.flags & ~DNSKEY_FLAG_REVOKE); + else + md_add_uint16(md, dnskey->dnskey.flags); md_add_uint8(md, dnskey->dnskey.protocol); md_add_uint8(md, dnskey->dnskey.algorithm); gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size); @@ -1038,7 +1056,9 @@ int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ if (r == 0) continue; - r = dnssec_verify_dnskey(dnskey, ds); + r = dnssec_verify_dnskey(dnskey, ds, false); + if (r == -EKEYREJECTED) + return 0; /* The DNSKEY is revoked or otherwise invalid, we won't bless it */ if (r < 0) return r; if (r > 0) @@ -1048,6 +1068,20 @@ int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ return 0; } +static int nsec3_hash_to_gcrypt_md(uint8_t algorithm) { + + /* Translates a DNSSEC NSEC3 hash algorithm into a gcrypt digest identifier */ + + switch (algorithm) { + + case NSEC3_ALGORITHM_SHA1: + return GCRY_MD_SHA1; + + default: + return -EOPNOTSUPP; + } +} + int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) { uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX]; gcry_md_hd_t md = NULL; @@ -1064,7 +1098,12 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) { if (nsec3->key->type != DNS_TYPE_NSEC3) return -EINVAL; - algorithm = digest_to_gcrypt_md(nsec3->nsec3.algorithm); + if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX) { + log_debug("Ignoring NSEC3 RR %s with excessive number of iterations.", dns_resource_record_to_string(nsec3)); + return -EOPNOTSUPP; + } + + algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm); if (algorithm < 0) return algorithm; @@ -1129,6 +1168,13 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsAnswerFlags flags, DnsResourc if (!IN_SET(rr->nsec3.flags, 0, 1)) return 0; + /* Ignore NSEC3 RRs whose algorithm we don't know */ + if (nsec3_hash_to_gcrypt_md(rr->nsec3.algorithm) < 0) + return 0; + /* Ignore NSEC3 RRs with an excessive number of required iterations */ + if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX) + return 0; + if (!nsec3) return 1; @@ -1165,21 +1211,61 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsAnswerFlags flags, DnsResourc return dns_name_equal(a, b); } -static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated) { - _cleanup_free_ char *next_closer_domain = NULL, *l = NULL; +static int nsec3_hashed_domain(DnsResourceRecord *nsec3, const char *domain, const char *zone, char **ret) { + _cleanup_free_ char *l = NULL, *hashed_domain = NULL; uint8_t hashed[DNSSEC_HASH_SIZE_MAX]; - const char *suffix, *p, *pp = NULL; - DnsResourceRecord *rr, *suffix_rr; + int hashed_size; + + assert(nsec3); + assert(domain); + assert(zone); + assert(ret); + + hashed_size = dnssec_nsec3_hash(nsec3, domain, hashed); + if (hashed_size < 0) + return hashed_size; + + l = base32hexmem(hashed, hashed_size, false); + if (!l) + return -ENOMEM; + + hashed_domain = strjoin(l, ".", zone, NULL); + if (!hashed_domain) + return -ENOMEM; + + *ret = hashed_domain; + hashed_domain = NULL; + + return hashed_size; +} + +/* See RFC 5155, Section 8 + * First try to find a NSEC3 record that matches our query precisely, if that fails, find the closest + * enclosure. Secondly, find a proof that there is no closer enclosure and either a proof that there + * is no wildcard domain as a direct descendant of the closest enclosure, or find an NSEC3 record that + * matches the wildcard domain. + * + * Based on this we can prove either the existence of the record in @key, or NXDOMAIN or NODATA, or + * that there is no proof either way. The latter is the case if a the proof of non-existence of a given + * name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records + * to conclude anything we indicate this by returning NO_RR. */ +static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) { + _cleanup_free_ char *next_closer_domain = NULL, *wildcard = NULL, *wildcard_domain = NULL; + const char *zone, *p, *pp = NULL; + DnsResourceRecord *rr, *enclosure_rr, *suffix_rr, *wildcard_rr = NULL; DnsAnswerFlags flags; int hashed_size, r; - bool a; + bool a, no_closer = false, no_wildcard = false, optout = false; assert(key); assert(result); - assert(authenticated); - /* First step, look for the longest common suffix we find with any NSEC3 RR in the response. */ - suffix = DNS_RESOURCE_KEY_NAME(key); + /* First step, find the zone name and the NSEC3 parameters of the zone. + * it is sufficient to look for the longest common suffix we find with + * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3 + * records from a given zone in a response must use the same + * parameters. */ + zone = DNS_RESOURCE_KEY_NAME(key); for (;;) { DNS_ANSWER_FOREACH_FLAGS(suffix_rr, flags, answer) { r = nsec3_is_good(suffix_rr, flags, NULL); @@ -1188,15 +1274,15 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR if (r == 0) continue; - r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(suffix_rr->key), 1, suffix); + r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(suffix_rr->key), 1, zone); if (r < 0) return r; if (r > 0) - goto found_suffix; + goto found_zone; } /* Strip one label from the front */ - r = dns_name_parent(&suffix); + r = dns_name_parent(&zone); if (r < 0) return r; if (r == 0) @@ -1206,13 +1292,13 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR *result = DNSSEC_NSEC_NO_RR; return 0; -found_suffix: +found_zone: /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */ p = DNS_RESOURCE_KEY_NAME(key); for (;;) { - _cleanup_free_ char *hashed_domain = NULL, *label = NULL; + _cleanup_free_ char *hashed_domain = NULL; - hashed_size = dnssec_nsec3_hash(suffix_rr, p, hashed); + hashed_size = nsec3_hashed_domain(suffix_rr, p, zone, &hashed_domain); if (hashed_size == -EOPNOTSUPP) { *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM; return 0; @@ -1220,26 +1306,18 @@ found_suffix: if (hashed_size < 0) return hashed_size; - label = base32hexmem(hashed, hashed_size, false); - if (!label) - return -ENOMEM; - - hashed_domain = strjoin(label, ".", suffix, NULL); - if (!hashed_domain) - return -ENOMEM; - - DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) { + DNS_ANSWER_FOREACH_FLAGS(enclosure_rr, flags, answer) { - r = nsec3_is_good(rr, flags, suffix_rr); + r = nsec3_is_good(enclosure_rr, flags, suffix_rr); if (r < 0) return r; if (r == 0) continue; - if (rr->nsec3.next_hashed_name_size != (size_t) hashed_size) + if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size) continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain); + r = dns_name_equal(DNS_RESOURCE_KEY_NAME(enclosure_rr->key), hashed_domain); if (r < 0) return r; if (r > 0) { @@ -1269,37 +1347,51 @@ found_closest_encloser: /* We found a closest encloser in 'p'; next closer is 'pp' */ /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */ - if (bitmap_isset(rr->nsec3.types, DNS_TYPE_DNAME)) + if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME)) return -EBADMSG; /* Ensure that this data is from the delegated domain * (i.e. originates from the "lower" DNS server), and isn't * just glue records (i.e. doesn't originate from the "upper" * DNS server). */ - if (bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) && - !bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA)) + if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) && + !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA)) return -EBADMSG; if (!pp) { /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */ - *result = bitmap_isset(rr->nsec3.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA; - *authenticated = a; + if (bitmap_isset(enclosure_rr->nsec3.types, key->type)) + *result = DNSSEC_NSEC_FOUND; + else if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_CNAME)) + *result = DNSSEC_NSEC_CNAME; + else + *result = DNSSEC_NSEC_NODATA; + + if (authenticated) + *authenticated = a; + if (ttl) + *ttl = enclosure_rr->ttl; + return 0; } - r = dnssec_nsec3_hash(rr, pp, hashed); + /* Prove that there is no next closer and whether or not there is a wildcard domain. */ + + wildcard = strappend("*.", p); + if (!wildcard) + return -ENOMEM; + + r = nsec3_hashed_domain(enclosure_rr, wildcard, zone, &wildcard_domain); if (r < 0) return r; if (r != hashed_size) return -EBADMSG; - l = base32hexmem(hashed, hashed_size, false); - if (!l) - return -ENOMEM; - - next_closer_domain = strjoin(l, ".", p, NULL); - if (!next_closer_domain) - return -ENOMEM; + r = nsec3_hashed_domain(enclosure_rr, pp, zone, &next_closer_domain); + if (r < 0) + return r; + if (r != hashed_size) + return -EBADMSG; DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) { _cleanup_free_ char *label = NULL, *next_hashed_domain = NULL; @@ -1314,7 +1406,7 @@ found_closest_encloser: if (!label) return -ENOMEM; - next_hashed_domain = strjoin(label, ".", p, NULL); + next_hashed_domain = strjoin(label, ".", zone, NULL); if (!next_hashed_domain) return -ENOMEM; @@ -1323,20 +1415,89 @@ found_closest_encloser: return r; if (r > 0) { if (rr->nsec3.flags & 1) - *result = DNSSEC_NSEC_OPTOUT; - else - *result = DNSSEC_NSEC_NXDOMAIN; + optout = true; - *authenticated = a && (flags & DNS_ANSWER_AUTHENTICATED); - return 1; + a = a && (flags & DNS_ANSWER_AUTHENTICATED); + + no_closer = true; + } + + r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain); + if (r < 0) + return r; + if (r > 0) { + a = a && (flags & DNS_ANSWER_AUTHENTICATED); + + wildcard_rr = rr; + } + + r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain, next_hashed_domain); + if (r < 0) + return r; + if (r > 0) { + if (rr->nsec3.flags & 1) + /* This only makes sense if we have a wildcard delegation, which is + * very unlikely, see RFC 4592, Section 4.2, but we cannot rely on + * this not happening, so hence cannot simply conclude NXDOMAIN as + * we would wish */ + optout = true; + + a = a && (flags & DNS_ANSWER_AUTHENTICATED); + + no_wildcard = true; } } - *result = DNSSEC_NSEC_NO_RR; + if (wildcard_rr && no_wildcard) + return -EBADMSG; + + if (!no_closer) { + *result = DNSSEC_NSEC_NO_RR; + return 0; + } + + if (wildcard_rr) { + /* A wildcard exists that matches our query. */ + if (optout) + /* This is not specified in any RFC to the best of my knowledge, but + * if the next closer enclosure is covered by an opt-out NSEC3 RR + * it means that we cannot prove that the source of synthesis is + * correct, as there may be a closer match. */ + *result = DNSSEC_NSEC_OPTOUT; + else if (bitmap_isset(wildcard_rr->nsec3.types, key->type)) + *result = DNSSEC_NSEC_FOUND; + else if (bitmap_isset(wildcard_rr->nsec3.types, DNS_TYPE_CNAME)) + *result = DNSSEC_NSEC_CNAME; + else + *result = DNSSEC_NSEC_NODATA; + } else { + if (optout) + /* The RFC only specifies that we have to care for optout for NODATA for + * DS records. However, children of an insecure opt-out delegation should + * also be considered opt-out, rather than verified NXDOMAIN. + * Note that we do not require a proof of wildcard non-existence if the + * next closer domain is covered by an opt-out, as that would not provide + * any additional information. */ + *result = DNSSEC_NSEC_OPTOUT; + else if (no_wildcard) + *result = DNSSEC_NSEC_NXDOMAIN; + else { + *result = DNSSEC_NSEC_NO_RR; + + return 0; + } + } + + if (authenticated) + *authenticated = a; + + if (ttl) + *ttl = enclosure_rr->ttl; + return 0; } -int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated) { +int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) { DnsResourceRecord *rr; bool have_nsec3 = false; DnsAnswerFlags flags; @@ -1344,7 +1505,6 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r assert(key); assert(result); - assert(authenticated); /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */ @@ -1361,8 +1521,18 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r if (r < 0) return r; if (r > 0) { - *result = bitmap_isset(rr->nsec.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA; - *authenticated = flags & DNS_ANSWER_AUTHENTICATED; + if (bitmap_isset(rr->nsec.types, key->type)) + *result = DNSSEC_NSEC_FOUND; + else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME)) + *result = DNSSEC_NSEC_CNAME; + else + *result = DNSSEC_NSEC_NODATA; + + if (authenticated) + *authenticated = flags & DNS_ANSWER_AUTHENTICATED; + if (ttl) + *ttl = rr->ttl; + return 0; } @@ -1371,7 +1541,12 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r return r; if (r > 0) { *result = DNSSEC_NSEC_NXDOMAIN; - *authenticated = flags & DNS_ANSWER_AUTHENTICATED; + + if (authenticated) + *authenticated = flags & DNS_ANSWER_AUTHENTICATED; + if (ttl) + *ttl = rr->ttl; + return 0; } break; @@ -1384,7 +1559,7 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r /* OK, this was not sufficient. Let's see if NSEC3 can help. */ if (have_nsec3) - return dnssec_test_nsec3(answer, key, result, authenticated); + return dnssec_test_nsec3(answer, key, result, authenticated, ttl); /* No approproate NSEC RR found, report this. */ *result = DNSSEC_NSEC_NO_RR; |