mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 10:18:25 +00:00
Caching of DNSSEC records.
This commit is contained in:
77
src/cache.c
77
src/cache.c
@@ -316,6 +316,7 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
|
|||||||
if (flags & F_FORWARD)
|
if (flags & F_FORWARD)
|
||||||
{
|
{
|
||||||
for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
|
for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
|
||||||
|
{
|
||||||
if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
|
if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
|
||||||
{
|
{
|
||||||
*up = crecp->hash_next;
|
*up = crecp->hash_next;
|
||||||
@@ -324,20 +325,63 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
|
|||||||
cache_unlink(crecp);
|
cache_unlink(crecp);
|
||||||
cache_free(crecp);
|
cache_free(crecp);
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
else if ((crecp->flags & F_FORWARD) &&
|
|
||||||
((flags & crecp->flags & F_TYPE) || ((crecp->flags | flags) & F_CNAME)) &&
|
if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
|
||||||
hostname_isequal(cache_get_name(crecp), name))
|
{
|
||||||
|
|
||||||
|
if ((flags & crecp->flags & (F_IPV4 | F_IPV6)) || ((crecp->flags | flags) & F_CNAME))
|
||||||
{
|
{
|
||||||
if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
|
if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
|
||||||
return 0;
|
return 0;
|
||||||
*up = crecp->hash_next;
|
*up = crecp->hash_next;
|
||||||
cache_unlink(crecp);
|
cache_unlink(crecp);
|
||||||
cache_free(crecp);
|
cache_free(crecp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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))
|
||||||
|
{
|
||||||
|
int del = 0;
|
||||||
|
switch (flags & (F_DS | F_DNSKEY))
|
||||||
|
{
|
||||||
|
case F_DS:
|
||||||
|
if (crecp->addr.ds.class == addr->addr.dnssec.class)
|
||||||
|
del = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case F_DNSKEY:
|
||||||
|
if (crecp->addr.key.class == addr->addr.dnssec.class)
|
||||||
|
del = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Both set -> RRSIG */
|
||||||
|
case F_DS | F_DNSKEY:
|
||||||
|
if (crecp->addr.sig.class == addr->addr.dnssec.class &&
|
||||||
|
crecp->addr.sig.type_covered == addr->addr.dnssec.type)
|
||||||
|
del = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (del)
|
||||||
|
{
|
||||||
|
if (crecp->flags & F_CONFIG)
|
||||||
|
return 0;
|
||||||
|
*up = crecp->hash_next;
|
||||||
|
cache_unlink(crecp);
|
||||||
|
cache_free(crecp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else
|
|
||||||
up = &crecp->hash_next;
|
up = &crecp->hash_next;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@@ -409,8 +453,8 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
|
|||||||
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
|
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
|
||||||
ttl = daemon->max_cache_ttl;
|
ttl = daemon->max_cache_ttl;
|
||||||
|
|
||||||
/* Don't log keys */
|
/* Don't log keys here, done elsewhere */
|
||||||
if (flags & (F_IPV4 | F_IPV6))
|
if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
|
||||||
log_query(flags | F_UPSTREAM, name, addr, NULL);
|
log_query(flags | F_UPSTREAM, name, addr, NULL);
|
||||||
|
|
||||||
/* if previous insertion failed give up now. */
|
/* if previous insertion failed give up now. */
|
||||||
@@ -554,6 +598,9 @@ 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 (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
|
||||||
{
|
{
|
||||||
if ((crecp->flags & F_FORWARD) &&
|
if ((crecp->flags & F_FORWARD) &&
|
||||||
|
#ifdef HAVE_DNSSEC
|
||||||
|
((crecp->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) &&
|
||||||
|
#endif
|
||||||
(crecp->flags & prot) &&
|
(crecp->flags & prot) &&
|
||||||
hostname_isequal(cache_get_name(crecp), name))
|
hostname_isequal(cache_get_name(crecp), name))
|
||||||
{
|
{
|
||||||
@@ -611,6 +658,9 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
|
|||||||
|
|
||||||
if (ans &&
|
if (ans &&
|
||||||
(ans->flags & F_FORWARD) &&
|
(ans->flags & F_FORWARD) &&
|
||||||
|
#ifdef HAVE_DNSSEC
|
||||||
|
((ans->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) &&
|
||||||
|
#endif
|
||||||
(ans->flags & prot) &&
|
(ans->flags & prot) &&
|
||||||
hostname_isequal(cache_get_name(ans), name))
|
hostname_isequal(cache_get_name(ans), name))
|
||||||
return ans;
|
return ans;
|
||||||
@@ -971,7 +1021,9 @@ void cache_reload(void)
|
|||||||
cache->name.namep = key->name;
|
cache->name.namep = key->name;
|
||||||
cache->uid = key->keylen;
|
cache->uid = key->keylen;
|
||||||
cache->addr.key.algo = key->algo;
|
cache->addr.key.algo = key->algo;
|
||||||
|
cache->addr.key.flags = key->flags;
|
||||||
cache->addr.key.keytag = dnskey_keytag(key->algo, key->flags, (unsigned char *)key->key, key->keylen);
|
cache->addr.key.keytag = dnskey_keytag(key->algo, key->flags, (unsigned char *)key->key, key->keylen);
|
||||||
|
cache->addr.key.class = C_IN; /* TODO - in option? */
|
||||||
cache_hash(cache);
|
cache_hash(cache);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1228,16 +1280,17 @@ void dump_cache(time_t now)
|
|||||||
if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
|
if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
|
||||||
a = cache_get_cname_target(cache);
|
a = cache_get_cname_target(cache);
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
else if (cache->flags & F_DNSKEY)
|
|
||||||
{
|
|
||||||
a = daemon->addrbuff;
|
|
||||||
sprintf(a, "%3u %u", cache->addr.key.algo, cache->uid);
|
|
||||||
}
|
|
||||||
else if (cache->flags & F_DS)
|
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);
|
||||||
|
}
|
||||||
|
else if (cache->flags & F_DNSKEY)
|
||||||
{
|
{
|
||||||
a = daemon->addrbuff;
|
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.digest);
|
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))
|
||||||
|
|||||||
@@ -76,7 +76,6 @@
|
|||||||
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
|
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
|
||||||
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */
|
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */
|
||||||
|
|
||||||
|
|
||||||
struct dns_header {
|
struct dns_header {
|
||||||
u16 id;
|
u16 id;
|
||||||
u8 hb3,hb4;
|
u8 hb3,hb4;
|
||||||
|
|||||||
@@ -244,7 +244,12 @@ struct all_addr {
|
|||||||
#ifdef HAVE_IPV6
|
#ifdef HAVE_IPV6
|
||||||
struct in6_addr addr6;
|
struct in6_addr addr6;
|
||||||
#endif
|
#endif
|
||||||
|
/* for log_query */
|
||||||
unsigned int keytag;
|
unsigned int keytag;
|
||||||
|
/* for cache_insert if RRSIG, DNSKEY, DS */
|
||||||
|
struct {
|
||||||
|
unsigned short class, type;
|
||||||
|
} dnssec;
|
||||||
} addr;
|
} addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -363,13 +368,22 @@ struct crec {
|
|||||||
} cname;
|
} cname;
|
||||||
struct {
|
struct {
|
||||||
struct blockdata *keydata;
|
struct blockdata *keydata;
|
||||||
|
unsigned short class, flags, keytag;
|
||||||
unsigned char algo;
|
unsigned char algo;
|
||||||
unsigned char digest; /* DS only */
|
|
||||||
unsigned short keytag;
|
|
||||||
} key;
|
} key;
|
||||||
|
struct {
|
||||||
|
struct blockdata *keydata;
|
||||||
|
unsigned short class, keytag;
|
||||||
|
unsigned char algo;
|
||||||
|
unsigned char digest;
|
||||||
|
} ds;
|
||||||
|
struct {
|
||||||
|
struct blockdata *keydata;
|
||||||
|
unsigned short class, type_covered, keytag;
|
||||||
|
} sig;
|
||||||
} addr;
|
} addr;
|
||||||
time_t ttd; /* time to die */
|
time_t ttd; /* time to die */
|
||||||
/* used as keylen ifF_DNSKEY, index to source for F_HOSTS */
|
/* used as keylen if F_DNSKEY or F_DS, index to source for F_HOSTS */
|
||||||
int uid;
|
int uid;
|
||||||
unsigned short flags;
|
unsigned short flags;
|
||||||
union {
|
union {
|
||||||
@@ -409,7 +423,7 @@ struct crec {
|
|||||||
#define F_SECSTAT (1u<<24)
|
#define F_SECSTAT (1u<<24)
|
||||||
|
|
||||||
/* composites */
|
/* composites */
|
||||||
#define F_TYPE (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS) /* Only one may be set */
|
#define F_TYPE (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS) /* F_DS & F_DNSKEY -> RRSIG yuck. */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
162
src/dnssec.c
162
src/dnssec.c
@@ -707,7 +707,9 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
|||||||
{
|
{
|
||||||
/* iterate through all possible keys 4035 5.3.1 */
|
/* iterate through all possible keys 4035 5.3.1 */
|
||||||
for (; crecp; crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY))
|
for (; crecp; crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY))
|
||||||
if (crecp->addr.key.algo == algo && crecp->addr.key.keytag == key_tag &&
|
if (crecp->addr.key.algo == algo &&
|
||||||
|
crecp->addr.key.keytag == key_tag &&
|
||||||
|
crecp->addr.key.class == class &&
|
||||||
verify(crecp->addr.key.keydata, crecp->uid, sig, sig_len, digest, algo))
|
verify(crecp->addr.key.keydata, crecp->uid, sig, sig_len, digest, algo))
|
||||||
return STAT_SECURE;
|
return STAT_SECURE;
|
||||||
}
|
}
|
||||||
@@ -732,6 +734,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
struct crec *crecp, *recp1;
|
struct crec *crecp, *recp1;
|
||||||
int rc, j, qtype, qclass, ttl, rdlen, flags, algo, valid, keytag;
|
int rc, j, qtype, qclass, ttl, rdlen, flags, algo, valid, keytag;
|
||||||
struct blockdata *key;
|
struct blockdata *key;
|
||||||
|
struct all_addr a;
|
||||||
|
|
||||||
if (ntohs(header->qdcount) != 1 ||
|
if (ntohs(header->qdcount) != 1 ||
|
||||||
!extract_name(header, plen, &p, name, 1, 4))
|
!extract_name(header, plen, &p, name, 1, 4))
|
||||||
@@ -786,23 +789,34 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
algo = *p++;
|
algo = *p++;
|
||||||
keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
|
keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
|
||||||
|
|
||||||
|
/* 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
|
/* 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. */
|
call cache_end_insert() and this will never be committed. */
|
||||||
if ((key = blockdata_alloc((char*)p, rdlen - 4)) &&
|
if ((key = blockdata_alloc((char*)p, rdlen - 4)) &&
|
||||||
(recp1 = cache_insert(name, NULL, now, ttl, F_FORWARD | F_DNSKEY)))
|
(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->uid = rdlen - 4;
|
||||||
recp1->addr.key.keydata = key;
|
recp1->addr.key.keydata = key;
|
||||||
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.class = class;
|
||||||
}
|
}
|
||||||
|
|
||||||
p = psave;
|
p = psave;
|
||||||
if (!ADD_RDLEN(header, p, plen, rdlen))
|
if (!ADD_RDLEN(header, p, plen, rdlen))
|
||||||
return STAT_INSECURE; /* bad packet */
|
return STAT_INSECURE; /* bad packet */
|
||||||
|
|
||||||
/* Already determined that message is OK. Just loop stuffing cache */
|
/* Already determined that message is OK or failed to store or ineligable
|
||||||
if (valid || !key)
|
(ie no zone key flag) key. Don't attempt to validate, just loop stuffing cache */
|
||||||
|
if (valid || !key || !(flags & 0x100))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (recp1 = crecp; recp1; recp1 = cache_find_by_name(recp1, name, now, F_DS))
|
for (recp1 = crecp; recp1; recp1 = cache_find_by_name(recp1, name, now, F_DS))
|
||||||
@@ -811,10 +825,10 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
unsigned char *digest, *ds_digest;
|
unsigned char *digest, *ds_digest;
|
||||||
const struct nettle_hash *hash;
|
const struct nettle_hash *hash;
|
||||||
|
|
||||||
if (recp1->addr.key.algo == algo &&
|
if (recp1->addr.ds.algo == algo &&
|
||||||
recp1->addr.key.keytag == keytag &&
|
recp1->addr.ds.keytag == keytag &&
|
||||||
(flags & 0x100) && /* zone key flag */
|
recp1->addr.ds.class == class &&
|
||||||
(hash = hash_find(ds_digest_name(recp1->addr.key.digest))) &&
|
(hash = hash_find(ds_digest_name(recp1->addr.ds.digest))) &&
|
||||||
hash_init(hash, &ctx, &digest))
|
hash_init(hash, &ctx, &digest))
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -833,10 +847,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
memcmp(ds_digest, digest, recp1->uid) == 0 &&
|
memcmp(ds_digest, digest, recp1->uid) == 0 &&
|
||||||
validate_rrset(now, header, plen, class, T_DNSKEY, name, keyname, key, rdlen - 4, algo, keytag))
|
validate_rrset(now, header, plen, class, T_DNSKEY, name, keyname, key, rdlen - 4, algo, keytag))
|
||||||
{
|
{
|
||||||
struct all_addr a;
|
|
||||||
valid = 1;
|
valid = 1;
|
||||||
a.addr.keytag = keytag;
|
|
||||||
log_query(F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -866,10 +877,8 @@ 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 *psave, *p = (unsigned char *)(header+1);
|
unsigned char *p = (unsigned char *)(header+1);
|
||||||
struct crec *crecp;
|
int qtype, qclass, val;
|
||||||
int qtype, qclass, val, j;
|
|
||||||
struct blockdata *key;
|
|
||||||
|
|
||||||
if (ntohs(header->qdcount) != 1 ||
|
if (ntohs(header->qdcount) != 1 ||
|
||||||
!extract_name(header, plen, &p, name, 1, 4))
|
!extract_name(header, plen, &p, name, 1, 4))
|
||||||
@@ -884,68 +893,12 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
|||||||
if (qtype != T_DS || qclass != class || ntohs(header->ancount) == 0)
|
if (qtype != T_DS || qclass != class || ntohs(header->ancount) == 0)
|
||||||
return STAT_INSECURE;
|
return STAT_INSECURE;
|
||||||
|
|
||||||
val = validate_rrset(now, header, plen, class, T_DS, name, keyname, NULL, 0, 0, 0);
|
val = dnssec_validate_reply(now, header, plen, name, keyname, NULL);
|
||||||
|
|
||||||
if (val == STAT_BOGUS)
|
if (val == STAT_BOGUS)
|
||||||
log_query(F_UPSTREAM, name, NULL, "BOGUS DS");
|
log_query(F_UPSTREAM, name, NULL, "BOGUS DS");
|
||||||
|
|
||||||
/* failed to validate or missing key. */
|
|
||||||
if (val != STAT_SECURE)
|
|
||||||
return val;
|
return val;
|
||||||
|
|
||||||
cache_start_insert();
|
|
||||||
|
|
||||||
for (j = ntohs(header->ancount); j != 0; j--)
|
|
||||||
{
|
|
||||||
int ttl, rdlen, rc, algo, digest, keytag;
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* check type, class and name, skip if not in DS rrset */
|
|
||||||
if (qclass == class && qtype == T_DS && rc == 1)
|
|
||||||
{
|
|
||||||
if (!CHECK_LEN(header, p, plen, rdlen) || rdlen < 4)
|
|
||||||
return STAT_INSECURE; /* bad packet */
|
|
||||||
|
|
||||||
psave = p;
|
|
||||||
GETSHORT(keytag, p);
|
|
||||||
algo = *p++;
|
|
||||||
digest = *p++;
|
|
||||||
|
|
||||||
/* We've proved that the DS is OK, store it in the cache */
|
|
||||||
if ((key = blockdata_alloc((char*)p, rdlen - 4)) &&
|
|
||||||
(crecp = cache_insert(name, NULL, now, ttl, F_FORWARD | F_DS)))
|
|
||||||
{
|
|
||||||
struct all_addr a;
|
|
||||||
a.addr.keytag = keytag;
|
|
||||||
log_query(F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u");
|
|
||||||
crecp->addr.key.digest = digest;
|
|
||||||
crecp->addr.key.keydata = key;
|
|
||||||
crecp->addr.key.algo = algo;
|
|
||||||
crecp->addr.key.keytag = keytag;
|
|
||||||
crecp->uid = rdlen - 4;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return STAT_INSECURE; /* cache problem */
|
|
||||||
|
|
||||||
p = psave;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ADD_RDLEN(header, p, plen, rdlen))
|
|
||||||
return STAT_INSECURE; /* bad packet */
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
cache_end_insert();
|
|
||||||
|
|
||||||
return STAT_SECURE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 4034 6.1 */
|
/* 4034 6.1 */
|
||||||
@@ -1014,6 +967,7 @@ static int hostname_cmp(const char *a, const char *b)
|
|||||||
|
|
||||||
|
|
||||||
/* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) */
|
/* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) */
|
||||||
|
/* Returns are the same as validate_rrset, plus the class if the missing key is in *class */
|
||||||
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class)
|
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class)
|
||||||
{
|
{
|
||||||
unsigned char *ans_start, *p1, *p2;
|
unsigned char *ans_start, *p1, *p2;
|
||||||
@@ -1058,11 +1012,69 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Not done, validate now */
|
/* Not done, validate now */
|
||||||
if (j == i && (rc = validate_rrset(now, header, plen, class1, type1, name, keyname, NULL, 0, 0, 0)) != STAT_SECURE)
|
if (j == i)
|
||||||
{
|
{
|
||||||
|
if ((rc = validate_rrset(now, header, plen, class1, type1, name, keyname, NULL, 0, 0, 0)) != STAT_SECURE)
|
||||||
|
{
|
||||||
|
if (class)
|
||||||
*class = class1; /* Class for DS or DNSKEY */
|
*class = class1; /* Class for DS or DNSKEY */
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If we just validated a DS RRset, cache it */
|
||||||
|
if (type1 == T_DS)
|
||||||
|
{
|
||||||
|
int ttl, keytag, algo, digest;
|
||||||
|
unsigned char *psave;
|
||||||
|
struct all_addr a;
|
||||||
|
struct blockdata *key;
|
||||||
|
struct crec *crecp;
|
||||||
|
|
||||||
|
cache_start_insert();
|
||||||
|
|
||||||
|
for (p2 = ans_start, j = 0; j < ntohs(header->ancount) + ntohs(header->nscount); j++)
|
||||||
|
{
|
||||||
|
if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
|
||||||
|
return STAT_INSECURE; /* bad packet */
|
||||||
|
|
||||||
|
GETSHORT(type2, p2);
|
||||||
|
GETSHORT(class2, p2);
|
||||||
|
GETLONG(ttl, p2);
|
||||||
|
GETSHORT(rdlen2, p2);
|
||||||
|
|
||||||
|
if (type2 == T_DS && class2 == class1 && rc == 1)
|
||||||
|
{
|
||||||
|
psave = p2;
|
||||||
|
GETSHORT(keytag, p2);
|
||||||
|
algo = *p2++;
|
||||||
|
digest = *p2++;
|
||||||
|
|
||||||
|
/* Cache needs to known class for DNSSEC stuff */
|
||||||
|
a.addr.dnssec.class = class2;
|
||||||
|
|
||||||
|
if ((key = blockdata_alloc((char*)p2, rdlen2 - 4)) &&
|
||||||
|
(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DS | F_DNSSECOK)))
|
||||||
|
{
|
||||||
|
a.addr.keytag = keytag;
|
||||||
|
log_query(F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u");
|
||||||
|
crecp->addr.ds.digest = digest;
|
||||||
|
crecp->addr.ds.keydata = key;
|
||||||
|
crecp->addr.ds.algo = algo;
|
||||||
|
crecp->addr.ds.keytag = keytag;
|
||||||
|
crecp->addr.ds.class = class2;
|
||||||
|
crecp->uid = rdlen2 - 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
p2 = psave;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ADD_RDLEN(header, p2, plen, rdlen2))
|
||||||
|
return STAT_INSECURE; /* bad packet */
|
||||||
|
}
|
||||||
|
|
||||||
|
cache_end_insert();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ADD_RDLEN(header, p1, plen, rdlen1))
|
if (!ADD_RDLEN(header, p1, plen, rdlen1))
|
||||||
@@ -1079,6 +1091,10 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
GETSHORT(type1, p1);
|
GETSHORT(type1, p1);
|
||||||
GETSHORT(class1, p1);
|
GETSHORT(class1, p1);
|
||||||
|
|
||||||
|
/* Can't validate RRSIG query */
|
||||||
|
if (type1 == T_RRSIG)
|
||||||
|
return STAT_INSECURE;
|
||||||
|
|
||||||
cname_loop:
|
cname_loop:
|
||||||
for (j = ntohs(header->ancount); j != 0; j--)
|
for (j = ntohs(header->ancount); j != 0; j--)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1368,6 +1368,11 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
|
|||||||
p += INADDRSZ;
|
p += INADDRSZ;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'b':
|
||||||
|
usval = va_arg(ap, int);
|
||||||
|
*p++ = usval;
|
||||||
|
break;
|
||||||
|
|
||||||
case 's':
|
case 's':
|
||||||
usval = va_arg(ap, int);
|
usval = va_arg(ap, int);
|
||||||
PUTSHORT(usval, p);
|
PUTSHORT(usval, p);
|
||||||
@@ -1538,6 +1543,58 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_DNSSEC
|
||||||
|
if ((qtype == T_DNSKEY || qtype == T_ANY) && (crecp = cache_find_by_name(NULL, name, now, F_DNSKEY)))
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
char *keydata;
|
||||||
|
|
||||||
|
if (crecp->addr.ds.class == qclass &&
|
||||||
|
(qtype == T_DNSKEY || (crecp->flags & F_CONFIG)) &&
|
||||||
|
(keydata = blockdata_retrieve(crecp->addr.key.keydata, crecp->uid, NULL)))
|
||||||
|
{
|
||||||
|
ans = 1;
|
||||||
|
if (!dryrun)
|
||||||
|
{
|
||||||
|
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->uid, keydata))
|
||||||
|
anscount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (crecp = cache_find_by_name(crecp, name, now, F_DNSKEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((qtype == T_DS || qtype == T_ANY) && (crecp = cache_find_by_name(NULL, name, now, F_DS)))
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
char *keydata;
|
||||||
|
|
||||||
|
if (crecp->addr.ds.class == qclass &&
|
||||||
|
(qtype == T_DS || (crecp->flags & F_CONFIG)) &&
|
||||||
|
(keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->uid, NULL)))
|
||||||
|
{
|
||||||
|
ans = 1;
|
||||||
|
if (!dryrun)
|
||||||
|
{
|
||||||
|
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->uid, keydata))
|
||||||
|
anscount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (crecp = cache_find_by_name(crecp, name, now, F_DS));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (qclass == C_IN)
|
if (qclass == C_IN)
|
||||||
{
|
{
|
||||||
struct txt_record *t;
|
struct txt_record *t;
|
||||||
|
|||||||
Reference in New Issue
Block a user