Commit b8eac191 authored by Simon Kelley's avatar Simon Kelley

Negative caching for DS records.

parent b47b04c8
...@@ -564,7 +564,14 @@ struct crec *cache_insert(char *name, struct all_addr *addr, ...@@ -564,7 +564,14 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
*cache_get_name(new) = 0; *cache_get_name(new) = 0;
if (addr) if (addr)
{
#ifdef HAVE_DNSSEC
if (flags & (F_DS | F_DNSKEY))
new->uid = addr->addr.dnssec.class;
else
#endif
new->addr.addr = *addr; new->addr.addr = *addr;
}
new->ttd = now + (time_t)ttl; new->ttd = now + (time_t)ttl;
new->next = new_chain; new->next = new_chain;
...@@ -1304,25 +1311,16 @@ void dump_cache(time_t now) ...@@ -1304,25 +1311,16 @@ void dump_cache(time_t now)
else if (cache->flags & F_DS) else if (cache->flags & F_DS)
{ {
if (cache->flags & F_DNSKEY) if (cache->flags & F_DNSKEY)
{
/* RRSIG */ /* RRSIG */
a = daemon->addrbuff;
sprintf(a, "%5u %3u %s", cache->addr.sig.keytag, sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered)); cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
} else if (!(cache->flags & F_NEG))
else
{
a = daemon->addrbuff;
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag, sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
cache->addr.ds.algo, cache->addr.ds.digest); cache->addr.ds.algo, cache->addr.ds.digest);
} }
}
else if (cache->flags & F_DNSKEY) else if (cache->flags & F_DNSKEY)
{
a = daemon->addrbuff;
sprintf(a, "%5u %3u %3u", cache->addr.key.keytag, sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
cache->addr.key.algo, cache->addr.key.flags); cache->addr.key.algo, cache->addr.key.flags);
}
#endif #endif
else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD)) else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
{ {
......
...@@ -862,6 +862,10 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch ...@@ -862,6 +862,10 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
return STAT_NEED_DS; return STAT_NEED_DS;
} }
/* If we've cached that DS provably doesn't exist, result must be INSECURE */
if (crecp->flags & F_NEG)
return STAT_INSECURE;
/* NOTE, we need to find ONE DNSKEY which matches the DS */ /* NOTE, we need to find ONE DNSKEY which matches the DS */
for (valid = 0, j = ntohs(header->ancount); j != 0 && !valid; j--) for (valid = 0, j = ntohs(header->ancount); j != 0 && !valid; j--)
{ {
...@@ -998,7 +1002,6 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch ...@@ -998,7 +1002,6 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
recp1->addr.key.algo = algo; recp1->addr.key.algo = algo;
recp1->addr.key.keytag = keytag; recp1->addr.key.keytag = keytag;
recp1->addr.key.flags = flags; recp1->addr.key.flags = flags;
recp1->uid = class;
} }
} }
} }
...@@ -1024,7 +1027,6 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch ...@@ -1024,7 +1027,6 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
blockdata_free(key); blockdata_free(key);
else else
{ {
crecp->uid = class;
crecp->addr.sig.keydata = key; crecp->addr.sig.keydata = key;
crecp->addr.sig.keylen = rdlen; crecp->addr.sig.keylen = rdlen;
crecp->addr.sig.keytag = keytag; crecp->addr.sig.keytag = keytag;
...@@ -1054,7 +1056,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch ...@@ -1054,7 +1056,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
/* The DNS packet is expected to contain the answer to a DS query /* The DNS packet is expected to contain the answer to a DS query
Put all DSs in the answer which are valid into the cache. Put all DSs in the answer which are valid into the cache.
return codes: return codes:
STAT_INSECURE bad packet, no DS in reply. STAT_INSECURE bad packet, no DS in reply, proven no DS in reply.
STAT_SECURE At least one valid DS found and in cache. STAT_SECURE At least one valid DS found and in cache.
STAT_BOGUS At least one DS found, which fails validation. STAT_BOGUS At least one DS found, which fails validation.
STAT_NEED_DNSKEY DNSKEY records to validate a DS not found, name in keyname STAT_NEED_DNSKEY DNSKEY records to validate a DS not found, name in keyname
...@@ -1063,10 +1065,10 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch ...@@ -1063,10 +1065,10 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class) int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
{ {
unsigned char *p = (unsigned char *)(header+1); unsigned char *p = (unsigned char *)(header+1);
int qtype, qclass, val; int qtype, qclass, val, i;
if (ntohs(header->qdcount) != 1 || if (ntohs(header->qdcount) != 1 ||
!extract_name(header, plen, &p, name, 1, 4)) !(p = skip_name(p, header, plen, 4)))
return STAT_INSECURE; return STAT_INSECURE;
GETSHORT(qtype, p); GETSHORT(qtype, p);
...@@ -1077,16 +1079,64 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char ...@@ -1077,16 +1079,64 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
else else
val = dnssec_validate_reply(now, header, plen, name, keyname, NULL); val = dnssec_validate_reply(now, header, plen, name, keyname, NULL);
if (val == STAT_BOGUS)
{
p = (unsigned char *)(header+1); p = (unsigned char *)(header+1);
extract_name(header, plen, &p, name, 1, 4); extract_name(header, plen, &p, name, 1, 4);
p += 4; /* qtype, qclass */
if (val == STAT_BOGUS)
log_query(F_UPSTREAM, name, NULL, "BOGUS DS"); log_query(F_UPSTREAM, name, NULL, "BOGUS DS");
}
/* proved that no DS exists, can't validate */ /* proved that no DS exists, cache neg answer, can't validate */
if (val == STAT_SECURE && ntohs(header->ancount) == 0) if (val == STAT_SECURE && ntohs(header->ancount) == 0)
{
int rdlen, rc;
unsigned long ttl, minttl = ULONG_MAX;
struct all_addr a;
for (i = ntohs(header->nscount); i != 0; i--)
{
if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
return STAT_INSECURE;
GETSHORT(qtype, p);
GETSHORT(qclass, p);
GETLONG(ttl, p);
GETSHORT(rdlen, p);
if (!CHECK_LEN(header, p, plen, rdlen) || rdlen < 4)
return STAT_INSECURE; /* bad packet */
if (qclass != class || qtype != T_SOA || rc ==2)
{
p += rdlen;
continue;
}
if (ttl < minttl)
minttl = ttl;
/* MNAME */
if (!(p = skip_name(p, header, plen, 0)))
return STAT_INSECURE; return STAT_INSECURE;
/* RNAME */
if (!(p = skip_name(p, header, plen, 20)))
return STAT_INSECURE;
p += 16; /* SERIAL REFRESH RETRY EXPIRE */
GETLONG(ttl, p); /* minTTL */
if (ttl < minttl)
minttl = ttl;
}
cache_start_insert();
a.addr.dnssec.class = class;
cache_insert(name, &a, now, ttl, F_FORWARD | F_DS | F_DNSSECOK | F_NEG | (RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0));
cache_end_insert();
return STAT_INSECURE;
}
return val; return val;
} }
...@@ -1707,7 +1757,6 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch ...@@ -1707,7 +1757,6 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
crecp->addr.ds.keydata = key; crecp->addr.ds.keydata = key;
crecp->addr.ds.algo = algo; crecp->addr.ds.algo = algo;
crecp->addr.ds.keytag = keytag; crecp->addr.ds.keytag = keytag;
crecp->uid = class2;
crecp->addr.ds.keylen = rdlen2 - 4; crecp->addr.ds.keylen = rdlen2 - 4;
} }
} }
...@@ -1737,7 +1786,6 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch ...@@ -1737,7 +1786,6 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
blockdata_free(key); blockdata_free(key);
else else
{ {
crecp->uid = class1;
crecp->addr.sig.keydata = key; crecp->addr.sig.keydata = key;
crecp->addr.sig.keylen = rdlen2; crecp->addr.sig.keylen = rdlen2;
crecp->addr.sig.keytag = keytag; crecp->addr.sig.keytag = keytag;
......
...@@ -1581,7 +1581,15 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -1581,7 +1581,15 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (crecp->uid == qclass) if (crecp->uid == qclass)
{ {
gotone = 1; gotone = 1;
if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL))) if (!dryrun)
{
if (crecp->flags & F_NEG)
{
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
log_query(F_UPSTREAM, name, NULL, "secure no DS");
}
else if ((keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
{ {
struct all_addr a; struct all_addr a;
a.addr.keytag = crecp->addr.ds.keytag; a.addr.keytag = crecp->addr.ds.keytag;
...@@ -1589,12 +1597,14 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -1589,12 +1597,14 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset, crec_ttl(crecp, now), &nameoffset,
T_DS, qclass, "sbbt", T_DS, qclass, "sbbt",
crecp->addr.ds.keytag, crecp->addr.ds.algo, crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata)) crecp->addr.ds.keytag, crecp->addr.ds.algo,
crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata))
anscount++; anscount++;
} }
} }
} }
}
else /* DNSKEY */ else /* DNSKEY */
{ {
crecp = NULL; crecp = NULL;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment