mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 18:28:25 +00:00
Replace CRC32 with SHA1 for spoof detection in DNSSEC builds.
This commit is contained in:
@@ -539,6 +539,12 @@ struct hostsfile {
|
|||||||
#define FREC_DNSKEY_QUERY 8
|
#define FREC_DNSKEY_QUERY 8
|
||||||
#define FREC_DS_QUERY 16
|
#define FREC_DS_QUERY 16
|
||||||
|
|
||||||
|
#ifdef HAVE_DNSSEC
|
||||||
|
#define HASH_SIZE 20 /* SHA-1 digest size */
|
||||||
|
#else
|
||||||
|
#define HASH_SIZE sizeof(int)
|
||||||
|
#endif
|
||||||
|
|
||||||
struct frec {
|
struct frec {
|
||||||
union mysockaddr source;
|
union mysockaddr source;
|
||||||
struct all_addr dest;
|
struct all_addr dest;
|
||||||
@@ -550,9 +556,9 @@ struct frec {
|
|||||||
unsigned int iface;
|
unsigned int iface;
|
||||||
unsigned short orig_id, new_id;
|
unsigned short orig_id, new_id;
|
||||||
int fd, forwardall, flags;
|
int fd, forwardall, flags;
|
||||||
unsigned int crc;
|
|
||||||
time_t time;
|
time_t time;
|
||||||
#ifdef HAVE_DNSSEC
|
unsigned char *hash[HASH_SIZE];
|
||||||
|
#ifdef HAVE_DNSSEC
|
||||||
int class;
|
int class;
|
||||||
struct blockdata *stash; /* Saved reply, whilst we validate */
|
struct blockdata *stash; /* Saved reply, whilst we validate */
|
||||||
size_t stash_len;
|
size_t stash_len;
|
||||||
@@ -1070,6 +1076,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char
|
|||||||
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);
|
||||||
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);
|
||||||
int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
|
int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
|
||||||
|
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
|
||||||
|
|
||||||
/* util.c */
|
/* util.c */
|
||||||
void rand_init(void);
|
void rand_init(void);
|
||||||
|
|||||||
30
src/dnssec.c
30
src/dnssec.c
@@ -1366,5 +1366,35 @@ size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, i
|
|||||||
|
|
||||||
return add_do_bit(header, p - (unsigned char *)header, end);
|
return add_do_bit(header, p - (unsigned char *)header, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name)
|
||||||
|
{
|
||||||
|
int q;
|
||||||
|
unsigned int len;
|
||||||
|
unsigned char *p = (unsigned char *)(header+1);
|
||||||
|
const struct nettle_hash *hash;
|
||||||
|
void *ctx;
|
||||||
|
unsigned char *digest;
|
||||||
|
|
||||||
|
if (!(hash = hash_find("sha1")) || !hash_init(hash, &ctx, &digest))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (q = ntohs(header->qdcount); q != 0; q--)
|
||||||
|
{
|
||||||
|
if (!extract_name(header, plen, &p, name, 1, 4))
|
||||||
|
return digest; /* bad packet */
|
||||||
|
|
||||||
|
len = to_wire(name);
|
||||||
|
hash->update(ctx, len, (unsigned char *)name);
|
||||||
|
/* CRC the class and type as well */
|
||||||
|
hash->update(ctx, 4, p);
|
||||||
|
|
||||||
|
p += 4;
|
||||||
|
if (!CHECK_LEN(header, p, plen, 0))
|
||||||
|
return digest; /* bad packet */
|
||||||
|
}
|
||||||
|
|
||||||
|
return digest;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* HAVE_DNSSEC */
|
#endif /* HAVE_DNSSEC */
|
||||||
|
|||||||
@@ -16,11 +16,11 @@
|
|||||||
|
|
||||||
#include "dnsmasq.h"
|
#include "dnsmasq.h"
|
||||||
|
|
||||||
static struct frec *lookup_frec(unsigned short id, unsigned int crc);
|
static struct frec *lookup_frec(unsigned short id, void *hash);
|
||||||
static struct frec *lookup_frec_by_sender(unsigned short id,
|
static struct frec *lookup_frec_by_sender(unsigned short id,
|
||||||
union mysockaddr *addr,
|
union mysockaddr *addr,
|
||||||
unsigned int crc);
|
void *hash);
|
||||||
static unsigned short get_id(unsigned int crc);
|
static unsigned short get_id(void);
|
||||||
static void free_frec(struct frec *f);
|
static void free_frec(struct frec *f);
|
||||||
static struct randfd *allocate_rfd(int family);
|
static struct randfd *allocate_rfd(int family);
|
||||||
|
|
||||||
@@ -239,18 +239,23 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
char *domain = NULL;
|
char *domain = NULL;
|
||||||
int type = 0, norebind = 0;
|
int type = 0, norebind = 0;
|
||||||
struct all_addr *addrp = NULL;
|
struct all_addr *addrp = NULL;
|
||||||
unsigned int crc = questions_crc(header, plen, daemon->namebuff);
|
|
||||||
unsigned int flags = 0;
|
unsigned int flags = 0;
|
||||||
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
|
|
||||||
struct server *start = NULL;
|
struct server *start = NULL;
|
||||||
|
#ifdef HAVE_DNSSEC
|
||||||
|
void *hash = hash_questions(header, plen, daemon->namebuff);
|
||||||
|
#else
|
||||||
|
unsigned int crc = questions_crc(header, plen, daemon->namebuff);
|
||||||
|
void *hash = &crc;
|
||||||
|
#endif
|
||||||
|
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
|
||||||
|
|
||||||
/* RFC 4035: sect 4.6 para 2 */
|
/* RFC 4035: sect 4.6 para 2 */
|
||||||
header->hb4 &= ~HB4_AD;
|
header->hb4 &= ~HB4_AD;
|
||||||
|
|
||||||
/* may be no servers available. */
|
/* may be no servers available. */
|
||||||
if (!daemon->servers)
|
if (!daemon->servers)
|
||||||
forward = NULL;
|
forward = NULL;
|
||||||
else if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc)))
|
else if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))))
|
||||||
{
|
{
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
/* If we've already got an answer to this query, but we're awaiting keys for vaildation,
|
/* If we've already got an answer to this query, but we're awaiting keys for vaildation,
|
||||||
@@ -320,9 +325,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
forward->dest = *dst_addr;
|
forward->dest = *dst_addr;
|
||||||
forward->iface = dst_iface;
|
forward->iface = dst_iface;
|
||||||
forward->orig_id = ntohs(header->id);
|
forward->orig_id = ntohs(header->id);
|
||||||
forward->new_id = get_id(crc);
|
forward->new_id = get_id();
|
||||||
forward->fd = udpfd;
|
forward->fd = udpfd;
|
||||||
forward->crc = crc;
|
memcpy(forward->hash, hash, HASH_SIZE);
|
||||||
forward->forwardall = 0;
|
forward->forwardall = 0;
|
||||||
forward->flags = 0;
|
forward->flags = 0;
|
||||||
if (norebind)
|
if (norebind)
|
||||||
@@ -653,7 +658,11 @@ void reply_query(int fd, int family, time_t now)
|
|||||||
ssize_t n = recvfrom(fd, daemon->packet, daemon->packet_buff_sz, 0, &serveraddr.sa, &addrlen);
|
ssize_t n = recvfrom(fd, daemon->packet, daemon->packet_buff_sz, 0, &serveraddr.sa, &addrlen);
|
||||||
size_t nn;
|
size_t nn;
|
||||||
struct server *server;
|
struct server *server;
|
||||||
|
void *hash;
|
||||||
|
#ifndef HAVE_DNSSEC
|
||||||
|
unsigned int crc;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* packet buffer overwritten */
|
/* packet buffer overwritten */
|
||||||
daemon->srv_save = NULL;
|
daemon->srv_save = NULL;
|
||||||
|
|
||||||
@@ -671,10 +680,17 @@ void reply_query(int fd, int family, time_t now)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
header = (struct dns_header *)daemon->packet;
|
header = (struct dns_header *)daemon->packet;
|
||||||
|
|
||||||
|
#ifdef HAVE_DNSSEC
|
||||||
|
hash = hash_questions(header, n, daemon->namebuff);
|
||||||
|
#else
|
||||||
|
hash = &crc;
|
||||||
|
crc = questions_crc(header, n, daemon->namebuff);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!server ||
|
if (!server ||
|
||||||
n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR) ||
|
n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR) ||
|
||||||
!(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
|
!(forward = lookup_frec(ntohs(header->id), hash)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
|
if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
|
||||||
@@ -813,8 +829,9 @@ void reply_query(int fd, int family, time_t now)
|
|||||||
nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz,
|
nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz,
|
||||||
daemon->keyname, forward->class, T_DS, &server->addr);
|
daemon->keyname, forward->class, T_DS, &server->addr);
|
||||||
}
|
}
|
||||||
new->crc = questions_crc(header, nn, daemon->namebuff);
|
if ((hash = hash_questions(header, nn, daemon->namebuff)))
|
||||||
new->new_id = get_id(new->crc);
|
memcpy(new->hash, hash, HASH_SIZE);
|
||||||
|
new->new_id = get_id();
|
||||||
header->id = htons(new->new_id);
|
header->id = htons(new->new_id);
|
||||||
/* Save query for retransmission */
|
/* Save query for retransmission */
|
||||||
new->stash = blockdata_alloc((char *)header, nn);
|
new->stash = blockdata_alloc((char *)header, nn);
|
||||||
@@ -1357,8 +1374,13 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
if (!flags && last_server)
|
if (!flags && last_server)
|
||||||
{
|
{
|
||||||
struct server *firstsendto = NULL;
|
struct server *firstsendto = NULL;
|
||||||
|
#ifdef HAVE_DNSSEC
|
||||||
|
unsigned char *newhash, *hash[HASH_SIZE];
|
||||||
|
if ((newhash = hash_questions(header, (unsigned int)size, daemon->keyname)))
|
||||||
|
memcpy(hash, newhash, HASH_SIZE);
|
||||||
|
#else
|
||||||
unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
|
unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
|
||||||
|
#endif
|
||||||
/* Loop round available servers until we succeed in connecting to one.
|
/* Loop round available servers until we succeed in connecting to one.
|
||||||
Note that this code subtley ensures that consecutive queries on this connection
|
Note that this code subtley ensures that consecutive queries on this connection
|
||||||
which can go to the same server, do so. */
|
which can go to the same server, do so. */
|
||||||
@@ -1481,10 +1503,18 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
/* If the crc of the question section doesn't match the crc we sent, then
|
/* If the crc of the question section doesn't match the crc we sent, then
|
||||||
someone might be attempting to insert bogus values into the cache by
|
someone might be attempting to insert bogus values into the cache by
|
||||||
sending replies containing questions and bogus answers. */
|
sending replies containing questions and bogus answers. */
|
||||||
if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
|
#ifdef HAVE_DNSSEC
|
||||||
m = process_reply(header, now, last_server, (unsigned int)m,
|
newhash = hash_questions(header, (unsigned int)m, daemon->namebuff);
|
||||||
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec,
|
if (!newhash || memcmp(hash, newhash, HASH_SIZE) != 0)
|
||||||
cache_secure, check_subnet, &peer_addr);
|
break;
|
||||||
|
#else
|
||||||
|
if (crc != questions_crc(header, (unsigned int)m, daemon->namebuff))
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m = process_reply(header, now, last_server, (unsigned int)m,
|
||||||
|
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec,
|
||||||
|
cache_secure, check_subnet, &peer_addr);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1674,13 +1704,13 @@ struct frec *get_new_frec(time_t now, int *wait, int force)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* crc is all-ones if not known. */
|
/* crc is all-ones if not known. */
|
||||||
static struct frec *lookup_frec(unsigned short id, unsigned int crc)
|
static struct frec *lookup_frec(unsigned short id, void *hash)
|
||||||
{
|
{
|
||||||
struct frec *f;
|
struct frec *f;
|
||||||
|
|
||||||
for(f = daemon->frec_list; f; f = f->next)
|
for(f = daemon->frec_list; f; f = f->next)
|
||||||
if (f->sentto && f->new_id == id &&
|
if (f->sentto && f->new_id == id &&
|
||||||
(f->crc == crc || crc == 0xffffffff))
|
(!hash || memcmp(hash, f->hash, HASH_SIZE) == 0))
|
||||||
return f;
|
return f;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -1688,14 +1718,14 @@ static struct frec *lookup_frec(unsigned short id, unsigned int crc)
|
|||||||
|
|
||||||
static struct frec *lookup_frec_by_sender(unsigned short id,
|
static struct frec *lookup_frec_by_sender(unsigned short id,
|
||||||
union mysockaddr *addr,
|
union mysockaddr *addr,
|
||||||
unsigned int crc)
|
void *hash)
|
||||||
{
|
{
|
||||||
struct frec *f;
|
struct frec *f;
|
||||||
|
|
||||||
for(f = daemon->frec_list; f; f = f->next)
|
for(f = daemon->frec_list; f; f = f->next)
|
||||||
if (f->sentto &&
|
if (f->sentto &&
|
||||||
f->orig_id == id &&
|
f->orig_id == id &&
|
||||||
f->crc == crc &&
|
memcmp(hash, f->hash, HASH_SIZE) == 0 &&
|
||||||
sockaddr_isequal(&f->source, addr))
|
sockaddr_isequal(&f->source, addr))
|
||||||
return f;
|
return f;
|
||||||
|
|
||||||
@@ -1719,13 +1749,13 @@ void server_gone(struct server *server)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* return unique random ids. */
|
/* return unique random ids. */
|
||||||
static unsigned short get_id(unsigned int crc)
|
static unsigned short get_id(void)
|
||||||
{
|
{
|
||||||
unsigned short ret = 0;
|
unsigned short ret = 0;
|
||||||
|
|
||||||
do
|
do
|
||||||
ret = rand16();
|
ret = rand16();
|
||||||
while (lookup_frec(ret, crc));
|
while (lookup_frec(ret, NULL));
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user