aboutsummaryrefslogtreecommitdiff
path: root/contrib/unbound/validator/val_sigcrypt.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/unbound/validator/val_sigcrypt.c')
-rw-r--r--contrib/unbound/validator/val_sigcrypt.c289
1 files changed, 188 insertions, 101 deletions
diff --git a/contrib/unbound/validator/val_sigcrypt.c b/contrib/unbound/validator/val_sigcrypt.c
index b15fba3f499d..7c2b9d7e6608 100644
--- a/contrib/unbound/validator/val_sigcrypt.c
+++ b/contrib/unbound/validator/val_sigcrypt.c
@@ -48,6 +48,7 @@
#include "util/data/msgparse.h"
#include "util/data/dname.h"
#include "util/rbtree.h"
+#include "util/rfc_1982.h"
#include "util/module.h"
#include "util/net_help.h"
#include "util/regional.h"
@@ -78,6 +79,9 @@
#include <openssl/engine.h>
#endif
+/** Maximum number of RRSIG validations for an RRset. */
+#define MAX_VALIDATE_RRSIGS 8
+
/** return number of rrs in an rrset */
static size_t
rrset_get_count(struct ub_packed_rrset_key* rrset)
@@ -513,23 +517,113 @@ size_t algo_needs_num_missing(struct algo_needs* n)
int algo_needs_missing(struct algo_needs* n)
{
- int i;
- /* first check if a needed algo was bogus - report that */
- for(i=0; i<ALGO_NEEDS_MAX; i++)
+ int i, miss = -1;
+ /* check if a needed algo was bogus - report that;
+ * check the first missing algo - report that;
+ * or return 0 */
+ for(i=0; i<ALGO_NEEDS_MAX; i++) {
if(n->needs[i] == 2)
return 0;
- /* now check which algo is missing */
- for(i=0; i<ALGO_NEEDS_MAX; i++)
- if(n->needs[i] == 1)
- return i;
+ if(n->needs[i] == 1 && miss == -1)
+ miss = i;
+ }
+ if(miss != -1) return miss;
return 0;
}
+/**
+ * verify rrset, with dnskey rrset, for a specific rrsig in rrset
+ * @param env: module environment, scratch space is used.
+ * @param ve: validator environment, date settings.
+ * @param now: current time for validation (can be overridden).
+ * @param rrset: to be validated.
+ * @param dnskey: DNSKEY rrset, keyset to try.
+ * @param sig_idx: which signature to try to validate.
+ * @param sortree: reused sorted order. Stored in region. Pass NULL at start,
+ * and for a new rrset.
+ * @param reason: if bogus, a string returned, fixed or alloced in scratch.
+ * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
+ * @param section: section of packet where this rrset comes from.
+ * @param qstate: qstate with region.
+ * @param numverified: incremented when the number of RRSIG validations
+ * increases.
+ * @return secure if any key signs *this* signature. bogus if no key signs it,
+ * unchecked on error, or indeterminate if all keys are not supported by
+ * the crypto library (openssl3+ only).
+ */
+static enum sec_status
+dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
+ time_t now, struct ub_packed_rrset_key* rrset,
+ struct ub_packed_rrset_key* dnskey, size_t sig_idx,
+ struct rbtree_type** sortree,
+ char** reason, sldns_ede_code *reason_bogus,
+ sldns_pkt_section section, struct module_qstate* qstate,
+ int* numverified)
+{
+ /* find matching keys and check them */
+ enum sec_status sec = sec_status_bogus;
+ uint16_t tag = rrset_get_sig_keytag(rrset, sig_idx);
+ int algo = rrset_get_sig_algo(rrset, sig_idx);
+ size_t i, num = rrset_get_count(dnskey);
+ size_t numchecked = 0;
+ size_t numindeterminate = 0;
+ int buf_canon = 0;
+ verbose(VERB_ALGO, "verify sig %d %d", (int)tag, algo);
+ if(!dnskey_algo_id_is_supported(algo)) {
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_UNSUPPORTED_DNSKEY_ALG;
+ verbose(VERB_QUERY, "verify sig: unknown algorithm");
+ return sec_status_insecure;
+ }
+
+ for(i=0; i<num; i++) {
+ /* see if key matches keytag and algo */
+ if(algo != dnskey_get_algo(dnskey, i) ||
+ tag != dnskey_calc_keytag(dnskey, i))
+ continue;
+ numchecked ++;
+ (*numverified)++;
+
+ /* see if key verifies */
+ sec = dnskey_verify_rrset_sig(env->scratch,
+ env->scratch_buffer, ve, now, rrset, dnskey, i,
+ sig_idx, sortree, &buf_canon, reason, reason_bogus,
+ section, qstate);
+ if(sec == sec_status_secure)
+ return sec;
+ else if(sec == sec_status_indeterminate)
+ numindeterminate ++;
+ if(*numverified > MAX_VALIDATE_RRSIGS) {
+ *reason = "too many RRSIG validations";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
+ verbose(VERB_ALGO, "verify sig: too many RRSIG validations");
+ return sec_status_bogus;
+ }
+ }
+ if(numchecked == 0) {
+ *reason = "signatures from unknown keys";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSKEY_MISSING;
+ verbose(VERB_QUERY, "verify: could not find appropriate key");
+ return sec_status_bogus;
+ }
+ if(numindeterminate == numchecked) {
+ *reason = "unsupported algorithm by crypto library";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_UNSUPPORTED_DNSKEY_ALG;
+ verbose(VERB_ALGO, "verify sig: unsupported algorithm by "
+ "crypto library");
+ return sec_status_indeterminate;
+ }
+ return sec_status_bogus;
+}
+
enum sec_status
dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
- uint8_t* sigalg, char** reason, sldns_pkt_section section,
- struct module_qstate* qstate)
+ uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus,
+ sldns_pkt_section section, struct module_qstate* qstate, int* verified)
{
enum sec_status sec;
size_t i, num;
@@ -537,12 +631,15 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
/* make sure that for all DNSKEY algorithms there are valid sigs */
struct algo_needs needs;
int alg;
+ *verified = 0;
num = rrset_get_sigcount(rrset);
if(num == 0) {
verbose(VERB_QUERY, "rrset failed to verify due to a lack of "
"signatures");
*reason = "no signatures";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_RRSIGS_MISSING;
return sec_status_bogus;
}
@@ -551,12 +648,15 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
if(algo_needs_num_missing(&needs) == 0) {
verbose(VERB_QUERY, "zone has no known algorithms");
*reason = "zone has no known algorithms";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_UNSUPPORTED_DNSKEY_ALG;
return sec_status_insecure;
}
}
for(i=0; i<num; i++) {
sec = dnskeyset_verify_rrset_sig(env, ve, *env->now, rrset,
- dnskey, i, &sortree, reason, section, qstate);
+ dnskey, i, &sortree, reason, reason_bogus,
+ section, qstate, verified);
/* see which algorithm has been fixed up */
if(sec == sec_status_secure) {
if(!sigalg)
@@ -568,6 +668,13 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
algo_needs_set_bogus(&needs,
(uint8_t)rrset_get_sig_algo(rrset, i));
}
+ if(*verified > MAX_VALIDATE_RRSIGS) {
+ verbose(VERB_QUERY, "rrset failed to verify, too many RRSIG validations");
+ *reason = "too many RRSIG validations";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
+ return sec_status_bogus;
+ }
}
if(sigalg && (alg=algo_needs_missing(&needs)) != 0) {
verbose(VERB_ALGO, "rrset failed to verify: "
@@ -594,24 +701,27 @@ void algo_needs_reason(struct module_env* env, int alg, char** reason, char* s)
*reason = s;
}
-enum sec_status
+enum sec_status
dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
- size_t dnskey_idx, char** reason, sldns_pkt_section section,
- struct module_qstate* qstate)
+ size_t dnskey_idx, char** reason, sldns_ede_code *reason_bogus,
+ sldns_pkt_section section, struct module_qstate* qstate)
{
enum sec_status sec;
- size_t i, num, numchecked = 0;
+ size_t i, num, numchecked = 0, numindeterminate = 0;
rbtree_type* sortree = NULL;
int buf_canon = 0;
uint16_t tag = dnskey_calc_keytag(dnskey, dnskey_idx);
int algo = dnskey_get_algo(dnskey, dnskey_idx);
+ int numverified = 0;
num = rrset_get_sigcount(rrset);
if(num == 0) {
verbose(VERB_QUERY, "rrset failed to verify due to a lack of "
"signatures");
*reason = "no signatures";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_RRSIGS_MISSING;
return sec_status_bogus;
}
for(i=0; i<num; i++) {
@@ -620,58 +730,37 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
tag != rrset_get_sig_keytag(rrset, i))
continue;
buf_canon = 0;
- sec = dnskey_verify_rrset_sig(env->scratch,
+ sec = dnskey_verify_rrset_sig(env->scratch,
env->scratch_buffer, ve, *env->now, rrset,
dnskey, dnskey_idx, i, &sortree, &buf_canon, reason,
- section, qstate);
+ reason_bogus, section, qstate);
if(sec == sec_status_secure)
return sec;
numchecked ++;
+ numverified ++;
+ if(sec == sec_status_indeterminate)
+ numindeterminate ++;
+ if(numverified > MAX_VALIDATE_RRSIGS) {
+ verbose(VERB_QUERY, "rrset failed to verify, too many RRSIG validations");
+ *reason = "too many RRSIG validations";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
+ return sec_status_bogus;
+ }
}
- verbose(VERB_ALGO, "rrset failed to verify: all signatures are bogus");
- if(!numchecked) *reason = "signature missing";
- return sec_status_bogus;
-}
-
-enum sec_status
-dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
- time_t now, struct ub_packed_rrset_key* rrset,
- struct ub_packed_rrset_key* dnskey, size_t sig_idx,
- struct rbtree_type** sortree, char** reason, sldns_pkt_section section,
- struct module_qstate* qstate)
-{
- /* find matching keys and check them */
- enum sec_status sec = sec_status_bogus;
- uint16_t tag = rrset_get_sig_keytag(rrset, sig_idx);
- int algo = rrset_get_sig_algo(rrset, sig_idx);
- size_t i, num = rrset_get_count(dnskey);
- size_t numchecked = 0;
- int buf_canon = 0;
- verbose(VERB_ALGO, "verify sig %d %d", (int)tag, algo);
- if(!dnskey_algo_id_is_supported(algo)) {
- verbose(VERB_QUERY, "verify sig: unknown algorithm");
- return sec_status_insecure;
- }
-
- for(i=0; i<num; i++) {
- /* see if key matches keytag and algo */
- if(algo != dnskey_get_algo(dnskey, i) ||
- tag != dnskey_calc_keytag(dnskey, i))
- continue;
- numchecked ++;
-
- /* see if key verifies */
- sec = dnskey_verify_rrset_sig(env->scratch,
- env->scratch_buffer, ve, now, rrset, dnskey, i,
- sig_idx, sortree, &buf_canon, reason, section, qstate);
- if(sec == sec_status_secure)
- return sec;
- }
- if(numchecked == 0) {
- *reason = "signatures from unknown keys";
- verbose(VERB_QUERY, "verify: could not find appropriate key");
- return sec_status_bogus;
+ if(!numchecked) {
+ *reason = "signature for expected key and algorithm missing";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
+ } else if(numchecked == numindeterminate) {
+ verbose(VERB_ALGO, "rrset failed to verify due to algorithm "
+ "refusal by cryptolib");
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_UNSUPPORTED_DNSKEY_ALG;
+ *reason = "algorithm refused by cryptolib";
+ return sec_status_indeterminate;
}
+ verbose(VERB_ALGO, "rrset failed to verify: all signatures are bogus");
return sec_status_bogus;
}
@@ -1321,48 +1410,10 @@ sigdate_error(const char* str, int32_t expi, int32_t incep, int32_t now)
(unsigned)incep, (unsigned)now);
}
-/** RFC 1982 comparison, uses unsigned integers, and tries to avoid
- * compiler optimization (eg. by avoiding a-b<0 comparisons),
- * this routine matches compare_serial(), for SOA serial number checks */
-static int
-compare_1982(uint32_t a, uint32_t b)
-{
- /* for 32 bit values */
- const uint32_t cutoff = ((uint32_t) 1 << (32 - 1));
-
- if (a == b) {
- return 0;
- } else if ((a < b && b - a < cutoff) || (a > b && a - b > cutoff)) {
- return -1;
- } else {
- return 1;
- }
-}
-
-/** if we know that b is larger than a, return the difference between them,
- * that is the distance between them. in RFC1982 arith */
-static uint32_t
-subtract_1982(uint32_t a, uint32_t b)
-{
- /* for 32 bit values */
- const uint32_t cutoff = ((uint32_t) 1 << (32 - 1));
-
- if(a == b)
- return 0;
- if(a < b && b - a < cutoff) {
- return b-a;
- }
- if(a > b && a - b > cutoff) {
- return ((uint32_t)0xffffffff) - (a-b-1);
- }
- /* wrong case, b smaller than a */
- return 0;
-}
-
/** check rrsig dates */
static int
-check_dates(struct val_env* ve, uint32_t unow,
- uint8_t* expi_p, uint8_t* incep_p, char** reason)
+check_dates(struct val_env* ve, uint32_t unow, uint8_t* expi_p,
+ uint8_t* incep_p, char** reason, sldns_ede_code *reason_bogus)
{
/* read out the dates */
uint32_t expi, incep, now;
@@ -1386,6 +1437,14 @@ check_dates(struct val_env* ve, uint32_t unow,
sigdate_error("verify: inception after expiration, "
"signature bad", expi, incep, now);
*reason = "signature inception after expiration";
+ if(reason_bogus){
+ /* from RFC8914 on Signature Not Yet Valid: The resolver
+ * attempted to perform DNSSEC validation, but no
+ * signatures are presently valid and at least some are
+ * not yet valid. */
+ *reason_bogus = LDNS_EDE_SIGNATURE_NOT_YET_VALID;
+ }
+
return 0;
}
if(compare_1982(incep, now) > 0) {
@@ -1397,6 +1456,8 @@ check_dates(struct val_env* ve, uint32_t unow,
sigdate_error("verify: signature bad, current time is"
" before inception date", expi, incep, now);
*reason = "signature before inception date";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_SIGNATURE_NOT_YET_VALID;
return 0;
}
sigdate_error("verify warning suspicious signature inception "
@@ -1410,6 +1471,8 @@ check_dates(struct val_env* ve, uint32_t unow,
sigdate_error("verify: signature expired", expi,
incep, now);
*reason = "signature expired";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_SIGNATURE_EXPIRED;
return 0;
}
sigdate_error("verify warning suspicious signature expiration "
@@ -1473,7 +1536,8 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf,
struct val_env* ve, time_t now,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
size_t dnskey_idx, size_t sig_idx,
- struct rbtree_type** sortree, int* buf_canon, char** reason,
+ struct rbtree_type** sortree, int* buf_canon,
+ char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate)
{
enum sec_status sec;
@@ -1492,12 +1556,16 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf,
if(siglen < 2+20) {
verbose(VERB_QUERY, "verify: signature too short");
*reason = "signature too short";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
if(!(dnskey_get_flags(dnskey, dnskey_idx) & DNSKEY_BIT_ZSK)) {
verbose(VERB_QUERY, "verify: dnskey without ZSK flag");
*reason = "dnskey without ZSK flag";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_NO_ZONE_KEY_BIT_SET;
return sec_status_bogus;
}
@@ -1505,6 +1573,8 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf,
/* RFC 4034 says DNSKEY PROTOCOL MUST be 3 */
verbose(VERB_QUERY, "verify: dnskey has wrong key protocol");
*reason = "dnskey has wrong protocolnumber";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
@@ -1514,17 +1584,23 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf,
if(!signer_len) {
verbose(VERB_QUERY, "verify: malformed signer name");
*reason = "signer name malformed";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus; /* signer name invalid */
}
if(!dname_subdomain_c(rrset->rk.dname, signer)) {
verbose(VERB_QUERY, "verify: signer name is off-tree");
*reason = "signer name off-tree";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus; /* signer name offtree */
}
sigblock = (unsigned char*)signer+signer_len;
if(siglen < 2+18+signer_len+1) {
verbose(VERB_QUERY, "verify: too short, no signature data");
*reason = "signature too short, no signature data";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus; /* sig rdf is < 1 byte */
}
sigblock_len = (unsigned int)(siglen - 2 - 18 - signer_len);
@@ -1537,6 +1613,8 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf,
log_nametypeclass(VERB_QUERY, "the key name is",
dnskey->rk.dname, 0, 0);
*reason = "signer name mismatches key name";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
@@ -1545,18 +1623,24 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf,
if(memcmp(sig+2, &rrset->rk.type, 2) != 0) {
verbose(VERB_QUERY, "verify: wrong type covered");
*reason = "signature covers wrong type";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
/* verify keytag and sig algo (possibly again) */
if((int)sig[2+2] != dnskey_get_algo(dnskey, dnskey_idx)) {
verbose(VERB_QUERY, "verify: wrong algorithm");
*reason = "signature has wrong algorithm";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
ktag = htons(dnskey_calc_keytag(dnskey, dnskey_idx));
if(memcmp(sig+2+16, &ktag, 2) != 0) {
verbose(VERB_QUERY, "verify: wrong keytag");
*reason = "signature has wrong keytag";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
@@ -1564,6 +1648,8 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf,
if((int)sig[2+3] > dname_signame_label_count(rrset->rk.dname)) {
verbose(VERB_QUERY, "verify: labelcount out of range");
*reason = "signature labelcount out of range";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
@@ -1598,7 +1684,8 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf,
/* verify inception, expiration dates
* Do this last so that if you ignore expired-sigs the
* rest is sure to be OK. */
- if(!check_dates(ve, now, sig+2+8, sig+2+12, reason)) {
+ if(!check_dates(ve, now, sig+2+8, sig+2+12,
+ reason, reason_bogus)) {
return sec_status_bogus;
}
}