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