From: Simon Kelley Date: Tue, 15 Dec 2015 12:04:40 +0000 (+0000) Subject: Abandon caching RRSIGs and returning them from cache. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=93be5b1e023b0c661e1ec2cd6d811a8ec9055c49;p=people%2Fms%2Fdnsmasq.git Abandon caching RRSIGs and returning them from cache. The list of exceptions to being able to locally answer cached data for validated records when DNSSEC data is requested was getting too long, so don't ever do that. This means that the cache no longer has to hold RRSIGS and allows us to lose lots of code. Note that cached validated answers are still returned as long as do=0 --- diff --git a/src/cache.c b/src/cache.c index 1b76b67..51ba7cc 100644 --- a/src/cache.c +++ b/src/cache.c @@ -189,12 +189,7 @@ static void cache_hash(struct crec *crecp) static void cache_blockdata_free(struct crec *crecp) { if (crecp->flags & F_DNSKEY) - { - if (crecp->flags & F_DS) - blockdata_free(crecp->addr.sig.keydata); - else - blockdata_free(crecp->addr.key.keydata); - } + blockdata_free(crecp->addr.key.keydata); else if ((crecp->flags & F_DS) && !(crecp->flags & F_NEG)) blockdata_free(crecp->addr.ds.keydata); } @@ -369,13 +364,8 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no } #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)) && - (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)) + /* Deletion has to be class-sensitive for DS and DNSKEY */ + if ((flags & crecp->flags & (F_DNSKEY | F_DS)) && crecp->uid == addr->addr.dnssec.class) { if (crecp->flags & F_CONFIG) return crecp; @@ -532,13 +522,9 @@ struct crec *cache_insert(char *name, struct all_addr *addr, struct all_addr free_addr = new->addr.addr;; #ifdef HAVE_DNSSEC - /* For DNSSEC records, addr holds class and type_covered for RRSIG */ + /* For DNSSEC records, addr holds class. */ if (new->flags & (F_DS | F_DNSKEY)) - { - free_addr.addr.dnssec.class = new->uid; - if ((new->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY)) - free_addr.addr.dnssec.type = new->addr.sig.type_covered; - } + free_addr.addr.dnssec.class = new->uid; #endif free_avail = 1; /* Must be free space now. */ @@ -653,9 +639,6 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp)) { if ((crecp->flags & F_FORWARD) && -#ifdef HAVE_DNSSEC - (((crecp->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) || (prot & F_NSIGMATCH)) && -#endif (crecp->flags & prot) && hostname_isequal(cache_get_name(crecp), name)) { @@ -713,9 +696,6 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi if (ans && (ans->flags & F_FORWARD) && -#ifdef HAVE_DNSSEC - (((ans->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) || (prot & F_NSIGMATCH)) && -#endif (ans->flags & prot) && hostname_isequal(cache_get_name(ans), name)) return ans; @@ -1472,11 +1452,7 @@ void dump_cache(time_t now) #ifdef HAVE_DNSSEC else if (cache->flags & F_DS) { - if (cache->flags & F_DNSKEY) - /* RRSIG */ - sprintf(a, "%5u %3u %s", cache->addr.sig.keytag, - cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered)); - else if (!(cache->flags & F_NEG)) + if (!(cache->flags & F_NEG)) sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag, cache->addr.ds.algo, cache->addr.ds.digest); } @@ -1502,8 +1478,6 @@ void dump_cache(time_t now) else if (cache->flags & F_CNAME) t = "C"; #ifdef HAVE_DNSSEC - else if ((cache->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY)) - t = "G"; /* DNSKEY and DS set -> RRISG */ else if (cache->flags & F_DS) t = "S"; else if (cache->flags & F_DNSKEY) diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 023a1cf..4344cae 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -398,14 +398,9 @@ struct crec { unsigned char algo; unsigned char digest; } ds; - struct { - struct blockdata *keydata; - unsigned short keylen, type_covered, keytag; - char algo; - } sig; } addr; time_t ttd; /* time to die */ - /* used as class if DNSKEY/DS/RRSIG, index to source for F_HOSTS */ + /* used as class if DNSKEY/DS, index to source for F_HOSTS */ unsigned int uid; unsigned short flags; union { @@ -445,8 +440,7 @@ struct crec { #define F_SECSTAT (1u<<24) #define F_NO_RR (1u<<25) #define F_IPSET (1u<<26) -#define F_NSIGMATCH (1u<<27) -#define F_NOEXTRA (1u<<28) +#define F_NOEXTRA (1u<<27) /* Values of uid in crecs with F_CONFIG bit set. */ #define SRC_INTERFACE 0 diff --git a/src/dnssec.c b/src/dnssec.c index de7b335..1ae03a6 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -1004,7 +1004,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch { unsigned char *psave, *p = (unsigned char *)(header+1); struct crec *crecp, *recp1; - int rc, j, qtype, qclass, ttl, rdlen, flags, algo, valid, keytag, type_covered; + int rc, j, qtype, qclass, ttl, rdlen, flags, algo, valid, keytag; struct blockdata *key; struct all_addr a; @@ -1115,7 +1115,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch if (valid) { - /* DNSKEY RRset determined to be OK, now cache it and the RRsigs that sign it. */ + /* DNSKEY RRset determined to be OK, now cache it. */ cache_start_insert(); p = skip_questions(header, plen); @@ -1155,7 +1155,10 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch if ((key = blockdata_alloc((char*)p, rdlen - 4))) { if (!(recp1 = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DNSSECOK))) - blockdata_free(key); + { + blockdata_free(key); + return STAT_BOGUS; + } else { a.addr.keytag = keytag; @@ -1169,38 +1172,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch } } } - else if (qtype == T_RRSIG) - { - /* RRSIG, cache if covers DNSKEY RRset */ - if (rdlen < 18) - return STAT_BOGUS; /* bad packet */ - - GETSHORT(type_covered, p); - - if (type_covered == T_DNSKEY) - { - a.addr.dnssec.class = class; - a.addr.dnssec.type = type_covered; - - algo = *p++; - p += 13; /* labels, orig_ttl, expiration, inception */ - GETSHORT(keytag, p); - if ((key = blockdata_alloc((char*)psave, rdlen))) - { - if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DS))) - blockdata_free(key); - else - { - crecp->addr.sig.keydata = key; - crecp->addr.sig.keylen = rdlen; - crecp->addr.sig.keytag = keytag; - crecp->addr.sig.type_covered = type_covered; - crecp->addr.sig.algo = algo; - } - } - } - } - + p = psave; } @@ -1326,7 +1298,8 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char cache_start_insert(); a.addr.dnssec.class = class; - cache_insert(name, &a, now, ttl, flags); + if (!cache_insert(name, &a, now, ttl, flags)) + return STAT_BOGUS; cache_end_insert(); @@ -2028,14 +2001,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch /* Not done, validate now */ if (j == i) { - int ttl, keytag, algo, digest, type_covered, sigcnt, rrcnt; + int ttl, keytag, algo, digest, sigcnt, rrcnt; unsigned char *psave; struct all_addr a; struct blockdata *key; struct crec *crecp; char *wildname; - int have_wildcard = 0; - + if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt)) return STAT_BOGUS; @@ -2096,8 +2068,6 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch if (rc == STAT_SECURE_WILDCARD) { - have_wildcard = 1; - /* An attacker replay a wildcard answer with a different answer and overlay a genuine RR. To prove this hasn't happened, the answer must prove that @@ -2119,7 +2089,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch return rc; } - /* Cache RRsigs in answer section, and if we just validated a DS RRset, cache it */ + /* If we just validated a DS RRset, cache it */ /* Also note if the RRset is the answer to the question, or the target of a CNAME */ cache_start_insert(); @@ -2168,45 +2138,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch } } } - else if (type2 == T_RRSIG) - { - if (rdlen2 < 18) - return STAT_BOGUS; /* bad packet */ - - GETSHORT(type_covered, p2); - - if (type_covered == type1 && - (type_covered == T_A || type_covered == T_AAAA || - type_covered == T_CNAME || type_covered == T_DS || - type_covered == T_DNSKEY || type_covered == T_PTR)) - { - a.addr.dnssec.type = type_covered; - a.addr.dnssec.class = class1; - - algo = *p2++; - p2 += 13; /* labels, orig_ttl, expiration, inception */ - GETSHORT(keytag, p2); - - /* We don't cache sigs for wildcard answers, because to reproduce the - answer from the cache will require one or more NSEC/NSEC3 records - which we don't cache. The lack of the RRSIG ensures that a query for - this RRset asking for a secure answer will always be forwarded. */ - if (!have_wildcard && (key = blockdata_alloc((char*)psave, rdlen2))) - { - if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DS))) - blockdata_free(key); - else - { - crecp->addr.sig.keydata = key; - crecp->addr.sig.keylen = rdlen2; - crecp->addr.sig.keytag = keytag; - crecp->addr.sig.type_covered = type_covered; - crecp->addr.sig.algo = algo; - } - } - } - } - + p2 = psave; } diff --git a/src/rfc1035.c b/src/rfc1035.c index 4eb1772..def8fa0 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -1275,11 +1275,9 @@ int check_for_local_domain(char *name, time_t now) struct naptr *naptr; /* Note: the call to cache_find_by_name is intended to find any record which matches - ie A, AAAA, CNAME, DS. Because RRSIG records are marked by setting both F_DS and F_DNSKEY, - cache_find_by name ordinarily only returns records with an exact match on those bits (ie - for the call below, only DS records). The F_NSIGMATCH bit changes this behaviour */ + ie A, AAAA, CNAME. */ - if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME | F_DS | F_NO_RR | F_NSIGMATCH)) && + if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME |F_NO_RR)) && (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))) return 1; @@ -1566,9 +1564,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, GETSHORT(flags, pheader); if ((sec_reqd = flags & 0x8000)) - *do_bit = 1;/* do bit */ + { + *do_bit = 1;/* do bit */ + *ad_reqd = 1; + } - *ad_reqd = 1; dryrun = 1; } @@ -1636,98 +1636,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, } } -#ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DNSKEY || qtype == T_DS)) - { - int gotone = 0; - struct blockdata *keydata; - - /* Do we have RRSIG? Can't do DS or DNSKEY otherwise. */ - if (sec_reqd) - { - crecp = NULL; - while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS))) - if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype) - break; - } - - if (!sec_reqd || crecp) - { - if (qtype == T_DS) - { - crecp = NULL; - while ((crecp = cache_find_by_name(crecp, name, now, F_DS))) - if (crecp->uid == qclass) - { - gotone = 1; - if (!dryrun) - { - if (crecp->flags & F_NEG) - { - if (crecp->flags & F_NXDOMAIN) - nxdomain = 1; - log_query(F_UPSTREAM, name, NULL, "no DS"); - } - else if ((keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL))) - { - struct all_addr a; - a.addr.keytag = crecp->addr.ds.keytag; - log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DS keytag %u"); - 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->addr.ds.keylen, keydata)) - anscount++; - - } - } - } - } - else /* DNSKEY */ - { - crecp = NULL; - while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY))) - if (crecp->uid == qclass) - { - gotone = 1; - if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.key.keydata, crecp->addr.key.keylen, NULL))) - { - struct all_addr a; - a.addr.keytag = crecp->addr.key.keytag; - log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DNSKEY keytag %u"); - 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->addr.key.keylen, keydata)) - anscount++; - } - } - } - } - - /* Now do RRSIGs */ - if (gotone) - { - ans = 1; - auth = 0; - if (!dryrun && sec_reqd) - { - crecp = NULL; - while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS))) - if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype && - (keydata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL))) - { - add_resource_record(header, limit, &trunc, nameoffset, &ansp, - crec_ttl(crecp, now), &nameoffset, - T_RRSIG, qclass, "t", crecp->addr.sig.keylen, keydata); - anscount++; - } - } - } - } -#endif - if (qclass == C_IN) { struct txt_record *t; @@ -1736,6 +1644,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name)) { ans = 1; + sec_data = 0; if (!dryrun) { log_query(F_CONFIG | F_RRNAME, name, NULL, ""); @@ -1792,6 +1701,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (intr) { + sec_data = 0; ans = 1; if (!dryrun) { @@ -1805,6 +1715,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, else if (ptr) { ans = 1; + sec_data = 0; if (!dryrun) { log_query(F_CONFIG | F_RRNAME, name, NULL, ""); @@ -1819,38 +1730,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, } else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa))) { - if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd) - { - if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK))) - crecp = NULL; -#ifdef HAVE_DNSSEC - else if (crecp->flags & F_DNSSECOK) - { - int gotsig = 0; - struct crec *rr_crec = NULL; - - while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY))) - { - if (rr_crec->addr.sig.type_covered == T_PTR && rr_crec->uid == C_IN) - { - char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL); - gotsig = 1; - - if (!dryrun && - add_resource_record(header, limit, &trunc, nameoffset, &ansp, - rr_crec->ttd - now, &nameoffset, - T_RRSIG, C_IN, "t", crecp->addr.sig.keylen, sigdata)) - anscount++; - } - } - - if (!gotsig) - crecp = NULL; - } -#endif - } - - if (crecp) + /* Don't use cache when DNSSEC data required. */ + if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK)) { do { @@ -1860,19 +1741,19 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!(crecp->flags & F_DNSSECOK)) sec_data = 0; - + + ans = 1; + if (crecp->flags & F_NEG) { - ans = 1; auth = 0; if (crecp->flags & F_NXDOMAIN) nxdomain = 1; if (!dryrun) log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL); } - else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID)) + else { - ans = 1; if (!(crecp->flags & (F_HOSTS | F_DHCP))) auth = 0; if (!dryrun) @@ -1892,6 +1773,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, else if (is_rev_synth(is_arpa, &addr, name)) { ans = 1; + sec_data = 0; if (!dryrun) { log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL); @@ -1908,6 +1790,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, { /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */ ans = 1; + sec_data = 0; nxdomain = 1; if (!dryrun) log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN, @@ -1955,6 +1838,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (i == 4) { ans = 1; + sec_data = 0; if (!dryrun) { addr.addr.addr4.s_addr = htonl(a); @@ -1993,6 +1877,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, continue; #endif ans = 1; + sec_data = 0; if (!dryrun) { gotit = 1; @@ -2032,48 +1917,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, crecp = save; } - /* If the client asked for DNSSEC and we can't provide RRSIGs, either - because we've not doing DNSSEC or the cached answer is signed by negative, - don't answer from the cache, forward instead. */ - if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd) - { - if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK))) - crecp = NULL; -#ifdef HAVE_DNSSEC - else if (crecp->flags & F_DNSSECOK) - { - /* We're returning validated data, need to return the RRSIG too. */ - struct crec *rr_crec = NULL; - 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) - sigtype = T_CNAME; - - while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY))) - { - if (rr_crec->addr.sig.type_covered == sigtype && rr_crec->uid == C_IN) - { - char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL); - gotsig = 1; - - if (!dryrun && - add_resource_record(header, limit, &trunc, nameoffset, &ansp, - rr_crec->ttd - now, &nameoffset, - T_RRSIG, C_IN, "t", rr_crec->addr.sig.keylen, sigdata)) - anscount++; - } - } - - if (!gotsig) - crecp = NULL; - } -#endif - } - - if (crecp) + /* If the client asked for DNSSEC don't use cached data. */ + if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK)) do { /* don't answer wildcard queries with data not from /etc/hosts