From 824202ef5420692260f2c70cf78d69a4fa179d6d Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Thu, 23 Jan 2014 20:59:46 +0000 Subject: [PATCH] More DNSSEC cache readout. --- src/cache.c | 45 ++++--------- src/dnsmasq.h | 8 +-- src/dnssec.c | 24 +++---- src/rfc1035.c | 171 +++++++++++++++++++++++++++++++------------------- 4 files changed, 135 insertions(+), 113 deletions(-) diff --git a/src/cache.c b/src/cache.c index 4eda3c7..71a3efc 100644 --- a/src/cache.c +++ b/src/cache.c @@ -345,38 +345,17 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign #ifdef HAVE_DNSSEC /* Deletion has to be class-sensitive for DS, DNSKEY, RRSIG, also type-covered sensitive for RRSIG */ - if ((flags & (F_DNSKEY | F_DS)) == (crecp->flags & (F_DNSKEY | F_DS))) + if ((flags & (F_DNSKEY | F_DS)) == (crecp->flags & (F_DNSKEY | F_DS)) && + crecp->uid == addr->addr.dnssec.class && + (!((flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY)) || + crecp->addr.sig.type_covered == addr->addr.dnssec.type)) { - int del = 0; - switch (flags & (F_DS | F_DNSKEY)) - { - case F_DS: - if (crecp->addr.ds.class == addr->addr.dnssec.class) - del = 1; - break; - - case F_DNSKEY: - if (crecp->addr.key.class == addr->addr.dnssec.class) - del = 1; - break; - - /* Both set -> RRSIG */ - case F_DS | F_DNSKEY: - if (crecp->addr.sig.class == addr->addr.dnssec.class && - crecp->addr.sig.type_covered == addr->addr.dnssec.type) - del = 1; - break; - } - - if (del) - { - if (crecp->flags & F_CONFIG) - return 0; - *up = crecp->hash_next; - cache_unlink(crecp); - cache_free(crecp); - continue; - } + if (crecp->flags & F_CONFIG) + return 0; + *up = crecp->hash_next; + cache_unlink(crecp); + cache_free(crecp); + continue; } #endif } @@ -1020,11 +999,11 @@ void cache_reload(void) { cache->flags = F_FORWARD | F_IMMORTAL | F_DNSKEY | F_CONFIG | F_NAMEP; cache->name.namep = key->name; - cache->uid = key->keylen; + cache->addr.key.keylen = key->keylen; cache->addr.key.algo = key->algo; cache->addr.key.flags = key->flags; cache->addr.key.keytag = dnskey_keytag(key->algo, key->flags, (unsigned char *)key->key, key->keylen); - cache->addr.key.class = C_IN; /* TODO - in option? */ + cache->uid = C_IN; /* TODO - in option? */ cache_hash(cache); } #endif diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 71d9ece..8213dde 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -368,23 +368,23 @@ struct crec { } cname; struct { struct blockdata *keydata; - unsigned short class, flags, keytag; + unsigned short keylen, flags, keytag; unsigned char algo; } key; struct { struct blockdata *keydata; - unsigned short class, keytag; + unsigned short keylen, keytag; unsigned char algo; unsigned char digest; } ds; struct { struct blockdata *keydata; - unsigned short class, type_covered, keytag; + unsigned short keylen, type_covered, keytag; char algo; } sig; } addr; time_t ttd; /* time to die */ - /* used as keylen if F_DNSKEY or F_DS, index to source for F_HOSTS */ + /* used as class if DNSKEY/DS/RRSIG, index to source for F_HOSTS */ int uid; unsigned short flags; union { diff --git a/src/dnssec.c b/src/dnssec.c index eff01b8..012e6d6 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -594,9 +594,9 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in if ((block = blockdata_alloc((char*)pdata + 2, rdlen)) && (crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DS))) { - crecp->uid = rdlen; + crecp->uid = class; crecp->addr.sig.keydata = block; - crecp->addr.sig.class = class; + crecp->addr.sig.keylen = rdlen; crecp->addr.sig.keytag = key_tag; crecp->addr.sig.type_covered = type_covered; crecp->addr.sig.algo = algo; @@ -737,8 +737,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in for (; crecp; crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY)) if (crecp->addr.key.algo == algo && crecp->addr.key.keytag == key_tag && - crecp->addr.key.class == class && - verify(crecp->addr.key.keydata, crecp->uid, sig, sig_len, digest, algo)) + crecp->uid == class && + verify(crecp->addr.key.keydata, crecp->addr.key.keylen, sig, sig_len, digest, algo)) return STAT_SECURE; } } @@ -837,7 +837,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch if (recp1->addr.ds.algo == algo && recp1->addr.ds.keytag == keytag && - recp1->addr.ds.class == class && + recp1->uid == class && (hash = hash_find(ds_digest_name(recp1->addr.ds.digest))) && hash_init(hash, &ctx, &digest)) @@ -852,9 +852,9 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch from_wire(name); - if (recp1->uid == (int)hash->digest_size && - (ds_digest = blockdata_retrieve(recp1->addr.key.keydata, recp1->uid, NULL)) && - memcmp(ds_digest, digest, recp1->uid) == 0 && + if (recp1->addr.ds.keylen == (int)hash->digest_size && + (ds_digest = blockdata_retrieve(recp1->addr.key.keydata, recp1->addr.ds.keylen, NULL)) && + memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 && validate_rrset(now, header, plen, class, T_DNSKEY, name, keyname, key, rdlen - 4, algo, keytag)) { valid = 1; @@ -913,12 +913,12 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch a.addr.keytag = keytag; log_query(F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u"); - recp1->uid = rdlen - 4; + recp1->addr.key.keylen = rdlen - 4; recp1->addr.key.keydata = key; recp1->addr.key.algo = algo; recp1->addr.key.keytag = keytag; recp1->addr.key.flags = flags; - recp1->addr.key.class = class; + recp1->uid = class; } p = psave; @@ -1131,8 +1131,8 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch crecp->addr.ds.keydata = key; crecp->addr.ds.algo = algo; crecp->addr.ds.keytag = keytag; - crecp->addr.ds.class = class2; - crecp->uid = rdlen2 - 4; + crecp->uid = class2; + crecp->addr.ds.keylen = rdlen2 - 4; } p2 = psave; diff --git a/src/rfc1035.c b/src/rfc1035.c index bd7aea7..75ba017 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -1549,9 +1549,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, do { char *keydata; - if (crecp->addr.ds.class == qclass && + if (crecp->uid == qclass && (qtype == T_DNSKEY || (crecp->flags & F_CONFIG)) && - (keydata = blockdata_retrieve(crecp->addr.key.keydata, crecp->uid, NULL))) + (keydata = blockdata_retrieve(crecp->addr.key.keydata, crecp->addr.key.keylen, NULL))) { ans = 1; if (!dryrun) @@ -1562,7 +1562,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, crec_ttl(crecp, now), &nameoffset, T_DNSKEY, qclass, "sbbt", - crecp->addr.key.flags, 3, crecp->addr.key.algo, crecp->uid, keydata)) + crecp->addr.key.flags, 3, crecp->addr.key.algo, crecp->addr.key.keylen, keydata)) anscount++; } } @@ -1574,9 +1574,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, do { char *keydata; - if (crecp->addr.ds.class == qclass && + if (crecp->uid == qclass && (qtype == T_DS || (crecp->flags & F_CONFIG)) && - (keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->uid, NULL))) + (keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL))) { ans = 1; if (!dryrun) @@ -1587,7 +1587,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, crec_ttl(crecp, now), &nameoffset, T_DS, qclass, "sbbt", - crecp->addr.ds.keytag, crecp->addr.ds.algo, crecp->addr.ds.digest, crecp->uid, keydata)) + crecp->addr.ds.keytag, crecp->addr.ds.algo, crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata)) anscount++; } } @@ -1859,72 +1859,115 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME))); crecp = save; } - - do - { - /* don't answer wildcard queries with data not from /etc/hosts - or DHCP leases */ - if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))) - break; - - if (!(crecp->flags & F_DNSSECOK)) - sec_data = 0; + +#ifdef HAVE_DNSSEC + if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && + (crecp->flags & F_DNSSECOK) && + !(crecp->flags & F_NEG) && + sec_reqd && + option_bool(OPT_DNSSEC_VALID)) + { + /* We're returning validated data, need to return the RRSIG too. */ + + int sigtype = type; + /* The signature may have expired even though the data is still in cache, + forward instead of answering from cache if so. */ + int gotsig = 0; if (crecp->flags & F_CNAME) - { - char *cname_target = cache_get_cname_target(crecp); - - if (!dryrun) - { - log_query(crecp->flags, name, NULL, record_source(crecp->uid)); - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, - crec_ttl(crecp, now), &nameoffset, - T_CNAME, C_IN, "d", cname_target)) - anscount++; - } - - strcpy(name, cname_target); - /* check if target interface_name */ - if (crecp->addr.cname.uid == -1) - goto intname_restart; - else - goto cname_restart; - } + sigtype = T_CNAME; - if (crecp->flags & F_NEG) + crecp = NULL; + while ((crecp = cache_find_by_name(crecp, name, now, F_DS | F_DNSKEY))) { - ans = 1; - auth = 0; - if (crecp->flags & F_NXDOMAIN) - nxdomain = 1; - if (!dryrun) - log_query(crecp->flags, name, NULL, NULL); - } - else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID)) - { - /* If we are returning local answers depending on network, - filter here. */ - if (localise && - (crecp->flags & F_HOSTS) && - !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask)) - continue; - - if (!(crecp->flags & (F_HOSTS | F_DHCP))) - auth = 0; - - ans = 1; - if (!dryrun) + if (crecp->addr.sig.type_covered == sigtype && crecp->uid == C_IN) { - log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr, - record_source(crecp->uid)); - - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, - crec_ttl(crecp, now), NULL, type, C_IN, - type == T_A ? "4" : "6", &crecp->addr)) + char *sigdata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL); + gotsig = 1; + + if (!dryrun && + add_resource_record(header, limit, &trunc, nameoffset, &ansp, + crecp->ttd - now, &nameoffset, + T_RRSIG, C_IN, "t", crecp->addr.sig.keylen, sigdata)) anscount++; } } - } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME))); + /* Need to re-run original cache search */ + crecp = gotsig ? cache_find_by_name(NULL, name, now, flag | F_CNAME) : NULL; + } + +#endif + if (crecp) + do + { + /* don't answer wildcard queries with data not from /etc/hosts + or DHCP leases */ + if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))) + break; + + if (!(crecp->flags & F_DNSSECOK)) + sec_data = 0; + + if (crecp->flags & F_CNAME) + { + char *cname_target = cache_get_cname_target(crecp); + + if (!dryrun) + { + log_query(crecp->flags, name, NULL, record_source(crecp->uid)); + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + crec_ttl(crecp, now), &nameoffset, + T_CNAME, C_IN, "d", cname_target)) + anscount++; + } + + strcpy(name, cname_target); + /* check if target interface_name */ + if (crecp->addr.cname.uid == -1) + goto intname_restart; + else + goto cname_restart; + } + + if (crecp->flags & F_NEG) + { + /* We don't cache NSEC records, so if a DNSSEC-validated negative answer + is cached and the client wants DNSSEC, forward rather than answering from the cache */ + if (!sec_reqd || !(crecp->flags & F_DNSSECOK)) + { + ans = 1; + auth = 0; + if (crecp->flags & F_NXDOMAIN) + nxdomain = 1; + if (!dryrun) + log_query(crecp->flags, name, NULL, NULL); + } + } + else + { + /* If we are returning local answers depending on network, + filter here. */ + if (localise && + (crecp->flags & F_HOSTS) && + !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask)) + continue; + + if (!(crecp->flags & (F_HOSTS | F_DHCP))) + auth = 0; + + ans = 1; + if (!dryrun) + { + log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr, + record_source(crecp->uid)); + + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + crec_ttl(crecp, now), NULL, type, C_IN, + type == T_A ? "4" : "6", &crecp->addr)) + anscount++; + } + } + } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME))); } else if (is_name_synthetic(flag, name, &addr)) {