DnsResourceRecord **ret_rrsig) {
bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
+ unsigned nvalidations = 0;
DnsResourceRecord *rrsig;
int r;
if (realtime == USEC_INFINITY)
realtime = now(CLOCK_REALTIME);
+ /* Have we seen an unreasonable number of invalid signaures? */
+ if (nvalidations > DNSSEC_INVALID_MAX) {
+ if (ret_rrsig)
+ *ret_rrsig = NULL;
+ *result = DNSSEC_TOO_MANY_VALIDATIONS;
+ return (int) nvalidations;
+ }
+
/* Yay, we found a matching RRSIG with a matching
* DNSKEY, awesome. Now let's verify all entries of
* the RRSet against the RRSIG and DNSKEY
if (r < 0)
return r;
+ nvalidations++;
+
switch (one_result) {
case DNSSEC_VALIDATED:
*ret_rrsig = rrsig;
*result = one_result;
- return 0;
+ return (int) nvalidations;
case DNSSEC_INVALID:
/* If the signature is invalid, let's try another
if (ret_rrsig)
*ret_rrsig = NULL;
- return 0;
+ return (int) nvalidations;
}
int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
[DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
[DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
[DNSSEC_UPSTREAM_FAILURE] = "upstream-failure",
+ [DNSSEC_TOO_MANY_VALIDATIONS] = "too-many-validations",
};
DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);
#include "resolved-dns-rr.h"
enum DnssecResult {
- /* These five are returned by dnssec_verify_rrset() */
+ /* These six are returned by dnssec_verify_rrset() */
DNSSEC_VALIDATED,
DNSSEC_VALIDATED_WILDCARD, /* Validated via a wildcard RRSIG, further NSEC/NSEC3 checks necessary */
DNSSEC_INVALID,
DNSSEC_SIGNATURE_EXPIRED,
DNSSEC_UNSUPPORTED_ALGORITHM,
+ DNSSEC_TOO_MANY_VALIDATIONS,
/* These two are added by dnssec_verify_rrset_search() */
DNSSEC_NO_SIGNATURE,
/* The longest digest we'll ever generate, of all digest algorithms we support */
#define DNSSEC_HASH_SIZE_MAX (MAX(20, 32))
+/* The most invalid signatures we will tolerate for a single rrset */
+#define DNSSEC_INVALID_MAX 5
+
+/* The total number of signature validations we will tolerate for a single transaction */
+#define DNSSEC_VALIDATION_MAX 64
+
int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok);
int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig);
DnsTransaction *t,
Phase phase,
bool *have_nsec,
+ unsigned *nvalidations,
DnsAnswer **validated) {
DnsResourceRecord *rr;
int r;
+ assert(nvalidations);
+
/* Returns negative on error, 0 if validation failed, 1 to restart validation, 2 when finished. */
DNS_ANSWER_FOREACH(rr, t->answer) {
&rrsig);
if (r < 0)
return r;
+ *nvalidations += r;
log_debug("Looking at %s: %s", strna(dns_resource_record_to_string(rr)), dnssec_result_to_string(result));
DNSSEC_SIGNATURE_EXPIRED,
DNSSEC_NO_SIGNATURE))
manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, rr->key);
- else /* DNSSEC_MISSING_KEY or DNSSEC_UNSUPPORTED_ALGORITHM */
+ else /* DNSSEC_MISSING_KEY, DNSSEC_UNSUPPORTED_ALGORITHM,
+ or DNSSEC_TOO_MANY_VALIDATIONS */
manager_dnssec_verdict(t->scope->manager, DNSSEC_INDETERMINATE, rr->key);
/* This is a primary response to our question, and it failed validation.
return r;
phase = DNSSEC_PHASE_DNSKEY;
- for (;;) {
+ for (unsigned nvalidations = 0;;) {
bool have_nsec = false;
- r = dnssec_validate_records(t, phase, &have_nsec, &validated);
+ r = dnssec_validate_records(t, phase, &have_nsec, &nvalidations, &validated);
if (r <= 0)
return r;
+ if (nvalidations > DNSSEC_VALIDATION_MAX) {
+ /* This reply requires an onerous number of signature validations to verify. Let's
+ * not waste our time trying, as this shouldn't happen for well-behaved domains
+ * anyway. */
+ t->answer_dnssec_result = DNSSEC_TOO_MANY_VALIDATIONS;
+ return 0;
+ }
+
/* Try again as long as we managed to achieve something */
if (r == 1)
continue;