Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(RHEL-26643) resolved: limit the number of signature validations in a transaction #237

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions src/resolve/resolved-dns-dnssec.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EC_KEY*, EC_KEY_free, NULL);
/* 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. RFC5155 says 2500 shall be the maximum useful value */
#define NSEC3_ITERATIONS_MAX 2500
/* Maximum number of NSEC3 iterations we'll do. RFC5155 says 2500 shall be the maximum useful value, but
* RFC9276 § 3.2 says that we should reduce the acceptable iteration count */
#define NSEC3_ITERATIONS_MAX 100

/*
* The DNSSEC Chain of trust:
Expand Down Expand Up @@ -1176,6 +1177,7 @@ int dnssec_verify_rrset_search(
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;

Expand Down Expand Up @@ -1221,6 +1223,14 @@ int dnssec_verify_rrset_search(
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
Expand All @@ -1230,6 +1240,8 @@ int dnssec_verify_rrset_search(
if (r < 0)
return r;

nvalidations++;

switch (one_result) {

case DNSSEC_VALIDATED:
Expand All @@ -1240,7 +1252,7 @@ int dnssec_verify_rrset_search(
*ret_rrsig = rrsig;

*result = one_result;
return 0;
return (int) nvalidations;

case DNSSEC_INVALID:
/* If the signature is invalid, let's try another
Expand Down Expand Up @@ -1287,7 +1299,7 @@ int dnssec_verify_rrset_search(
if (ret_rrsig)
*ret_rrsig = NULL;

return 0;
return (int) nvalidations;
}

int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
Expand Down Expand Up @@ -2571,6 +2583,7 @@ static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
[DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
[DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
[DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
[DNSSEC_TOO_MANY_VALIDATIONS] = "too-many-validations",
};
DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);

Expand Down
9 changes: 8 additions & 1 deletion src/resolve/resolved-dns-dnssec.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ typedef enum DnssecVerdict DnssecVerdict;
#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,
Expand Down Expand Up @@ -45,6 +46,12 @@ enum DnssecVerdict {
/* 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);

Expand Down
19 changes: 16 additions & 3 deletions src/resolve/resolved-dns-transaction.c
Original file line number Diff line number Diff line change
Expand Up @@ -3140,11 +3140,14 @@ static int dnssec_validate_records(
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) {
Expand Down Expand Up @@ -3186,6 +3189,7 @@ static int dnssec_validate_records(
&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));

Expand Down Expand Up @@ -3383,7 +3387,8 @@ static int dnssec_validate_records(
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.
Expand Down Expand Up @@ -3476,13 +3481,21 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
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;
Expand Down
Loading