$NetBSD: patch-mergequeries,v 1.1 2018/09/28 20:36:24 schmonz Exp $ Address the dnscache poisoning weaknesses described in CVE-2008-4392. From Jeff King in --- clients.h.orig 2009-04-21 23:43:02.000000000 -0400 +++ clients.h @@ -0,0 +1,7 @@ +#ifndef CLIENTS_H +#define CLIENTS_H + +#define MAXUDP 200 +#define MAXTCP 20 + +#endif /* CLIENTS_H */ --- dns.h.orig 2001-02-11 16:11:45.000000000 -0500 +++ dns.h @@ -4,6 +4,7 @@ #include "stralloc.h" #include "iopause.h" #include "taia.h" +#include "clients.h" #define DNS_C_IN "\0\1" #define DNS_C_ANY "\0\377" @@ -37,8 +38,14 @@ struct dns_transmit { const char *servers; char localip[4]; char qtype[2]; + struct dns_transmit *master; + struct dns_transmit *slaves[MAXUDP]; + int nslaves; } ; +extern void dns_enable_merge(void (*logger)(const char *, const char *, + const char *)); + extern void dns_random_init(const char *); extern unsigned int dns_random(unsigned int); --- dns_transmit.c.orig 2001-02-11 16:11:45.000000000 -0500 +++ dns_transmit.c @@ -7,6 +7,61 @@ #include "byte.h" #include "uint16.h" #include "dns.h" +#include "strerr.h" + +static int merge_enable; +static void (*merge_logger)(const char *, const char *, const char *); +void dns_enable_merge(void (*f)(const char *, const char *, const char *)) +{ + merge_enable = 1; + merge_logger = f; +} + +static int merge_equal(struct dns_transmit *a, struct dns_transmit *b) +{ + const char *ip1 = a->servers + 4 * a->curserver; + const char *ip2 = b->servers + 4 * b->curserver; + return + byte_equal(ip1, 4, ip2) && + byte_equal(a->qtype, 2, b->qtype) && + dns_domain_equal(a->query + 14, b->query + 14); +} + +struct dns_transmit *inprogress[MAXUDP]; + +static int try_merge(struct dns_transmit *d) +{ + int i; + for (i = 0; i < MAXUDP; i++) { + if (!inprogress[i]) continue; + if (!merge_equal(d, inprogress[i])) continue; + d->master = inprogress[i]; + inprogress[i]->slaves[inprogress[i]->nslaves++] = d; + return 1; + } + return 0; +} + +static void register_inprogress(struct dns_transmit *d) +{ + int i; + for (i = 0; i < MAXUDP; i++) { + if (!inprogress[i]) { + inprogress[i] = d; + return; + } + } + strerr_die1x(100, "BUG: out of inprogress slots"); +} + +static void unregister_inprogress(struct dns_transmit *d) +{ + int i; + for (i = 0; i < MAXUDP; i++) { + if (inprogress[i] == d) + inprogress[i] = 0; + } +} static int serverwantstcp(const char *buf,unsigned int len) { @@ -59,8 +114,28 @@ static void packetfree(struct dns_transm d->packet = 0; } +static void mergefree(struct dns_transmit *d) +{ + int i; + if (merge_enable) + unregister_inprogress(d); + /* unregister us from our master */ + if (d->master) { + for (i = 0; i < d->master->nslaves; i++) + if (d->master->slaves[i] == d) + d->master->slaves[i] = 0; + } + /* and unregister all of our slaves from us */ + for (i = 0; i < d->nslaves; i++) { + if (d->slaves[i]) + d->slaves[i]->master = NULL; + } + d->nslaves = 0; +} + static void queryfree(struct dns_transmit *d) { + mergefree(d); if (!d->query) return; alloc_free(d->query); d->query = 0; @@ -99,11 +174,18 @@ static int thisudp(struct dns_transmit * const char *ip; socketfree(d); + mergefree(d); while (d->udploop < 4) { for (;d->curserver < 16;++d->curserver) { ip = d->servers + 4 * d->curserver; if (byte_diff(ip,4,"\0\0\0\0")) { + if (merge_enable && try_merge(d)) { + if (merge_logger) + merge_logger(ip, d->qtype, d->query + 14); + return 0; + } + d->query[2] = dns_random(256); d->query[3] = dns_random(256); @@ -118,6 +200,8 @@ static int thisudp(struct dns_transmit * taia_uint(&d->deadline,timeouts[d->udploop]); taia_add(&d->deadline,&d->deadline,&now); d->tcpstate = 0; + if (merge_enable) + register_inprogress(d); return 0; } @@ -226,8 +310,12 @@ void dns_transmit_io(struct dns_transmit x->fd = d->s1 - 1; switch(d->tcpstate) { - case 0: case 3: case 4: case 5: - x->events = IOPAUSE_READ; + case 0: + if (d->master) return; + if (d->packet) { taia_now(deadline); return; } + /* otherwise, fall through */ + case 3: case 4: case 5: + x->events = IOPAUSE_READ; break; case 1: case 2: x->events = IOPAUSE_WRITE; @@ -244,10 +332,14 @@ int dns_transmit_get(struct dns_transmit unsigned char ch; int r; int fd; + int i; errno = error_io; fd = d->s1 - 1; + if (d->tcpstate == 0 && d->master) return 0; + if (d->tcpstate == 0 && d->packet) return 1; + if (!x->revents) { if (taia_less(when,&d->deadline)) return 0; errno = error_timeout; @@ -279,6 +371,15 @@ have sent query to curserver on UDP sock d->packet = alloc(d->packetlen); if (!d->packet) { dns_transmit_free(d); return -1; } byte_copy(d->packet,d->packetlen,udpbuf); + + for (i = 0; i < d->nslaves; i++) { + if (!d->slaves[i]) continue; + d->slaves[i]->packetlen = d->packetlen; + d->slaves[i]->packet = alloc(d->packetlen); + if (!d->slaves[i]->packet) { dns_transmit_free(d->slaves[i]); continue; } + byte_copy(d->slaves[i]->packet,d->packetlen,udpbuf); + } + queryfree(d); return 1; } --- dnscache.c.orig 2001-02-11 16:11:45.000000000 -0500 +++ dnscache.c @@ -54,7 +54,6 @@ uint64 numqueries = 0; static int udp53; -#define MAXUDP 200 static struct udpclient { struct query q; struct taia start; @@ -131,7 +130,6 @@ void u_new(void) static int tcp53; -#define MAXTCP 20 struct tcpclient { struct query q; struct taia start; @@ -435,6 +433,8 @@ int main() response_hidettl(); if (env_get("FORWARDONLY")) query_forwardonly(); + if (env_get("MERGEQUERIES")) + dns_enable_merge(log_merge); if (!roots_init()) strerr_die2sys(111,FATAL,"unable to read servers: "); --- log.c.orig 2001-02-11 16:11:45.000000000 -0500 +++ log.c @@ -150,6 +150,12 @@ void log_tx(const char *q,const char qty line(); } +void log_merge(const char *addr, const char qtype[2], const char *q) +{ + string("merge "); ip(addr); space(); logtype(qtype); space(); name(q); + line(); +} + void log_cachedanswer(const char *q,const char type[2]) { string("cached "); logtype(type); space(); --- log.h.orig 2001-02-11 16:11:45.000000000 -0500 +++ log.h @@ -18,6 +18,7 @@ extern void log_cachednxdomain(const cha extern void log_cachedns(const char *,const char *); extern void log_tx(const char *,const char *,const char *,const char *,unsigned int); +extern void log_merge(const char *, const char *, const char *); extern void log_nxdomain(const char *,const char *,unsigned int); extern void log_nodata(const char *,const char *,const char *,unsigned int);