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 { ...@@ -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 { ...@@ -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 ...@@ -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);
......
...@@ -1366,5 +1366,35 @@ size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, i ...@@ -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 @@ ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
} }
......
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