Commit 8a9be9e4 authored by Simon Kelley's avatar Simon Kelley

Replace CRC32 with SHA1 for spoof detection in DNSSEC builds.

parent c92f0083
......@@ -539,6 +539,12 @@ struct hostsfile {
#define FREC_DNSKEY_QUERY 8
#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 {
union mysockaddr source;
struct all_addr dest;
......@@ -550,9 +556,9 @@ struct frec {
unsigned int iface;
unsigned short orig_id, new_id;
int fd, forwardall, flags;
unsigned int crc;
time_t time;
#ifdef HAVE_DNSSEC
unsigned char *hash[HASH_SIZE];
#ifdef HAVE_DNSSEC
int class;
struct blockdata *stash; /* Saved reply, whilst we validate */
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_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);
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
/* util.c */
void rand_init(void);
......
......@@ -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);
}
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 */
......@@ -16,11 +16,11 @@
#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,
union mysockaddr *addr,
unsigned int crc);
static unsigned short get_id(unsigned int crc);
void *hash);
static unsigned short get_id(void);
static void free_frec(struct frec *f);
static struct randfd *allocate_rfd(int family);
......@@ -239,18 +239,23 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
char *domain = NULL;
int type = 0, norebind = 0;
struct all_addr *addrp = NULL;
unsigned int crc = questions_crc(header, plen, daemon->namebuff);
unsigned int flags = 0;
unsigned int gotname = extract_request(header, plen, daemon->namebuff, 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 */
header->hb4 &= ~HB4_AD;
/* may be no servers available. */
if (!daemon->servers)
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
/* 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->iface = dst_iface;
forward->orig_id = ntohs(header->id);
forward->new_id = get_id(crc);
forward->new_id = get_id();
forward->fd = udpfd;
forward->crc = crc;
memcpy(forward->hash, hash, HASH_SIZE);
forward->forwardall = 0;
forward->flags = 0;
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);
size_t nn;
struct server *server;
void *hash;
#ifndef HAVE_DNSSEC
unsigned int crc;
#endif
/* packet buffer overwritten */
daemon->srv_save = NULL;
......@@ -671,10 +680,17 @@ void reply_query(int fd, int family, time_t now)
break;
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 ||
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;
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,
daemon->keyname, forward->class, T_DS, &server->addr);
}
new->crc = questions_crc(header, nn, daemon->namebuff);
new->new_id = get_id(new->crc);
if ((hash = hash_questions(header, nn, daemon->namebuff)))
memcpy(new->hash, hash, HASH_SIZE);
new->new_id = get_id();
header->id = htons(new->new_id);
/* Save query for retransmission */
new->stash = blockdata_alloc((char *)header, nn);
......@@ -1357,8 +1374,13 @@ unsigned char *tcp_request(int confd, time_t now,
if (!flags && last_server)
{
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);
#endif
/* Loop round available servers until we succeed in connecting to one.
Note that this code subtley ensures that consecutive queries on this connection
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
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */
if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
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);
#ifdef HAVE_DNSSEC
newhash = hash_questions(header, (unsigned int)m, daemon->namebuff);
if (!newhash || memcmp(hash, newhash, HASH_SIZE) != 0)
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;
}
......@@ -1674,13 +1704,13 @@ struct frec *get_new_frec(time_t now, int *wait, int force)
}
/* 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;
for(f = daemon->frec_list; f; f = f->next)
if (f->sentto && f->new_id == id &&
(f->crc == crc || crc == 0xffffffff))
(!hash || memcmp(hash, f->hash, HASH_SIZE) == 0))
return f;
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,
union mysockaddr *addr,
unsigned int crc)
void *hash)
{
struct frec *f;
for(f = daemon->frec_list; f; f = f->next)
if (f->sentto &&
f->orig_id == id &&
f->crc == crc &&
memcmp(hash, f->hash, HASH_SIZE) == 0 &&
sockaddr_isequal(&f->source, addr))
return f;
......@@ -1719,13 +1749,13 @@ void server_gone(struct server *server)
}
/* return unique random ids. */
static unsigned short get_id(unsigned int crc)
static unsigned short get_id(void)
{
unsigned short ret = 0;
do
ret = rand16();
while (lookup_frec(ret, crc));
while (lookup_frec(ret, NULL));
return ret;
}
......
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