mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 10:18:25 +00:00
Cache RRSIGS.
This commit is contained in:
49
src/cache.c
49
src/cache.c
@@ -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 */
|
||||
|
||||
162
src/dnssec.c
162
src/dnssec.c
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user