Commit e7829aef authored by Simon Kelley's avatar Simon Kelley

Cache RRSIGS.

parent 51ea3ca2
......@@ -330,8 +330,9 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
{
if ((flags & crecp->flags & (F_IPV4 | F_IPV6)) || ((crecp->flags | flags) & F_CNAME))
/* Don't delete DNSSEC in favour of a CNAME, they can co-exist */
if ((flags & crecp->flags & (F_IPV4 | F_IPV6)) ||
((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS)))
{
if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
return 0;
......@@ -344,7 +345,7 @@ 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 & crecp->flags & (F_DNSKEY | F_DS))
if ((flags & (F_DNSKEY | F_DS)) == (crecp->flags & (F_DNSKEY | F_DS)))
{
int del = 0;
switch (flags & (F_DS | F_DNSKEY))
......@@ -1227,6 +1228,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
void dump_cache(time_t now)
{
struct server *serv, *serv1;
char *t = "";
my_syslog(LOG_INFO, _("time %lu"), (unsigned long)now);
my_syslog(LOG_INFO, _("cache size %d, %d/%d cache insertions re-used unexpired cache entries."),
......@@ -1267,7 +1269,7 @@ void dump_cache(time_t now)
{
struct crec *cache ;
int i;
my_syslog(LOG_INFO, "Host Address Flags Expires");
my_syslog(LOG_INFO, "Host Address Flags Expires");
for (i=0; i<hash_size; i++)
for (cache = hash_table[i]; cache; cache = cache->hash_next)
......@@ -1282,9 +1284,21 @@ void dump_cache(time_t now)
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DS)
{
a = daemon->addrbuff;
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
cache->addr.ds.algo, cache->addr.ds.digest);
if (cache->flags & F_DNSKEY)
{
char tp[20];
/* RRSIG */
querystr("", tp, cache->addr.sig.type_covered);
a = daemon->addrbuff;
sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
cache->addr.sig.algo, tp);
}
else
{
a = daemon->addrbuff;
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
cache->addr.ds.algo, cache->addr.ds.digest);
}
}
else if (cache->flags & F_DNSKEY)
{
......@@ -1304,12 +1318,21 @@ void dump_cache(time_t now)
#endif
}
p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s%s%s%s ", a,
cache->flags & F_IPV4 ? "4" : "",
cache->flags & F_IPV6 ? "6" : "",
cache->flags & F_DNSKEY ? "K" : "",
cache->flags & F_DS ? "S" : "",
cache->flags & F_CNAME ? "C" : "",
if (cache->flags & F_IPV4)
t = "4";
else if (cache->flags & F_IPV6)
t = "6";
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)
t = "K";
#endif
p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s ", a, t,
cache->flags & F_FORWARD ? "F" : " ",
cache->flags & F_REVERSE ? "R" : " ",
cache->flags & F_IMMORTAL ? "I" : " ",
......
......@@ -380,6 +380,7 @@ struct crec {
struct {
struct blockdata *keydata;
unsigned short class, type_covered, keytag;
char algo;
} sig;
} addr;
time_t ttd; /* time to die */
......
......@@ -484,7 +484,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
otherwise find the key in the cache.
*/
static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class,
int type, char *name, char *keyname, struct blockdata *key, int keylen, int algo_in, int keytag_in)
int type, char *name, char *keyname, struct blockdata *key, int keylen, int algo_in, int keytag_in)
{
static unsigned char **rrset = NULL, **sigs = NULL;
static int rrset_sz = 0, sig_sz = 0;
......@@ -500,12 +500,14 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
name_labels = count_labels(name); /* For 4035 5.3.2 check */
cache_start_insert(); /* RRSIGS */
/* look for RRSIGs for this RRset and get pointers to each RR in the set. */
for (rrsetidx = 0, sigidx = 0, j = ntohs(header->ancount) + ntohs(header->nscount);
j != 0; j--)
{
unsigned char *pstart, *pdata;
int stype, sclass;
int stype, sclass, ttl;
pstart = p;
......@@ -514,12 +516,15 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
GETSHORT(stype, p);
GETSHORT(sclass, p);
p += 4; /* TTL */
GETLONG(ttl, p);
pdata = p;
GETSHORT(rdlen, p);
if (!CHECK_LEN(header, p, plen, rdlen))
return STAT_INSECURE;
if (res == 1 && sclass == class)
{
if (stype == type)
......@@ -550,17 +555,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
return STAT_INSECURE; /* bad packet */
GETSHORT(type_covered, p);
algo = *p++;
labels = *p++;
p += 4; /* orig_ttl */
GETLONG(sig_expiration, p);
GETLONG(sig_inception, p);
p = pdata + 2; /* restore for ADD_RDLEN */
if (type_covered == type &&
check_date_range(sig_inception, sig_expiration) &&
hash_find(algo_digest_name(algo)) &&
labels <= name_labels)
if (type_covered == type)
{
if (sigidx == sig_sz)
{
......@@ -581,7 +577,34 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
}
sigs[sigidx++] = pdata;
}
/* If it's a type we're going to cache, cache the RRISG too */
if (type_covered == T_A || type_covered == T_AAAA ||
type_covered == T_CNAME || type_covered == T_DS ||
type_covered == T_DNSKEY)
{
struct all_addr a;
struct blockdata *block;
a.addr.dnssec.class = class;
a.addr.dnssec.type = type_covered;
algo = *p++;
p += 13; /* labels, orig_ttl, expiration, inception */
GETSHORT(key_tag, p);
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->addr.sig.keydata = block;
crecp->addr.sig.class = class;
crecp->addr.sig.keytag = key_tag;
crecp->addr.sig.type_covered = type_covered;
crecp->addr.sig.algo = algo;
}
}
}
p = pdata + 2; /* restore for ADD_RDLEN */
}
}
......@@ -589,6 +612,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
return STAT_INSECURE;
}
cache_end_insert(); /* RRSIGS */
/* RRset empty, no RRSIGs */
if (rrsetidx == 0 || sigidx == 0)
return STAT_INSECURE;
......@@ -616,23 +641,26 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
algo = *p++;
labels = *p++;
GETLONG(orig_ttl, p);
p += 8; /* sig_expiration and sig_inception */
GETLONG(sig_expiration, p);
GETLONG(sig_inception, p);
GETSHORT(key_tag, p);
if (!extract_name(header, plen, &p, keyname, 1, 0))
return STAT_INSECURE;
if (!check_date_range(sig_inception, sig_expiration) ||
labels > name_labels ||
!(hash = hash_find(algo_digest_name(algo))) ||
!hash_init(hash, &ctx, &digest))
continue;
/* OK, we have the signature record, see if the relevant DNSKEY is in the cache. */
if (!key && !(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))
return STAT_NEED_KEY;
sig = p;
sig_len = rdlen - (p - psav);
if (!(hash = hash_find(algo_digest_name(algo))) ||
!hash_init(hash, &ctx, &digest))
continue;
nsigttl = htonl(orig_ttl);
hash->update(ctx, 18, psav);
......@@ -756,10 +784,8 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
return STAT_NEED_DS;
}
cache_start_insert();
/* NOTE, we need to find ONE DNSKEY which matches the DS */
for (valid = 0, j = ntohs(header->ancount); j != 0; j--)
for (valid = 0, j = ntohs(header->ancount); j != 0 && !valid; j--)
{
/* Ensure we have type, class TTL and length */
if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
......@@ -788,35 +814,19 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
return STAT_INSECURE;
algo = *p++;
keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
key = NULL;
/* Cache needs to known class for DNSSEC stuff */
a.addr.dnssec.class = class;
/* Put the key into the cache. Note that if the validation fails, we won't
call cache_end_insert() and this will never be committed. */
if ((key = blockdata_alloc((char*)p, rdlen - 4)) &&
(recp1 = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DNSSECOK)))
{
struct all_addr a;
a.addr.keytag = keytag;
log_query(F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u");
recp1->uid = 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;
}
/* key must have zone key flag set */
if (flags & 0x100)
key = blockdata_alloc((char*)p, rdlen - 4);
p = psave;
if (!ADD_RDLEN(header, p, plen, rdlen))
return STAT_INSECURE; /* bad packet */
/* Already determined that message is OK or failed to store or ineligable
(ie no zone key flag) key. Don't attempt to validate, just loop stuffing cache */
if (valid || !key || !(flags & 0x100))
/* No zone key flag or malloc failure */
if (!key)
continue;
for (recp1 = crecp; recp1; recp1 = cache_find_by_name(recp1, name, now, F_DS))
......@@ -852,10 +862,70 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
}
}
}
blockdata_free(key);
}
if (valid)
{
/* DNSKEY RRset determined to be OK, now cache it. */
cache_start_insert();
p = skip_questions(header, plen);
for (j = ntohs(header->ancount); j != 0; j--)
{
/* Ensure we have type, class TTL and length */
if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
return STAT_INSECURE; /* bad packet */
GETSHORT(qtype, p);
GETSHORT(qclass, p);
GETLONG(ttl, p);
GETSHORT(rdlen, p);
if (qclass != class || qtype != T_DNSKEY || rc == 2)
{
if (ADD_RDLEN(header, p, plen, rdlen))
continue;
return STAT_INSECURE; /* bad packet */
}
if (!CHECK_LEN(header, p, plen, rdlen) || rdlen < 4)
return STAT_INSECURE; /* bad packet */
psave = p;
GETSHORT(flags, p);
if (*p++ != 3)
return STAT_INSECURE;
algo = *p++;
keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
/* Cache needs to known class for DNSSEC stuff */
a.addr.dnssec.class = class;
if ((key = blockdata_alloc((char*)p, rdlen - 4)) &&
(recp1 = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DNSSECOK)))
{
struct all_addr a;
a.addr.keytag = keytag;
log_query(F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u");
recp1->uid = 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;
}
p = psave;
if (!ADD_RDLEN(header, p, plen, rdlen))
return STAT_INSECURE; /* bad packet */
}
/* commit cache insert. */
cache_end_insert();
return STAT_SECURE;
......
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