diff --git a/SOURCES/dnsmasq-2.76-nettlehash.patch b/SOURCES/dnsmasq-2.76-nettlehash.patch new file mode 100644 index 0000000..26cd9df --- /dev/null +++ b/SOURCES/dnsmasq-2.76-nettlehash.patch @@ -0,0 +1,203 @@ +From ca1e22ee7d4e36204ec665305962e895ad63081e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= +Date: Wed, 25 Nov 2020 17:18:55 +0100 +Subject: [PATCH] Support hash function from nettle (only) + +Unlike COPTS=-DHAVE_DNSSEC, allow usage of just sha256 function from +nettle, but keep DNSSEC disabled at build time. Skips use of internal +hash implementation without support for validation built-in. +--- + Makefile | 6 ++++-- + bld/pkg-wrapper | 39 +++++++++++++++++++++------------------ + src/config.h | 8 ++++++++ + src/dnssec.c | 17 +++++++++++++++-- + src/hash_questions.c | 7 ++++++- + 5 files changed, 54 insertions(+), 23 deletions(-) + +diff --git a/Makefile b/Makefile +index 8a3f2e2..9890ae1 100644 +--- a/Makefile ++++ b/Makefile +@@ -59,8 +59,10 @@ ct_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CON + ct_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack` + lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.1` + lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua5.1` +-nettle_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags nettle hogweed` +-nettle_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --libs nettle hogweed` ++nettle_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags 'nettle hogweed' \ ++ HAVE_NETTLEHASH $(PKG_CONFIG) --cflags nettle` ++nettle_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --libs 'nettle hogweed' \ ++ HAVE_NETTLEHASH $(PKG_CONFIG) --libs nettle` + gmp_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC NO_GMP --copy -lgmp` + sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi` + version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"' +diff --git a/bld/pkg-wrapper b/bld/pkg-wrapper +index 0ddb678..3478962 100755 +--- a/bld/pkg-wrapper ++++ b/bld/pkg-wrapper +@@ -1,33 +1,35 @@ + #!/bin/sh + +-search=$1 +-shift +-pkg=$1 +-shift +-op=$1 +-shift +- + in=`cat` + +-if grep "^\#[[:space:]]*define[[:space:]]*$search" config.h >/dev/null 2>&1 || \ +- echo $in | grep $search >/dev/null 2>&1; then ++search() ++{ ++ grep "^\#[[:space:]]*define[[:space:]]*$1" config.h >/dev/null 2>&1 || \ ++ echo $in | grep $1 >/dev/null 2>&1 ++} ++ ++while [ "$#" -gt 0 ]; do ++ search=$1 ++ pkg=$2 ++ op=$3 ++ lib=$4 ++ shift 4 ++if search "$search"; then ++ + # Nasty, nasty, in --copy, arg 2 is another config to search for, use with NO_GMP + if [ $op = "--copy" ]; then +- if grep "^\#[[:space:]]*define[[:space:]]*$pkg" config.h >/dev/null 2>&1 || \ +- echo $in | grep $pkg >/dev/null 2>&1; then ++ if search "$pkg"; then + pkg="" + else +- pkg="$*" ++ pkg="$lib" + fi +- elif grep "^\#[[:space:]]*define[[:space:]]*${search}_STATIC" config.h >/dev/null 2>&1 || \ +- echo $in | grep ${search}_STATIC >/dev/null 2>&1; then +- pkg=`$pkg --static $op $*` ++ elif search "${search}_STATIC"; then ++ pkg=`$pkg --static $op $lib` + else +- pkg=`$pkg $op $*` ++ pkg=`$pkg $op $lib` + fi + +- if grep "^\#[[:space:]]*define[[:space:]]*${search}_STATIC" config.h >/dev/null 2>&1 || \ +- echo $in | grep ${search}_STATIC >/dev/null 2>&1; then ++ if search "${search}_STATIC"; then + if [ $op = "--libs" ] || [ $op = "--copy" ]; then + echo "-Wl,-Bstatic $pkg -Wl,-Bdynamic" + else +@@ -38,3 +40,4 @@ if grep "^\#[[:space:]]*define[[:space:]]*$search" config.h >/dev/null 2>&1 || \ + fi + fi + ++done +diff --git a/src/config.h b/src/config.h +index 80a50e1..077147a 100644 +--- a/src/config.h ++++ b/src/config.h +@@ -111,6 +111,9 @@ HAVE_AUTH + define this to include the facility to act as an authoritative DNS + server for one or more zones. + ++HAVE_NETTLEHASH ++ include just hash function from nettle, but no DNSSEC. ++ + HAVE_DNSSEC + include DNSSEC validator. + +@@ -174,6 +177,7 @@ RESOLVFILE + /* #define HAVE_DBUS */ + /* #define HAVE_IDN */ + /* #define HAVE_CONNTRACK */ ++/* #define HAVE_NETTLEHASH */ + /* #define HAVE_DNSSEC */ + + +@@ -430,6 +434,10 @@ static char *compile_opts = + "no-" + #endif + "auth " ++#if !defined(HAVE_NETTLEHASH) && !defined(HAVE_DNSSEC) ++"no-" ++#endif ++"nettlehash " + #ifndef HAVE_DNSSEC + "no-" + #endif +diff --git a/src/dnssec.c b/src/dnssec.c +index f22faa1..d07cee9 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -25,8 +25,14 @@ + # include + # include + #endif ++#endif ++ ++#if defined(HAVE_DNSSEC) || defined(HAVE_NETTLEHASH) + #include + #include ++#endif ++ ++#ifdef HAVE_DNSSEC + + /* Nettle-3.0 moved to a new API for DSA. We use a name that's defined in the new API + to detect Nettle-3, and invoke the backwards compatibility mode. */ +@@ -80,9 +86,12 @@ static char *nsec3_digest_name(int digest) + default: return NULL; + } + } ++#endif ++ ++#if defined(HAVE_DNSSEC) || defined(HAVE_NETTLEHASH) + + /* Find pointer to correct hash function in nettle library */ +-static const struct nettle_hash *hash_find(char *name) ++const struct nettle_hash *hash_find(char *name) + { + int i; + +@@ -99,7 +108,7 @@ static const struct nettle_hash *hash_find(char *name) + } + + /* expand ctx and digest memory allocations if necessary and init hash function */ +-static int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **digestp) ++int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **digestp) + { + static void *ctx = NULL; + static unsigned char *digest = NULL; +@@ -135,6 +144,10 @@ static int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char + + return 1; + } ++ ++#endif ++ ++#ifdef HAVE_DNSSEC + + static int dnsmasq_rsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len, + unsigned char *digest, size_t digest_len, int algo) +diff --git a/src/hash_questions.c b/src/hash_questions.c +index ae112ac..0d25359 100644 +--- a/src/hash_questions.c ++++ b/src/hash_questions.c +@@ -28,7 +28,12 @@ + + #include "dnsmasq.h" + +-#ifdef HAVE_DNSSEC ++#if defined(HAVE_DNSSEC) || defined(HAVE_NETTLEHASH) ++#include ++ ++const struct nettle_hash *hash_find(char *name); ++int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **digestp); ++ + unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name) + { + int q; +-- +2.26.2 + diff --git a/SOURCES/dnsmasq-2.79-CVE-2020-25684.patch b/SOURCES/dnsmasq-2.79-CVE-2020-25684.patch new file mode 100644 index 0000000..374b082 --- /dev/null +++ b/SOURCES/dnsmasq-2.79-CVE-2020-25684.patch @@ -0,0 +1,94 @@ +From 40d08c8b1de73d74701b52e59baeb9bb5652cb9e Mon Sep 17 00:00:00 2001 +From: Simon Kelley +Date: Thu, 12 Nov 2020 18:49:23 +0000 +Subject: [PATCH] Check destination of DNS UDP query replies. + +At any time, dnsmasq will have a set of sockets open, bound to +random ports, on which it sends queries to upstream nameservers. +This patch fixes the existing problem that a reply for ANY in-flight +query would be accepted via ANY open port, which increases the +chances of an attacker flooding answers "in the blind" in an +attempt to poison the DNS cache. CERT VU#434904 refers. +--- + src/forward.c | 37 ++++++++++++++++++++++++++++--------- + 1 file changed, 28 insertions(+), 9 deletions(-) + +diff --git a/src/forward.c b/src/forward.c +index 720b503..e210864 100644 +--- a/src/forward.c ++++ b/src/forward.c +@@ -16,7 +16,7 @@ + + #include "dnsmasq.h" + +-static struct frec *lookup_frec(unsigned short id, void *hash); ++static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash); + static struct frec *lookup_frec_by_sender(unsigned short id, + union mysockaddr *addr, + void *hash); +@@ -779,7 +779,7 @@ void reply_query(int fd, int family, time_t now) + crc = questions_crc(header, n, daemon->namebuff); + #endif + +- if (!(forward = lookup_frec(ntohs(header->id), hash))) ++ if (!(forward = lookup_frec(ntohs(header->id), fd, family, hash))) + return; + + /* log_query gets called indirectly all over the place, so +@@ -2174,14 +2174,25 @@ 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, void *hash) ++static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash) + { + struct frec *f; + + for(f = daemon->frec_list; f; f = f->next) + if (f->sentto && f->new_id == id && + (!hash || memcmp(hash, f->hash, HASH_SIZE) == 0)) +- return f; ++ { ++ /* sent from random port */ ++ if (family == AF_INET && f->rfd4 && f->rfd4->fd == fd) ++ return f; ++ ++ if (family == AF_INET6 && f->rfd6 && f->rfd6->fd == fd) ++ return f; ++ ++ /* sent to upstream from bound socket. */ ++ if (f->sentto->sfd && f->sentto->sfd->fd == fd) ++ return f; ++ } + + return NULL; + } +@@ -2242,12 +2253,20 @@ void server_gone(struct server *server) + static unsigned short get_id(void) + { + unsigned short ret = 0; ++ struct frec *f; + +- do +- ret = rand16(); +- while (lookup_frec(ret, NULL)); +- +- return ret; ++ while (1) ++ { ++ ret = rand16(); ++ ++ /* ensure id is unique. */ ++ for (f = daemon->frec_list; f; f = f->next) ++ if (f->sentto && f->new_id == ret) ++ break; ++ ++ if (!f) ++ return ret; ++ } + } + + +-- +2.26.2 + diff --git a/SOURCES/dnsmasq-2.79-CVE-2020-25685.patch b/SOURCES/dnsmasq-2.79-CVE-2020-25685.patch new file mode 100644 index 0000000..89267bc --- /dev/null +++ b/SOURCES/dnsmasq-2.79-CVE-2020-25685.patch @@ -0,0 +1,584 @@ +From 95272dbce5e9118f652f3f714fb31f6ad70c26a2 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +Date: Thu, 12 Nov 2020 22:06:07 +0000 +Subject: [PATCH] Use SHA-256 to provide security against DNS cache poisoning. + +Use the SHA-256 hash function to verify that DNS answers +received are for the questions originally asked. This replaces +the slightly insecure SHA-1 (when compiled with DNSSEC) or +the very insecure CRC32 (otherwise). Refer: CERT VU#434904. +--- + Makefile | 2 +- + bld/Android.mk | 3 +- + src/dnsmasq.h | 11 +- + src/dnssec.c | 31 ----- + src/forward.c | 43 ++----- + src/hash_questions.c | 281 +++++++++++++++++++++++++++++++++++++++++++ + src/rfc1035.c | 49 -------- + 7 files changed, 296 insertions(+), 124 deletions(-) + create mode 100644 src/hash_questions.c + +diff --git a/Makefile b/Makefile +index dd0513b..8a3f2e2 100644 +--- a/Makefile ++++ b/Makefile +@@ -74,7 +74,7 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \ + helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \ + dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \ + domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \ +- poll.o rrfilter.o edns0.o arp.o ++ poll.o rrfilter.o edns0.o arp.o hash_questions.o + + hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ + dns-protocol.h radv-protocol.h ip6addr.h +diff --git a/bld/Android.mk b/bld/Android.mk +index eafef35..729fd17 100644 +--- a/bld/Android.mk ++++ b/bld/Android.mk +@@ -10,7 +10,8 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \ + dhcp6.c rfc3315.c dhcp-common.c outpacket.c \ + radv.c slaac.c auth.c ipset.c domain.c \ + dnssec.c dnssec-openssl.c blockdata.c tables.c \ +- loop.c inotify.c poll.c rrfilter.c edns0.c arp.c ++ loop.c inotify.c poll.c rrfilter.c edns0.c arp.c \ ++ hash_questions.c + + LOCAL_MODULE := dnsmasq + +diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 3b3f6ef..9f0af7f 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -599,11 +599,7 @@ struct hostsfile { + #define FREC_TEST_PKTSZ 256 + #define FREC_HAS_EXTRADATA 512 + +-#ifdef HAVE_DNSSEC +-#define HASH_SIZE 20 /* SHA-1 digest size */ +-#else +-#define HASH_SIZE sizeof(int) +-#endif ++#define HASH_SIZE 32 /* SHA-256 digest size */ + + struct frec { + union mysockaddr source; +@@ -1128,7 +1124,6 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, + struct bogus_addr *addr, time_t now); + int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr); + int check_for_local_domain(char *name, time_t now); +-unsigned int questions_crc(struct dns_header *header, size_t plen, char *buff); + size_t resize_packet(struct dns_header *header, size_t plen, + unsigned char *pheader, size_t hlen); + int add_resource_record(struct dns_header *header, char *limit, int *truncp, +@@ -1156,9 +1151,11 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch + int check_unsigned, int *neganswer, int *nons, char *rr_status); + int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen); + size_t filter_rrsigs(struct dns_header *header, size_t plen); +-unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name); + int setup_timestamp(void); + ++/* hash_questions.c */ ++unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name); ++ + /* util.c */ + void rand_init(void); + unsigned short rand16(void); +diff --git a/src/dnssec.c b/src/dnssec.c +index 3121eb1..f22faa1 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -2268,35 +2268,4 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char + return ret; + } + +-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)) +- break; /* 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)) +- break; /* bad packet */ +- } +- +- hash->digest(ctx, hash->digest_size, digest); +- return digest; +-} +- + #endif /* HAVE_DNSSEC */ +diff --git a/src/forward.c b/src/forward.c +index e210864..596f593 100644 +--- a/src/forward.c ++++ b/src/forward.c +@@ -237,19 +237,16 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + struct all_addr *addrp = NULL; + unsigned int flags = 0; + struct server *start = NULL; +-#ifdef HAVE_DNSSEC + void *hash = hash_questions(header, plen, daemon->namebuff); ++#ifdef HAVE_DNSSEC + int do_dnssec = 0; +-#else +- unsigned int crc = questions_crc(header, plen, daemon->namebuff); +- void *hash = &crc; + #endif + unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL); + + (void)do_bit; + + /* may be no servers available. */ +- if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash)))) ++ if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))) + { + /* If we didn't get an answer advertising a maximal packet in EDNS, + fall back to 1280, which should work everywhere on IPv6. +@@ -744,9 +741,6 @@ void reply_query(int fd, int family, time_t now) + size_t nn; + struct server *server; + void *hash; +-#ifndef HAVE_DNSSEC +- unsigned int crc; +-#endif + + /* packet buffer overwritten */ + daemon->srv_save = NULL; +@@ -772,12 +766,7 @@ void reply_query(int fd, int family, time_t now) + if (!server) + return; + +-#ifdef HAVE_DNSSEC + hash = hash_questions(header, n, daemon->namebuff); +-#else +- hash = &crc; +- crc = questions_crc(header, n, daemon->namebuff); +-#endif + + if (!(forward = lookup_frec(ntohs(header->id), fd, family, hash))) + return; +@@ -1001,8 +990,7 @@ void reply_query(int fd, int family, time_t now) + nn = dnssec_generate_query(header,((unsigned char *) header) + server->edns_pktsz, + daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz); + } +- if ((hash = hash_questions(header, nn, daemon->namebuff))) +- memcpy(new->hash, hash, HASH_SIZE); ++ memcpy(new->hash, hash_questions(header, nn, daemon->namebuff), HASH_SIZE); + new->new_id = get_id(); + header->id = htons(new->new_id); + /* Save query for retransmission */ +@@ -1814,15 +1802,9 @@ 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->namebuff))) +- memcpy(hash, newhash, HASH_SIZE); +- else +- memset(hash, 0, HASH_SIZE); +-#else +- unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff); +-#endif ++ unsigned char hash[HASH_SIZE]; ++ memcpy(hash, hash_questions(header, (unsigned int)size, daemon->namebuff), HASH_SIZE); ++ + /* 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. */ +@@ -1954,20 +1936,11 @@ 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. */ +-#ifdef HAVE_DNSSEC +- newhash = hash_questions(header, (unsigned int)m, daemon->namebuff); +- if (!newhash || memcmp(hash, newhash, HASH_SIZE) != 0) ++ if (memcmp(hash, hash_questions(header, (unsigned int)m, daemon->namebuff), HASH_SIZE) != 0) + { + m = 0; + break; + } +-#else +- if (crc != questions_crc(header, (unsigned int)m, daemon->namebuff)) +- { +- m = 0; +- break; +- } +-#endif + + m = process_reply(header, now, last_server, (unsigned int)m, + option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer, +@@ -2180,7 +2153,7 @@ static struct frec *lookup_frec(unsigned short id, int fd, int family, void *has + + for(f = daemon->frec_list; f; f = f->next) + if (f->sentto && f->new_id == id && +- (!hash || memcmp(hash, f->hash, HASH_SIZE) == 0)) ++ (memcmp(hash, f->hash, HASH_SIZE) == 0)) + { + /* sent from random port */ + if (family == AF_INET && f->rfd4 && f->rfd4->fd == fd) +diff --git a/src/hash_questions.c b/src/hash_questions.c +new file mode 100644 +index 0000000..ae112ac +--- /dev/null ++++ b/src/hash_questions.c +@@ -0,0 +1,281 @@ ++/* Copyright (c) 2012-2020 Simon Kelley ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; version 2 dated June, 1991, or ++ (at your option) version 3 dated 29 June, 2007. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++ ++/* Hash the question section. This is used to safely detect query ++ retransmission and to detect answers to questions we didn't ask, which ++ might be poisoning attacks. Note that we decode the name rather ++ than CRC the raw bytes, since replies might be compressed differently. ++ We ignore case in the names for the same reason. ++ ++ The hash used is SHA-256. If we're building with DNSSEC support, ++ we use the Nettle cypto library. If not, we prefer not to ++ add a dependency on Nettle, and use a stand-alone implementaion. ++*/ ++ ++#include "dnsmasq.h" ++ ++#ifdef HAVE_DNSSEC ++unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name) ++{ ++ int q; ++ unsigned char *p = (unsigned char *)(header+1); ++ const struct nettle_hash *hash; ++ void *ctx; ++ unsigned char *digest; ++ ++ if (!(hash = hash_find("sha256")) || !hash_init(hash, &ctx, &digest)) ++ { ++ /* don't think this can ever happen. */ ++ static unsigned char dummy[HASH_SIZE]; ++ static int warned = 0; ++ ++ if (warned) ++ my_syslog(LOG_ERR, _("Failed to create SHA-256 hash object")); ++ warned = 1; ++ ++ return dummy; ++ } ++ ++ for (q = ntohs(header->qdcount); q != 0; q--) ++ { ++ char *cp, c; ++ ++ if (!extract_name(header, plen, &p, name, 1, 4)) ++ break; /* bad packet */ ++ ++ for (cp = name; (c = *cp); cp++) ++ if (c >= 'A' && c <= 'Z') ++ *cp += 'a' - 'A'; ++ ++ hash->update(ctx, cp - name, (unsigned char *)name); ++ /* CRC the class and type as well */ ++ hash->update(ctx, 4, p); ++ ++ p += 4; ++ if (!CHECK_LEN(header, p, plen, 0)) ++ break; /* bad packet */ ++ } ++ ++ hash->digest(ctx, hash->digest_size, digest); ++ return digest; ++} ++ ++#else /* HAVE_DNSSEC */ ++ ++#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest ++typedef unsigned char BYTE; // 8-bit byte ++typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines ++ ++typedef struct { ++ BYTE data[64]; ++ WORD datalen; ++ unsigned long long bitlen; ++ WORD state[8]; ++} SHA256_CTX; ++ ++static void sha256_init(SHA256_CTX *ctx); ++static void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); ++static void sha256_final(SHA256_CTX *ctx, BYTE hash[]); ++ ++ ++unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name) ++{ ++ int q; ++ unsigned char *p = (unsigned char *)(header+1); ++ SHA256_CTX ctx; ++ static BYTE digest[SHA256_BLOCK_SIZE]; ++ ++ sha256_init(&ctx); ++ ++ for (q = ntohs(header->qdcount); q != 0; q--) ++ { ++ char *cp, c; ++ ++ if (!extract_name(header, plen, &p, name, 1, 4)) ++ break; /* bad packet */ ++ ++ for (cp = name; (c = *cp); cp++) ++ if (c >= 'A' && c <= 'Z') ++ *cp += 'a' - 'A'; ++ ++ sha256_update(&ctx, (BYTE *)name, cp - name); ++ /* CRC the class and type as well */ ++ sha256_update(&ctx, (BYTE *)p, 4); ++ ++ p += 4; ++ if (!CHECK_LEN(header, p, plen, 0)) ++ break; /* bad packet */ ++ } ++ ++ sha256_final(&ctx, digest); ++ return (unsigned char *)digest; ++} ++ ++/* Code from here onwards comes from https://github.com/B-Con/crypto-algorithms ++ and was written by Brad Conte (brad@bradconte.com), to whom all credit is given. ++ ++ This code is in the public domain, and the copyright notice at the head of this ++ file does not apply to it. ++*/ ++ ++ ++/****************************** MACROS ******************************/ ++#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) ++#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) ++ ++#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) ++#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) ++#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) ++#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) ++#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) ++#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) ++ ++/**************************** VARIABLES *****************************/ ++static const WORD k[64] = { ++ 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, ++ 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, ++ 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, ++ 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, ++ 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, ++ 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, ++ 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, ++ 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 ++}; ++ ++/*********************** FUNCTION DEFINITIONS ***********************/ ++static void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) ++{ ++ WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; ++ ++ for (i = 0, j = 0; i < 16; ++i, j += 4) ++ m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); ++ for ( ; i < 64; ++i) ++ m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; ++ ++ a = ctx->state[0]; ++ b = ctx->state[1]; ++ c = ctx->state[2]; ++ d = ctx->state[3]; ++ e = ctx->state[4]; ++ f = ctx->state[5]; ++ g = ctx->state[6]; ++ h = ctx->state[7]; ++ ++ for (i = 0; i < 64; ++i) ++ { ++ t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; ++ t2 = EP0(a) + MAJ(a,b,c); ++ h = g; ++ g = f; ++ f = e; ++ e = d + t1; ++ d = c; ++ c = b; ++ b = a; ++ a = t1 + t2; ++ } ++ ++ ctx->state[0] += a; ++ ctx->state[1] += b; ++ ctx->state[2] += c; ++ ctx->state[3] += d; ++ ctx->state[4] += e; ++ ctx->state[5] += f; ++ ctx->state[6] += g; ++ ctx->state[7] += h; ++} ++ ++static void sha256_init(SHA256_CTX *ctx) ++{ ++ ctx->datalen = 0; ++ ctx->bitlen = 0; ++ ctx->state[0] = 0x6a09e667; ++ ctx->state[1] = 0xbb67ae85; ++ ctx->state[2] = 0x3c6ef372; ++ ctx->state[3] = 0xa54ff53a; ++ ctx->state[4] = 0x510e527f; ++ ctx->state[5] = 0x9b05688c; ++ ctx->state[6] = 0x1f83d9ab; ++ ctx->state[7] = 0x5be0cd19; ++} ++ ++static void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) ++{ ++ WORD i; ++ ++ for (i = 0; i < len; ++i) ++ { ++ ctx->data[ctx->datalen] = data[i]; ++ ctx->datalen++; ++ if (ctx->datalen == 64) { ++ sha256_transform(ctx, ctx->data); ++ ctx->bitlen += 512; ++ ctx->datalen = 0; ++ } ++ } ++} ++ ++static void sha256_final(SHA256_CTX *ctx, BYTE hash[]) ++{ ++ WORD i; ++ ++ i = ctx->datalen; ++ ++ // Pad whatever data is left in the buffer. ++ if (ctx->datalen < 56) ++ { ++ ctx->data[i++] = 0x80; ++ while (i < 56) ++ ctx->data[i++] = 0x00; ++ } ++ else ++ { ++ ctx->data[i++] = 0x80; ++ while (i < 64) ++ ctx->data[i++] = 0x00; ++ sha256_transform(ctx, ctx->data); ++ memset(ctx->data, 0, 56); ++ } ++ ++ // Append to the padding the total message's length in bits and transform. ++ ctx->bitlen += ctx->datalen * 8; ++ ctx->data[63] = ctx->bitlen; ++ ctx->data[62] = ctx->bitlen >> 8; ++ ctx->data[61] = ctx->bitlen >> 16; ++ ctx->data[60] = ctx->bitlen >> 24; ++ ctx->data[59] = ctx->bitlen >> 32; ++ ctx->data[58] = ctx->bitlen >> 40; ++ ctx->data[57] = ctx->bitlen >> 48; ++ ctx->data[56] = ctx->bitlen >> 56; ++ sha256_transform(ctx, ctx->data); ++ ++ // Since this implementation uses little endian byte ordering and SHA uses big endian, ++ // reverse all the bytes when copying the final state to the output hash. ++ for (i = 0; i < 4; ++i) ++ { ++ hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; ++ hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; ++ hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; ++ hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; ++ hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; ++ hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; ++ hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; ++ hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; ++ } ++} ++ ++#endif +diff --git a/src/rfc1035.c b/src/rfc1035.c +index ae2cc96..b386e5b 100644 +--- a/src/rfc1035.c ++++ b/src/rfc1035.c +@@ -335,55 +335,6 @@ unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *h + return ansp; + } + +-/* CRC the question section. This is used to safely detect query +- retransmision and to detect answers to questions we didn't ask, which +- might be poisoning attacks. Note that we decode the name rather +- than CRC the raw bytes, since replies might be compressed differently. +- We ignore case in the names for the same reason. Return all-ones +- if there is not question section. */ +-#ifndef HAVE_DNSSEC +-unsigned int questions_crc(struct dns_header *header, size_t plen, char *name) +-{ +- int q; +- unsigned int crc = 0xffffffff; +- unsigned char *p1, *p = (unsigned char *)(header+1); +- +- for (q = ntohs(header->qdcount); q != 0; q--) +- { +- if (!extract_name(header, plen, &p, name, 1, 4)) +- return crc; /* bad packet */ +- +- for (p1 = (unsigned char *)name; *p1; p1++) +- { +- int i = 8; +- char c = *p1; +- +- if (c >= 'A' && c <= 'Z') +- c += 'a' - 'A'; +- +- crc ^= c << 24; +- while (i--) +- crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1; +- } +- +- /* CRC the class and type as well */ +- for (p1 = p; p1 < p+4; p1++) +- { +- int i = 8; +- crc ^= *p1 << 24; +- while (i--) +- crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1; +- } +- +- p += 4; +- if (!CHECK_LEN(header, p, plen, 0)) +- return crc; /* bad packet */ +- } +- +- return crc; +-} +-#endif +- + size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen) + { + unsigned char *ansp = skip_questions(header, plen); +-- +2.26.2 + diff --git a/SOURCES/dnsmasq-2.79-CVE-2020-25686.patch b/SOURCES/dnsmasq-2.79-CVE-2020-25686.patch new file mode 100644 index 0000000..d6d599a --- /dev/null +++ b/SOURCES/dnsmasq-2.79-CVE-2020-25686.patch @@ -0,0 +1,330 @@ +From 312723debac9b9c328c9399a191a5fc637f56e73 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +Date: Wed, 18 Nov 2020 18:34:55 +0000 +Subject: [PATCH] Handle multiple identical near simultaneous DNS queries + better. + +Previously, such queries would all be forwarded +independently. This is, in theory, inefficent but in practise +not a problem, _except_ that is means that an answer for any +of the forwarded queries will be accepted and cached. +An attacker can send a query multiple times, and for each repeat, +another {port, ID} becomes capable of accepting the answer he is +sending in the blind, to random IDs and ports. The chance of a +succesful attack is therefore multiplied by the number of repeats +of the query. The new behaviour detects repeated queries and +merely stores the clients sending repeats so that when the +first query completes, the answer can be sent to all the +clients who asked. Refer: CERT VU#434904. +--- + src/dnsmasq.h | 19 ++++--- + src/forward.c | 144 ++++++++++++++++++++++++++++++++++++++++++-------- + 2 files changed, 135 insertions(+), 28 deletions(-) + +diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 9f0af7f..9f5b48b 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -597,21 +597,26 @@ struct hostsfile { + #define FREC_DO_QUESTION 64 + #define FREC_ADDED_PHEADER 128 + #define FREC_TEST_PKTSZ 256 +-#define FREC_HAS_EXTRADATA 512 ++#define FREC_HAS_EXTRADATA 512 ++#define FREC_HAS_PHEADER 1024 + + #define HASH_SIZE 32 /* SHA-256 digest size */ + + struct frec { +- union mysockaddr source; +- struct all_addr dest; ++ struct frec_src { ++ union mysockaddr source; ++ struct all_addr dest; ++ unsigned int iface, log_id; ++ unsigned short orig_id; ++ struct frec_src *next; ++ } frec_src; + struct server *sentto; /* NULL means free */ + struct randfd *rfd4; + #ifdef HAVE_IPV6 + struct randfd *rfd6; + #endif +- unsigned int iface; +- unsigned short orig_id, new_id; +- int log_id, fd, forwardall, flags; ++ unsigned short new_id; ++ int fd, forwardall, flags; + time_t time; + unsigned char *hash[HASH_SIZE]; + #ifdef HAVE_DNSSEC +@@ -1007,6 +1012,8 @@ extern struct daemon { + #endif + unsigned int local_answer, queries_forwarded, auth_answer; + struct frec *frec_list; ++ struct frec_src *free_frec_src; ++ int frec_src_count; + struct serverfd *sfds; + struct irec *interfaces; + struct listener *listeners; +diff --git a/src/forward.c b/src/forward.c +index 596f593..a0b1f1d 100644 +--- a/src/forward.c ++++ b/src/forward.c +@@ -20,6 +20,8 @@ static struct frec *lookup_frec(unsigned short id, int fd, int family, void *has + static struct frec *lookup_frec_by_sender(unsigned short id, + union mysockaddr *addr, + void *hash); ++static struct frec *lookup_frec_by_query(void *hash, unsigned int flags); ++ + static unsigned short get_id(void); + static void free_frec(struct frec *f); + +@@ -236,15 +238,28 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + int type = SERV_DO_DNSSEC, norebind = 0; + struct all_addr *addrp = NULL; + unsigned int flags = 0; ++ unsigned int fwd_flags = 0; + struct server *start = NULL; + void *hash = hash_questions(header, plen, daemon->namebuff); + #ifdef HAVE_DNSSEC + int do_dnssec = 0; + #endif +- unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL); ++ unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL); ++ unsigned char *oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL); + + (void)do_bit; +- ++ ++ if (header->hb4 & HB4_CD) ++ fwd_flags |= FREC_CHECKING_DISABLED; ++ if (ad_reqd) ++ fwd_flags |= FREC_AD_QUESTION; ++ if (oph) ++ fwd_flags |= FREC_HAS_PHEADER; ++#ifdef HAVE_DNSSEC ++ if (do_bit) ++ fwd_flags |= FREC_DO_QUESTION; ++#endif ++ + /* may be no servers available. */ + if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))) + { +@@ -321,6 +336,41 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + } + else + { ++ /* Query from new source, but the same query may be in progress ++ from another source. If so, just add this client to the ++ list that will get the reply. ++ ++ Note that is the EDNS client subnet option is in use, we can't do this, ++ as the clients (and therefore query EDNS options) will be different ++ for each query. The EDNS subnet code has checks to avoid ++ attacks in this case. */ ++ if (!option_bool(OPT_CLIENT_SUBNET) && (forward = lookup_frec_by_query(hash, fwd_flags))) ++ { ++ if (!daemon->free_frec_src && ++ daemon->frec_src_count < daemon->ftabsize && ++ (daemon->free_frec_src = whine_malloc(sizeof(struct frec_src)))) ++ { ++ daemon->frec_src_count++; ++ daemon->free_frec_src->next = NULL; ++ } ++ ++ /* If we've been spammed with many duplicates, just drop the query. */ ++ if (daemon->free_frec_src) ++ { ++ struct frec_src *new = daemon->free_frec_src; ++ daemon->free_frec_src = new->next; ++ new->next = forward->frec_src.next; ++ forward->frec_src.next = new; ++ new->orig_id = ntohs(header->id); ++ new->source = *udpaddr; ++ new->dest = *dst_addr; ++ new->log_id = daemon->log_id; ++ new->iface = dst_iface; ++ } ++ ++ return 1; ++ } ++ + if (gotname) + flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind); + +@@ -328,22 +378,23 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + do_dnssec = type & SERV_DO_DNSSEC; + #endif + type &= ~SERV_DO_DNSSEC; +- ++ + if (daemon->servers && !flags) + forward = get_new_frec(now, NULL, 0); + /* table full - flags == 0, return REFUSED */ + + if (forward) + { +- forward->source = *udpaddr; +- forward->dest = *dst_addr; +- forward->iface = dst_iface; +- forward->orig_id = ntohs(header->id); ++ forward->frec_src.source = *udpaddr; ++ forward->frec_src.orig_id = ntohs(header->id); ++ forward->frec_src.dest = *dst_addr; ++ forward->frec_src.iface = dst_iface; ++ forward->frec_src.next = NULL; + forward->new_id = get_id(); + forward->fd = udpfd; + memcpy(forward->hash, hash, HASH_SIZE); + forward->forwardall = 0; +- forward->flags = 0; ++ forward->flags = fwd_flags; + if (norebind) + forward->flags |= FREC_NOREBIND; + if (header->hb4 & HB4_CD) +@@ -397,9 +448,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + size_t edns0_len; + + /* If a query is retried, use the log_id for the retry when logging the answer. */ +- forward->log_id = daemon->log_id; ++ forward->frec_src.log_id = daemon->log_id; + +- edns0_len = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->source, now, &subnet); ++ edns0_len = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->frec_src.source, now, &subnet); + + if (edns0_len != plen) + { +@@ -536,7 +587,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + return 1; + + /* could not send on, prepare to return */ +- header->id = htons(forward->orig_id); ++ header->id = htons(forward->frec_src.orig_id); + free_frec(forward); /* cancel */ + } + +@@ -773,8 +824,8 @@ void reply_query(int fd, int family, time_t now) + + /* log_query gets called indirectly all over the place, so + pass these in global variables - sorry. */ +- daemon->log_display_id = forward->log_id; +- daemon->log_source_addr = &forward->source; ++ daemon->log_display_id = forward->frec_src.log_id; ++ daemon->log_source_addr = &forward->frec_src.source; + + if (daemon->ignore_addr && RCODE(header) == NOERROR && + check_for_ignored_address(header, n, daemon->ignore_addr)) +@@ -973,6 +1024,7 @@ void reply_query(int fd, int family, time_t now) + #ifdef HAVE_IPV6 + new->rfd6 = NULL; + #endif ++ new->frec_src.next = NULL; + new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY); + + new->dependent = forward; /* to find query awaiting new one. */ +@@ -1094,9 +1146,11 @@ void reply_query(int fd, int family, time_t now) + + if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer, + forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, +- forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->source))) ++ forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->frec_src.source))) + { +- header->id = htons(forward->orig_id); ++ struct frec_src *src; ++ ++ header->id = htons(forward->frec_src.orig_id); + header->hb4 |= HB4_RA; /* recursion if available */ + #ifdef HAVE_DNSSEC + /* We added an EDNSO header for the purpose of getting DNSSEC RRs, and set the value of the UDP payload size +@@ -1111,9 +1165,22 @@ void reply_query(int fd, int family, time_t now) + nn = resize_packet(header, nn, NULL, 0); + } + #endif +- send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, +- &forward->source, &forward->dest, forward->iface); ++ for (src = &forward->frec_src; src; src = src->next) ++ { ++ header->id = htons(src->orig_id); ++ ++ send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, ++ &src->source, &src->dest, src->iface); ++ ++ if (option_bool(OPT_EXTRALOG) && src != &forward->frec_src) ++ { ++ daemon->log_display_id = src->log_id; ++ daemon->log_source_addr = &src->source; ++ log_query(F_UPSTREAM, "query", NULL, "duplicate"); ++ } ++ } + } ++ + free_frec(forward); /* cancel */ + } + } +@@ -2034,6 +2101,17 @@ void free_rfd(struct randfd *rfd) + + static void free_frec(struct frec *f) + { ++ struct frec_src *src, *tmp; ++ ++ /* add back to freelist of not the record builtin to every frec. */ ++ for (src = f->frec_src.next; src; src = tmp) ++ { ++ tmp = src->next; ++ src->next = daemon->free_frec_src; ++ daemon->free_frec_src = src; ++ } ++ ++ f->frec_src.next = NULL; + free_rfd(f->rfd4); + f->rfd4 = NULL; + f->sentto = NULL; +@@ -2175,17 +2253,39 @@ static struct frec *lookup_frec_by_sender(unsigned short id, + void *hash) + { + struct frec *f; ++ struct frec_src *src; ++ ++ for (f = daemon->frec_list; f; f = f->next) ++ if (f->sentto && ++ !(f->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) && ++ memcmp(hash, f->hash, HASH_SIZE) == 0) ++ for (src = &f->frec_src; src; src = src->next) ++ if (src->orig_id == id && ++ sockaddr_isequal(&src->source, addr)) ++ return f; ++ ++ return NULL; ++} ++ ++static struct frec *lookup_frec_by_query(void *hash, unsigned int flags) ++{ ++ struct frec *f; ++ ++ /* FREC_DNSKEY and FREC_DS_QUERY are never set in flags, so the test below ++ ensures that no frec created for internal DNSSEC query can be returned here. */ ++ ++#define FLAGMASK (FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION \ ++ | FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY) + + for(f = daemon->frec_list; f; f = f->next) + if (f->sentto && +- f->orig_id == id && +- memcmp(hash, f->hash, HASH_SIZE) == 0 && +- sockaddr_isequal(&f->source, addr)) ++ (f->flags & FLAGMASK) == flags && ++ memcmp(hash, f->hash, HASH_SIZE) == 0) + return f; +- ++ + return NULL; + } +- ++ + /* Send query packet again, if we can. */ + void resend_query() + { +-- +2.26.2 + diff --git a/SPECS/dnsmasq.spec b/SPECS/dnsmasq.spec index 84f6dc7..f3ea45a 100644 --- a/SPECS/dnsmasq.spec +++ b/SPECS/dnsmasq.spec @@ -13,7 +13,7 @@ Name: dnsmasq Version: 2.76 -Release: 16%{?extraversion}%{?dist} +Release: 16%{?extraversion}%{?dist}.1 Summary: A lightweight DHCP/caching DNS server Group: System Environment/Daemons @@ -78,12 +78,18 @@ Patch32: dnsmasq-2.76-rh1815080.patch Patch33: dnsmasq-2.76-rh1755610.patch # http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=ef3d137a646fa8309e1ff5184e3e145eef40cc4d Patch34: dnsmasq-2.79-fix-infitite-strict-mode-retries.patch +Patch35: dnsmasq-2.79-CVE-2020-25684.patch +Patch36: dnsmasq-2.79-CVE-2020-25685.patch +Patch37: dnsmasq-2.79-CVE-2020-25686.patch +# Link hash function from nettle, avoid local implementation +Patch38: dnsmasq-2.76-nettlehash.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: dbus-devel BuildRequires: pkgconfig BuildRequires: libidn-devel +BuildRequires: nettle-devel BuildRequires: systemd Requires(post): systemd systemd-sysv chkconfig @@ -147,6 +153,10 @@ query/remove a DHCP server's leases. %patch32 -p1 -b .rh1815080 %patch33 -p1 -b .rh1755610 %patch34 -p1 -b .rh1755610-strict-mode +%patch35 -p1 -b .CVE-2020-25684 +%patch36 -p1 -b .CVE-2020-25685 +%patch37 -p1 -b .CVE-2020-25686 +%patch38 -p1 -b .nettlehash # use /var/lib/dnsmasq instead of /var/lib/misc for file in dnsmasq.conf.example man/dnsmasq.8 man/es/dnsmasq.8 src/config.h; do @@ -159,6 +169,9 @@ sed -i 's|/\* #define HAVE_DBUS \*/|#define HAVE_DBUS|g' src/config.h #enable IDN support sed -i 's|/\* #define HAVE_IDN \*/|#define HAVE_IDN|g' src/config.h +#enable sha256 from nettle +sed -i 's|/\* #define HAVE_NETTLEHASH \*/|#define HAVE_NETTLEHASH|g' src/config.h + #enable /etc/dnsmasq.d fix bz 526703, ignore RPM backup files cat << EOF >> dnsmasq.conf.example @@ -233,6 +246,12 @@ rm -rf $RPM_BUILD_ROOT %{_mandir}/man1/dhcp_* %changelog +* Fri Nov 27 2020 Petr Menšík - 2.76-16.1 +- Accept responses only on correct sockets (CVE-2020-25684) +- Use strong verification on queries (CVE-2020-25685) +- Handle multiple identical DNS queries better (CVE-2020-25686) +- Link against nettle for sha256 hash implementation + * Thu Jul 02 2020 Petr Menšík - 2.76-16 - Fix strict-mode retries on REFUSED (#1755610)