diff options
Diffstat (limited to 'src/resolve')
| -rw-r--r-- | src/resolve/resolved-dns-answer.c | 1 | ||||
| -rw-r--r-- | src/resolve/resolved-dns-dnssec.c | 263 | ||||
| -rw-r--r-- | src/resolve/resolved-dns-dnssec.h | 15 | ||||
| -rw-r--r-- | src/resolve/resolved-dns-packet.c | 39 | ||||
| -rw-r--r-- | src/resolve/resolved-dns-packet.h | 4 | ||||
| -rw-r--r-- | src/resolve/resolved-dns-query.c | 11 | ||||
| -rw-r--r-- | src/resolve/resolved-dns-transaction.c | 58 | ||||
| -rw-r--r-- | src/resolve/test-dnssec.c | 132 | 
8 files changed, 468 insertions, 55 deletions
| diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index 6a37345e7e..5355303bd3 100644 --- a/src/resolve/resolved-dns-answer.c +++ b/src/resolve/resolved-dns-answer.c @@ -22,6 +22,7 @@  #include "alloc-util.h"  #include "dns-domain.h"  #include "resolved-dns-answer.h" +#include "resolved-dns-dnssec.h"  #include "string-util.h"  DnsAnswer *dns_answer_new(unsigned n) { diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 792c9d8692..ed126505ad 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -23,6 +23,7 @@  #include "alloc-util.h"  #include "dns-domain.h" +#include "hexdecoct.h"  #include "resolved-dns-dnssec.h"  #include "resolved-dns-packet.h"  #include "string-table.h" @@ -42,6 +43,8 @@   *   - multi-label zone compatibility   *   - DNSSEC cname/dname compatibility   *   - per-interface DNSSEC setting + *   - retry on failed validation + *   - fix TTL for cache entries to match RRSIG TTL   *   - DSA support   *   - EC support?   * @@ -64,6 +67,19 @@   *            Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS   */ +static void initialize_libgcrypt(void) { +        const char *p; + +        if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) +                return; + +        p = gcry_check_version("1.4.5"); +        assert(p); + +        gcry_control(GCRYCTL_DISABLE_SECMEM); +        gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); +} +  static bool dnssec_algorithm_supported(int algorithm) {          return IN_SET(algorithm,                        DNSSEC_ALGORITHM_RSASHA1, @@ -72,12 +88,6 @@ static bool dnssec_algorithm_supported(int algorithm) {                        DNSSEC_ALGORITHM_RSASHA512);  } -static bool dnssec_digest_supported(int digest) { -        return IN_SET(digest, -                      DNSSEC_DIGEST_SHA1, -                      DNSSEC_DIGEST_SHA256); -} -  uint16_t dnssec_keytag(DnsResourceRecord *dnskey) {          const uint8_t *p;          uint32_t sum; @@ -335,6 +345,8 @@ int dnssec_verify_rrset(          /* Bring the RRs into canonical order */          qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare); +        initialize_libgcrypt(); +          /* OK, the RRs are now in canonical order. Let's calculate the digest */          switch (rrsig->rrsig.algorithm) { @@ -679,9 +691,28 @@ int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {          return (int) c;  } +static int digest_to_gcrypt(uint8_t algorithm) { + +        /* Translates a DNSSEC digest algorithm into a gcrypt digest iedntifier */ + +        switch (algorithm) { + +        case DNSSEC_DIGEST_SHA1: +                return GCRY_MD_SHA1; + +        case DNSSEC_DIGEST_SHA256: +                return GCRY_MD_SHA256; + +        default: +                return -EOPNOTSUPP; +        } +} +  int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) { -        gcry_md_hd_t md = NULL;          char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX]; +        gcry_md_hd_t md = NULL; +        size_t hash_size; +        int algorithm;          void *result;          int r; @@ -704,38 +735,26 @@ int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) {          if (dnssec_keytag(dnskey) != ds->ds.key_tag)                  return 0; -        if (!dnssec_digest_supported(ds->ds.digest_type)) -                return -EOPNOTSUPP; - -        switch (ds->ds.digest_type) { - -        case DNSSEC_DIGEST_SHA1: - -                if (ds->ds.digest_size != 20) -                        return 0; +        initialize_libgcrypt(); -                gcry_md_open(&md, GCRY_MD_SHA1, 0); -                break; +        algorithm = digest_to_gcrypt(ds->ds.digest_type); +        if (algorithm < 0) +                return algorithm; -        case DNSSEC_DIGEST_SHA256: +        hash_size = gcry_md_get_algo_dlen(algorithm); +        assert(hash_size > 0); -                if (ds->ds.digest_size != 32) -                        return 0; - -                gcry_md_open(&md, GCRY_MD_SHA256, 0); -                break; +        if (ds->ds.digest_size != hash_size) +                return 0; -        default: -                assert_not_reached("Unknown digest"); -        } +        r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name)); +        if (r < 0) +                return r; +        gcry_md_open(&md, algorithm, 0);          if (!md)                  return -EIO; -        r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name)); -        if (r < 0) -                goto finish; -          gcry_md_write(md, owner_name, r);          md_add_uint16(md, dnskey->dnskey.flags);          md_add_uint8(md, dnskey->dnskey.protocol); @@ -779,6 +798,187 @@ int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_          return 0;  } +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; +        size_t hash_size; +        int algorithm; +        void *result; +        unsigned k; +        int r; + +        assert(nsec3); +        assert(name); +        assert(ret); + +        if (nsec3->key->type != DNS_TYPE_NSEC3) +                return -EINVAL; + +        algorithm = digest_to_gcrypt(nsec3->nsec3.algorithm); +        if (algorithm < 0) +                return algorithm; + +        initialize_libgcrypt(); + +        hash_size = gcry_md_get_algo_dlen(algorithm); +        assert(hash_size > 0); + +        if (nsec3->nsec3.next_hashed_name_size != hash_size) +                return -EINVAL; + +        r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true); +        if (r < 0) +                return r; + +        gcry_md_open(&md, algorithm, 0); +        if (!md) +                return -EIO; + +        gcry_md_write(md, wire_format, r); +        gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size); + +        result = gcry_md_read(md, 0); +        if (!result) { +                r = -EIO; +                goto finish; +        } + +        for (k = 0; k < nsec3->nsec3.iterations; k++) { +                uint8_t tmp[hash_size]; +                memcpy(tmp, result, hash_size); + +                gcry_md_reset(md); +                gcry_md_write(md, tmp, hash_size); +                gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size); + +                result = gcry_md_read(md, 0); +                if (!result) { +                        r = -EIO; +                        goto finish; +                } +        } + +        memcpy(ret, result, hash_size); +        r = (int) hash_size; + +finish: +        gcry_md_close(md); +        return r; +} + +int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result) { +        DnsResourceRecord *rr; +        int r; + +        assert(key); +        assert(result); + +        /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */ + +        DNS_ANSWER_FOREACH(rr, answer) { + +                if (rr->key->class != key->class) +                        continue; + +                switch (rr->key->type) { + +                case DNS_TYPE_NSEC: + +                        r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key)); +                        if (r < 0) +                                return r; +                        if (r > 0) { +                                *result = bitmap_isset(rr->nsec.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA; +                                return 0; +                        } + +                        r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key), rr->nsec.next_domain_name); +                        if (r < 0) +                                return r; +                        if (r > 0) { +                                *result = DNSSEC_NSEC_NXDOMAIN; +                                return 0; +                        } +                        break; + +                case DNS_TYPE_NSEC3: { +                        _cleanup_free_ void *decoded = NULL; +                        size_t decoded_size; +                        char label[DNS_LABEL_MAX]; +                        uint8_t hashed[DNSSEC_HASH_SIZE_MAX]; +                        int label_length, c, q; +                        const char *p; +                        bool covered; + +                        /* RFC  5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */ +                        if (!IN_SET(rr->nsec3.flags, 0, 1)) +                                continue; + +                        p = DNS_RESOURCE_KEY_NAME(rr->key); +                        label_length = dns_label_unescape(&p, label, sizeof(label)); +                        if (label_length < 0) +                                return label_length; +                        if (label_length == 0) +                                continue; + +                        r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), p); +                        if (r < 0) +                                return r; +                        if (r == 0) +                                continue; + +                        r = unbase32hexmem(label, label_length, false, &decoded, &decoded_size); +                        if (r == -EINVAL) +                                continue; +                        if (r < 0) +                                return r; + +                        if (decoded_size != rr->nsec3.next_hashed_name_size) +                                continue; + +                        c = memcmp(decoded, rr->nsec3.next_hashed_name, decoded_size); +                        if (c == 0) +                                continue; + +                        r = dnssec_nsec3_hash(rr, DNS_RESOURCE_KEY_NAME(key), hashed); +                        /* RFC 5155, Section 8.1 says we MUST ignore NSEC3 RRs with unknown algorithms */ +                        if (r == -EOPNOTSUPP) +                                continue; +                        if (r < 0) +                                return r; +                        if ((size_t) r != decoded_size) +                                continue; + +                        r = memcmp(decoded, hashed, decoded_size); +                        if (r == 0) { +                                *result = bitmap_isset(rr->nsec3.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA; +                                return 0; +                        } + +                        q = memcmp(hashed, rr->nsec3.next_hashed_name, decoded_size); + +                        covered = c < 0 ? +                                r < 0 && q < 0 : +                                q < 0 || r < 0; + +                        if (covered) { +                                *result = DNSSEC_NSEC_NXDOMAIN; +                                return 0; +                        } + +                        break; +                } + +                default: +                        break; +                } +        } + +        /* No approproate NSEC RR found, report this. */ +        *result = DNSSEC_NSEC_NO_RR; +        return 0; +} +  static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {          [DNSSEC_NO] = "no",          [DNSSEC_TRUST] = "trust", @@ -795,5 +995,6 @@ static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {          [DNSSEC_MISSING_KEY] = "missing-key",          [DNSSEC_UNSIGNED] = "unsigned",          [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary", +        [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",  };  DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult); diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h index f33abe3e11..442e301302 100644 --- a/src/resolve/resolved-dns-dnssec.h +++ b/src/resolve/resolved-dns-dnssec.h @@ -56,12 +56,16 @@ enum DnssecResult {          /* These two are added by the DnsTransaction logic */          DNSSEC_UNSIGNED,          DNSSEC_FAILED_AUXILIARY, +        DNSSEC_NSEC_MISMATCH,          _DNSSEC_RESULT_MAX,          _DNSSEC_RESULT_INVALID = -1  };  #define DNSSEC_CANONICAL_HOSTNAME_MAX (DNS_HOSTNAME_MAX + 2) +/* The longest digest we'll ever generate, of all digest algorithms we support */ +#define DNSSEC_HASH_SIZE_MAX (MAX(20, 32)) +  int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey);  int dnssec_key_match_rrsig(DnsResourceKey *key, DnsResourceRecord *rrsig); @@ -75,6 +79,17 @@ uint16_t dnssec_keytag(DnsResourceRecord *dnskey);  int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max); +int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret); + +typedef enum DnssecNsecResult { +        DNSSEC_NSEC_NO_RR,     /* No suitable NSEC/NSEC3 RR found */ +        DNSSEC_NSEC_NXDOMAIN, +        DNSSEC_NSEC_NODATA, +        DNSSEC_NSEC_FOUND, +} DnssecNsecResult; + +int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result); +  const char* dnssec_mode_to_string(DnssecMode m) _const_;  DnssecMode dnssec_mode_from_string(const char *s) _pure_; diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index b0739fcf12..c34ecc44f8 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -438,10 +438,15 @@ int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_          return 0;  } -int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start) { +int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, bool canonical_candidate, size_t *start) {          uint8_t *w;          int r; +        /* Append a label to a packet. Optionally, does this in DNSSEC +         * canonical form, if this label is marked as a candidate for +         * it, and the canonical form logic is enabled for the +         * packet */ +          assert(p);          assert(d); @@ -454,7 +459,7 @@ int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start          *(w++) = (uint8_t) l; -        if (p->canonical_form) { +        if (p->canonical_form && canonical_candidate) {                  size_t i;                  /* Generate in canonical form, as defined by DNSSEC @@ -479,6 +484,7 @@ int dns_packet_append_name(                  DnsPacket *p,                  const char *name,                  bool allow_compression, +                bool canonical_candidate,                  size_t *start) {          size_t saved_size; @@ -533,7 +539,7 @@ int dns_packet_append_name(                  if (k > 0)                          r = k; -                r = dns_packet_append_label(p, label, r, &n); +                r = dns_packet_append_label(p, label, r, canonical_candidate, &n);                  if (r < 0)                          goto fail; @@ -574,7 +580,7 @@ int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start)          saved_size = p->size; -        r = dns_packet_append_name(p, DNS_RESOURCE_KEY_NAME(k), true, NULL); +        r = dns_packet_append_name(p, DNS_RESOURCE_KEY_NAME(k), true, true, NULL);          if (r < 0)                  goto fail; @@ -596,7 +602,7 @@ fail:          return r;  } -static int dns_packet_append_type_window(DnsPacket *p, uint8_t window, uint8_t length, uint8_t *types, size_t *start) { +static int dns_packet_append_type_window(DnsPacket *p, uint8_t window, uint8_t length, const uint8_t *types, size_t *start) {          size_t saved_size;          int r; @@ -653,15 +659,16 @@ static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) {                  }                  window = n >> 8; -                  entry = n & 255;                  bitmaps[entry / 8] |= 1 << (7 - (entry % 8));          } -        r = dns_packet_append_type_window(p, window, entry / 8 + 1, bitmaps, NULL); -        if (r < 0) -                goto fail; +        if (bitmaps[entry / 8] != 0) { +                r = dns_packet_append_type_window(p, window, entry / 8 + 1, bitmaps, NULL); +                if (r < 0) +                        goto fail; +        }          if (start)                  *start = saved_size; @@ -762,14 +769,14 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star                  if (r < 0)                          goto fail; -                r = dns_packet_append_name(p, rr->srv.name, true, NULL); +                r = dns_packet_append_name(p, rr->srv.name, true, false, NULL);                  break;          case DNS_TYPE_PTR:          case DNS_TYPE_NS:          case DNS_TYPE_CNAME:          case DNS_TYPE_DNAME: -                r = dns_packet_append_name(p, rr->ptr.name, true, NULL); +                r = dns_packet_append_name(p, rr->ptr.name, true, false, NULL);                  break;          case DNS_TYPE_HINFO: @@ -812,11 +819,11 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star                  break;          case DNS_TYPE_SOA: -                r = dns_packet_append_name(p, rr->soa.mname, true, NULL); +                r = dns_packet_append_name(p, rr->soa.mname, true, false, NULL);                  if (r < 0)                          goto fail; -                r = dns_packet_append_name(p, rr->soa.rname, true, NULL); +                r = dns_packet_append_name(p, rr->soa.rname, true, false, NULL);                  if (r < 0)                          goto fail; @@ -844,7 +851,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star                  if (r < 0)                          goto fail; -                r = dns_packet_append_name(p, rr->mx.exchange, true, NULL); +                r = dns_packet_append_name(p, rr->mx.exchange, true, false, NULL);                  break;          case DNS_TYPE_LOC: @@ -948,7 +955,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star                  if (r < 0)                          goto fail; -                r = dns_packet_append_name(p, rr->rrsig.signer, false, NULL); +                r = dns_packet_append_name(p, rr->rrsig.signer, false, true, NULL);                  if (r < 0)                          goto fail; @@ -956,7 +963,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star                  break;          case DNS_TYPE_NSEC: -                r = dns_packet_append_name(p, rr->nsec.next_domain_name, false, NULL); +                r = dns_packet_append_name(p, rr->nsec.next_domain_name, false, false, NULL);                  if (r < 0)                          goto fail; diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 5b6a71dc01..082e92833c 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -169,8 +169,8 @@ int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start);  int dns_packet_append_uint32(DnsPacket *p, uint32_t v, size_t *start);  int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start);  int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_t *start); -int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, size_t *start); -int dns_packet_append_name(DnsPacket *p, const char *name, bool allow_compression, size_t *start); +int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, bool canonical_candidate, size_t *start); +int dns_packet_append_name(DnsPacket *p, const char *name, bool allow_compression, bool canonical_candidate, size_t *start);  int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *key, size_t *start);  int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start);  int dns_packet_append_opt_rr(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, size_t *start); diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index a6565f2ba2..405882a6ea 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -185,6 +185,14 @@ static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {                  switch (t->state) { +                case DNS_TRANSACTION_NULL: +                        /* If there's a NULL transaction pending, then +                         * this means not all transactions where +                         * started yet, and we were called from within +                         * the stackframe that is supposed to start +                         * remaining transactions. In this case, +                         * simply claim the candidate is pending. */ +                  case DNS_TRANSACTION_PENDING:                  case DNS_TRANSACTION_VALIDATING:                          /* If there's one transaction currently in @@ -197,9 +205,6 @@ static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {                          state = t->state;                          break; -                case DNS_TRANSACTION_NULL: -                        assert_not_reached("Transaction not started?"); -                  default:                          if (state != DNS_TRANSACTION_SUCCESS)                                  state = t->state; diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 82b49c1440..f09788b0c6 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -1288,7 +1288,10 @@ static int dns_transaction_is_primary_response(DnsTransaction *t, DnsResourceRec          /* Check if the specified RR is the "primary" response,           * i.e. either matches the question precisely or is a -         * CNAME/DNAME for it */ +         * CNAME/DNAME for it, or is any kind of NSEC/NSEC3 RR */ + +        if (IN_SET(rr->key->type, DNS_TYPE_NSEC, DNS_TYPE_NSEC3)) +                return 1;          r = dns_resource_key_match_rr(t->key, rr, NULL);          if (r != 0) @@ -1469,8 +1472,57 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {          /* Everything that's now in t->answer is known to be good, hence cacheable. */          t->n_answer_cacheable = (unsigned) -1; /* everything! */ -        t->answer_authenticated = true; -        t->dnssec_result = DNSSEC_VALIDATED; +        /* At this point the answer only contains validated +         * RRsets. Now, let's see if it actually answers the question +         * we asked. If so, great! If it doesn't, then see if +         * NSEC/NSEC3 can prove this. */ +        r = dns_answer_match_key(t->answer, t->key); +        if (r < 0) +                return r; +        if (r > 0) { +                /* Yes, it answer the question, everything is authenticated. */ +                t->dnssec_result = DNSSEC_VALIDATED; +                t->answer_rcode = DNS_RCODE_SUCCESS; +                t->answer_authenticated = true; +        } else if (r == 0) { +                DnssecNsecResult nr; + +                /* Bummer! Let's check NSEC/NSEC3 */ +                r = dnssec_test_nsec(t->answer, t->key, &nr); +                if (r < 0) +                        return r; + +                switch (nr) { + +                case DNSSEC_NSEC_NXDOMAIN: +                        /* NSEC proves the domain doesn't exist. Very good. */ +                        t->dnssec_result = DNSSEC_VALIDATED; +                        t->answer_rcode = DNS_RCODE_NXDOMAIN; +                        t->answer_authenticated = true; +                        break; + +                case DNSSEC_NSEC_NODATA: +                        /* NSEC proves that there's no data here, very good. */ +                        t->dnssec_result = DNSSEC_VALIDATED; +                        t->answer_rcode = DNS_RCODE_SUCCESS; +                        t->answer_authenticated = true; +                        break; + +                case DNSSEC_NSEC_NO_RR: +                        /* No NSEC data? Bummer! */ +                        t->dnssec_result = DNSSEC_UNSIGNED; +                        break; + +                case DNSSEC_NSEC_FOUND: +                        /* NSEC says it needs to be there, but we couldn't find it? Bummer! */ +                        t->dnssec_result = DNSSEC_NSEC_MISMATCH; +                        break; + +                default: +                        assert_not_reached("Unexpected NSEC result."); +                } +        } +          return 1;  } diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c index a2118513f1..807eeb3d9a 100644 --- a/src/resolve/test-dnssec.c +++ b/src/resolve/test-dnssec.c @@ -27,6 +27,103 @@  #include "resolved-dns-dnssec.h"  #include "resolved-dns-rr.h"  #include "string-util.h" +#include "hexdecoct.h" + +static void test_dnssec_verify_rrset2(void) { + +       static const uint8_t signature_blob[] = { +               0x48, 0x45, 0xc8, 0x8b, 0xc0, 0x14, 0x92, 0xf5, 0x15, 0xc6, 0x84, 0x9d, 0x2f, 0xe3, 0x32, 0x11, +               0x7d, 0xf1, 0xe6, 0x87, 0xb9, 0x42, 0xd3, 0x8b, 0x9e, 0xaf, 0x92, 0x31, 0x0a, 0x53, 0xad, 0x8b, +               0xa7, 0x5c, 0x83, 0x39, 0x8c, 0x28, 0xac, 0xce, 0x6e, 0x9c, 0x18, 0xe3, 0x31, 0x16, 0x6e, 0xca, +               0x38, 0x31, 0xaf, 0xd9, 0x94, 0xf1, 0x84, 0xb1, 0xdf, 0x5a, 0xc2, 0x73, 0x22, 0xf6, 0xcb, 0xa2, +               0xe7, 0x8c, 0x77, 0x0c, 0x74, 0x2f, 0xc2, 0x13, 0xb0, 0x93, 0x51, 0xa9, 0x4f, 0xae, 0x0a, 0xda, +               0x45, 0xcc, 0xfd, 0x43, 0x99, 0x36, 0x9a, 0x0d, 0x21, 0xe0, 0xeb, 0x30, 0x65, 0xd4, 0xa0, 0x27, +               0x37, 0x3b, 0xe4, 0xc1, 0xc5, 0xa1, 0x2a, 0xd1, 0x76, 0xc4, 0x7e, 0x64, 0x0e, 0x5a, 0xa6, 0x50, +               0x24, 0xd5, 0x2c, 0xcc, 0x6d, 0xe5, 0x37, 0xea, 0xbd, 0x09, 0x34, 0xed, 0x24, 0x06, 0xa1, 0x22, +        }; + +        static const uint8_t dnskey_blob[] = { +                0x03, 0x01, 0x00, 0x01, 0xc3, 0x7f, 0x1d, 0xd1, 0x1c, 0x97, 0xb1, 0x13, 0x34, 0x3a, 0x9a, 0xea, +                0xee, 0xd9, 0x5a, 0x11, 0x1b, 0x17, 0xc7, 0xe3, 0xd4, 0xda, 0x20, 0xbc, 0x5d, 0xba, 0x74, 0xe3, +                0x37, 0x99, 0xec, 0x25, 0xce, 0x93, 0x7f, 0xbd, 0x22, 0x73, 0x7e, 0x14, 0x71, 0xe0, 0x60, 0x07, +                0xd4, 0x39, 0x8b, 0x5e, 0xe9, 0xba, 0x25, 0xe8, 0x49, 0xe9, 0x34, 0xef, 0xfe, 0x04, 0x5c, 0xa5, +                0x27, 0xcd, 0xa9, 0xda, 0x70, 0x05, 0x21, 0xab, 0x15, 0x82, 0x24, 0xc3, 0x94, 0xf5, 0xd7, 0xb7, +                0xc4, 0x66, 0xcb, 0x32, 0x6e, 0x60, 0x2b, 0x55, 0x59, 0x28, 0x89, 0x8a, 0x72, 0xde, 0x88, 0x56, +                0x27, 0x95, 0xd9, 0xac, 0x88, 0x4f, 0x65, 0x2b, 0x68, 0xfc, 0xe6, 0x41, 0xc1, 0x1b, 0xef, 0x4e, +                0xd6, 0xc2, 0x0f, 0x64, 0x88, 0x95, 0x5e, 0xdd, 0x3a, 0x02, 0x07, 0x50, 0xa9, 0xda, 0xa4, 0x49, +                0x74, 0x62, 0xfe, 0xd7, +        }; + +        _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *nsec = NULL, *rrsig = NULL, *dnskey = NULL; +        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; +        _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL; +        DnssecResult result; + +        nsec = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC, "nasa.gov"); +        assert_se(nsec); + +        nsec->nsec.next_domain_name = strdup("3D-Printing.nasa.gov"); +        assert_se(nsec->nsec.next_domain_name); + +        nsec->nsec.types = bitmap_new(); +        assert_se(nsec->nsec.types); +        assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_A) >= 0); +        assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NS) >= 0); +        assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_SOA) >= 0); +        assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_MX) >= 0); +        assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_TXT) >= 0); +        assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_RRSIG) >= 0); +        assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NSEC) >= 0); +        assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_DNSKEY) >= 0); +        assert_se(bitmap_set(nsec->nsec.types, 65534) >= 0); + +        assert_se(dns_resource_record_to_string(nsec, &x) >= 0); +        log_info("NSEC: %s", x); + +        rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "NaSa.GOV."); +        assert_se(rrsig); + +        rrsig->rrsig.type_covered = DNS_TYPE_NSEC; +        rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_RSASHA256; +        rrsig->rrsig.labels = 2; +        rrsig->rrsig.original_ttl = 300; +        rrsig->rrsig.expiration = 0x5689002f; +        rrsig->rrsig.inception = 0x56617230; +        rrsig->rrsig.key_tag = 30390; +        rrsig->rrsig.signer = strdup("Nasa.Gov."); +        assert_se(rrsig->rrsig.signer); +        rrsig->rrsig.signature_size = sizeof(signature_blob); +        rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size); +        assert_se(rrsig->rrsig.signature); + +        assert_se(dns_resource_record_to_string(rrsig, &y) >= 0); +        log_info("RRSIG: %s", y); + +        dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nASA.gOV"); +        assert_se(dnskey); + +        dnskey->dnskey.flags = 256; +        dnskey->dnskey.protocol = 3; +        dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_RSASHA256; +        dnskey->dnskey.key_size = sizeof(dnskey_blob); +        dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob)); +        assert_se(dnskey->dnskey.key); + +        assert_se(dns_resource_record_to_string(dnskey, &z) >= 0); +        log_info("DNSKEY: %s", z); +        log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey)); + +        assert_se(dnssec_key_match_rrsig(nsec->key, rrsig) > 0); +        assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey) > 0); + +        answer = dns_answer_new(1); +        assert_se(answer); +        assert_se(dns_answer_add(answer, nsec, 0) >= 0); + +        /* Validate the RR as it if was 2015-12-11 today */ +        assert_se(dnssec_verify_rrset(answer, nsec->key, rrsig, dnskey, 1449849318*USEC_PER_SEC, &result) >= 0); +        assert_se(result == DNSSEC_VALIDATED); +}  static void test_dnssec_verify_rrset(void) { @@ -209,11 +306,46 @@ static void test_dnssec_canonicalize(void) {          test_dnssec_canonicalize_one("FOO..bar.", NULL, -EINVAL);  } +static void test_dnssec_nsec3_hash(void) { +        static const uint8_t salt[] = { 0xB0, 0x1D, 0xFA, 0xCE }; +        static const uint8_t next_hashed_name[] = { 0x84, 0x10, 0x26, 0x53, 0xc9, 0xfa, 0x4d, 0x85, 0x6c, 0x97, 0x82, 0xe2, 0x8f, 0xdf, 0x2d, 0x5e, 0x87, 0x69, 0xc4, 0x52 }; +        _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; +        _cleanup_free_ char *a = NULL, *b = NULL; +        uint8_t h[DNSSEC_HASH_SIZE_MAX]; +        int k; + +        /* The NSEC3 RR for eurid.eu on 2015-12-14. */ +        rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC3, "PJ8S08RR45VIQDAQGE7EN3VHKNROTBMM.eurid.eu."); +        assert_se(rr); + +        rr->nsec3.algorithm = DNSSEC_DIGEST_SHA1; +        rr->nsec3.flags = 1; +        rr->nsec3.iterations = 1; +        rr->nsec3.salt = memdup(salt, sizeof(salt)); +        assert_se(rr->nsec3.salt); +        rr->nsec3.salt_size = sizeof(salt); +        rr->nsec3.next_hashed_name = memdup(next_hashed_name, sizeof(next_hashed_name)); +        assert_se(rr->nsec3.next_hashed_name); +        rr->nsec3.next_hashed_name_size = sizeof(next_hashed_name); + +        assert_se(dns_resource_record_to_string(rr, &a) >= 0); +        log_info("NSEC3: %s", a); + +        k = dnssec_nsec3_hash(rr, "eurid.eu", &h); +        assert_se(k >= 0); + +        b = base32hexmem(h, k, false); +        assert_se(b); +        assert_se(strcasecmp(b, "PJ8S08RR45VIQDAQGE7EN3VHKNROTBMM") == 0); +} +  int main(int argc, char*argv[]) {          test_dnssec_canonicalize();          test_dnssec_verify_dns_key();          test_dnssec_verify_rrset(); +        test_dnssec_verify_rrset2(); +        test_dnssec_nsec3_hash();          return 0;  } | 
