aboutsummaryrefslogtreecommitdiffstats
path: root/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25681.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-networking/recipes-support/dnsmasq/files/CVE-2020-25681.patch')
-rw-r--r--meta-networking/recipes-support/dnsmasq/files/CVE-2020-25681.patch370
1 files changed, 370 insertions, 0 deletions
diff --git a/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25681.patch b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25681.patch
new file mode 100644
index 0000000000..6756157700
--- /dev/null
+++ b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25681.patch
@@ -0,0 +1,370 @@
+From 4e96a4be685c9e4445f6ee79ad0b36b9119b502a Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Wed, 11 Nov 2020 23:25:04 +0000
+Subject: [PATCH] Fix remote buffer overflow CERT VU#434904
+
+The problem is in the sort_rrset() function and allows a remote
+attacker to overwrite memory. Any dnsmasq instance with DNSSEC
+enabled is vulnerable.
+
+Signed-off-by: Sana Kazi <Sana.Kazi@kpit.com>
+---
+ CHANGELOG | 7 +-
+ src/dnssec.c | 273 ++++++++++++++++++++++++++++-----------------------
+ 2 files changed, 158 insertions(+), 122 deletions(-)
+
+CVE: CVE-2020-25681
+CVE: CVE-2020-25682
+CVE: CVE-2020-25683
+CVE: CVE-2020-25687
+Upstream-Status: Backport [https://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=patch;h=4e96a4be685c9e4445f6ee79ad0b36b9119b502a]
+Comment: Refreshed first two hunks
+
+Index: dnsmasq-2.81/src/dnssec.c
+===================================================================
+--- dnsmasq-2.81.orig/src/dnssec.c
++++ dnsmasq-2.81/src/dnssec.c
+@@ -223,138 +223,144 @@ static int check_date_range(unsigned lon
+ && serial_compare_32(curtime, date_end) == SERIAL_LT;
+ }
+
+-/* Return bytes of canonicalised rdata, when the return value is zero, the remaining
+- data, pointed to by *p, should be used raw. */
+-static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, char *buff, int bufflen,
+- unsigned char **p, u16 **desc)
++/* Return bytes of canonicalised rrdata one by one.
++ Init state->ip with the RR, and state->end with the end of same.
++ Init state->op to NULL.
++ Init state->desc to RR descriptor.
++ Init state->buff with a MAXDNAME * 2 buffer.
++
++ After each call which returns 1, state->op points to the next byte of data.
++ On returning 0, the end has been reached.
++*/
++struct rdata_state {
++ u16 *desc;
++ size_t c;
++ unsigned char *end, *ip, *op;
++ char *buff;
++};
++
++static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state *state)
+ {
+- int d = **desc;
++ int d;
+
+- /* No more data needs mangling */
+- if (d == (u16)-1)
++ if (state->op && state->c != 1)
+ {
+- /* If there's more data than we have space for, just return what fits,
+- we'll get called again for more chunks */
+- if (end - *p > bufflen)
+- {
+- memcpy(buff, *p, bufflen);
+- *p += bufflen;
+- return bufflen;
+- }
+-
+- return 0;
++ state->op++;
++ state->c--;
++ return 1;
+ }
+-
+- (*desc)++;
+-
+- if (d == 0 && extract_name(header, plen, p, buff, 1, 0))
+- /* domain-name, canonicalise */
+- return to_wire(buff);
+- else
+- {
+- /* plain data preceding a domain-name, don't run off the end of the data */
+- if ((end - *p) < d)
+- d = end - *p;
+-
+- if (d != 0)
++
++ while (1)
++ {
++ d = *(state->desc);
++ if (d == (u16)-1)
+ {
+- memcpy(buff, *p, d);
+- *p += d;
++ /* all the bytes to the end. */
++ if ((state->c = state->end - state->ip) != 0)
++ {
++ state->op = state->ip;
++ state->ip = state->end;;
++ }
++ else
++ return 0;
++ }
++ else
++ {
++ state->desc++;
++
++ if (d == (u16)0)
++ {
++ /* domain-name, canonicalise */
++ int len;
++
++ if (!extract_name(header, plen, &state->ip, state->buff, 1, 0) ||
++ (len = to_wire(state->buff)) == 0)
++ continue;
++
++ state->c = len;
++ state->op = (unsigned char *)state->buff;
++ }
++ else
++ {
++ /* plain data preceding a domain-name, don't run off the end of the data */
++ if ((state->end - state->ip) < d)
++ d = state->end - state->ip;
++
++ if (d == 0)
++ continue;
++
++ state->op = state->ip;
++ state->c = d;
++ state->ip += d;
++ }
+ }
+
+- return d;
++ return 1;
+ }
+ }
+
+-/* Bubble sort the RRset into the canonical order.
+- Note that the byte-streams from two RRs may get unsynced: consider
+- RRs which have two domain-names at the start and then other data.
+- The domain-names may have different lengths in each RR, but sort equal
+-
+- ------------
+- |abcde|fghi|
+- ------------
+- |abcd|efghi|
+- ------------
+-
+- leaving the following bytes as deciding the order. Hence the nasty left1 and left2 variables.
+-*/
++/* Bubble sort the RRset into the canonical order. */
+
+ static int sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int rrsetidx,
+ unsigned char **rrset, char *buff1, char *buff2)
+ {
+- int swap, quit, i, j;
++ int swap, i, j;
+
+ do
+ {
+ for (swap = 0, i = 0; i < rrsetidx-1; i++)
+ {
+- int rdlen1, rdlen2, left1, left2, len1, len2, len, rc;
+- u16 *dp1, *dp2;
+- unsigned char *end1, *end2;
++ int rdlen1, rdlen2;
++ struct rdata_state state1, state2;
++
+ /* Note that these have been determined to be OK previously,
+ so we don't need to check for NULL return here. */
+- unsigned char *p1 = skip_name(rrset[i], header, plen, 10);
+- unsigned char *p2 = skip_name(rrset[i+1], header, plen, 10);
+-
+- p1 += 8; /* skip class, type, ttl */
+- GETSHORT(rdlen1, p1);
+- end1 = p1 + rdlen1;
+-
+- p2 += 8; /* skip class, type, ttl */
+- GETSHORT(rdlen2, p2);
+- end2 = p2 + rdlen2;
+-
+- dp1 = dp2 = rr_desc;
+-
+- for (quit = 0, left1 = 0, left2 = 0, len1 = 0, len2 = 0; !quit;)
++ state1.ip = skip_name(rrset[i], header, plen, 10);
++ state2.ip = skip_name(rrset[i+1], header, plen, 10);
++ state1.op = state2.op = NULL;
++ state1.buff = buff1;
++ state2.buff = buff2;
++ state1.desc = state2.desc = rr_desc;
++
++ state1.ip += 8; /* skip class, type, ttl */
++ GETSHORT(rdlen1, state1.ip);
++ if (!CHECK_LEN(header, state1.ip, plen, rdlen1))
++ return rrsetidx; /* short packet */
++ state1.end = state1.ip + rdlen1;
++ state2.ip += 8; /* skip class, type, ttl */
++ GETSHORT(rdlen2, state2.ip);
++ if (!CHECK_LEN(header, state2.ip, plen, rdlen2))
++ return rrsetidx; /* short packet */
++ state2.end = state2.ip + rdlen2;
++
++ while (1)
+ {
+- if (left1 != 0)
+- memmove(buff1, buff1 + len1 - left1, left1);
+-
+- if ((len1 = get_rdata(header, plen, end1, buff1 + left1, (MAXDNAME * 2) - left1, &p1, &dp1)) == 0)
+- {
+- quit = 1;
+- len1 = end1 - p1;
+- memcpy(buff1 + left1, p1, len1);
++ int ok1, ok2;
++ ok1 = get_rdata(header, plen, &state1);
++ ok2 = get_rdata(header, plen, &state2);
++
++ if (!ok1 && !ok2)
++ {
++ /* Two RRs are equal, remove one copy. RFC 4034, para 6.3 */
++ for (j = i+1; j < rrsetidx-1; j++)
++ rrset[j] = rrset[j+1];
++ rrsetidx--;
++ i--;
++ break;
+ }
+- len1 += left1;
+-
+- if (left2 != 0)
+- memmove(buff2, buff2 + len2 - left2, left2);
+-
+- if ((len2 = get_rdata(header, plen, end2, buff2 + left2, (MAXDNAME *2) - left2, &p2, &dp2)) == 0)
+- {
+- quit = 1;
+- len2 = end2 - p2;
+- memcpy(buff2 + left2, p2, len2);
+- }
+- len2 += left2;
+-
+- if (len1 > len2)
+- left1 = len1 - len2, left2 = 0, len = len2;
+- else
+- left2 = len2 - len1, left1 = 0, len = len1;
+-
+- rc = (len == 0) ? 0 : memcmp(buff1, buff2, len);
+-
+- if (rc > 0 || (rc == 0 && quit && len1 > len2))
++ else if (ok1 && (!ok2 || *state1.op > *state2.op))
+ {
+ unsigned char *tmp = rrset[i+1];
+ rrset[i+1] = rrset[i];
+ rrset[i] = tmp;
+- swap = quit = 1;
+- }
+- else if (rc == 0 && quit && len1 == len2)
+- {
+- /* Two RRs are equal, remove one copy. RFC 4034, para 6.3 */
+- for (j = i+1; j < rrsetidx-1; j++)
+- rrset[j] = rrset[j+1];
+- rrsetidx--;
+- i--;
++ swap = 1;
++ break;
+ }
+- else if (rc < 0)
+- quit = 1;
++ else if (ok2 && (!ok1 || *state2.op > *state1.op))
++ break;
++
++ /* arrive here when bytes are equal, go round the loop again
++ and compare the next ones. */
+ }
+ }
+ } while (swap);
+@@ -569,12 +575,15 @@ static int validate_rrset(time_t now, st
+ wire_len = to_wire(keyname);
+ hash->update(ctx, (unsigned int)wire_len, (unsigned char*)keyname);
+ from_wire(keyname);
++
++#define RRBUFLEN 300 /* Most RRs are smaller than this. */
+
+ for (i = 0; i < rrsetidx; ++i)
+ {
+- int seg;
+- unsigned char *end, *cp;
+- u16 len, *dp;
++ int j;
++ struct rdata_state state;
++ u16 len;
++ unsigned char rrbuf[RRBUFLEN];
+
+ p = rrset[i];
+
+@@ -586,12 +595,11 @@ static int validate_rrset(time_t now, st
+ /* if more labels than in RRsig name, hash *.<no labels in rrsig labels field> 4035 5.3.2 */
+ if (labels < name_labels)
+ {
+- int k;
+- for (k = name_labels - labels; k != 0; k--)
++ for (j = name_labels - labels; j != 0; j--)
+ {
+ while (*name_start != '.' && *name_start != 0)
+ name_start++;
+- if (k != 1 && *name_start == '.')
++ if (j != 1 && *name_start == '.')
+ name_start++;
+ }
+
+@@ -612,24 +620,44 @@ static int validate_rrset(time_t now, st
+ if (!CHECK_LEN(header, p, plen, rdlen))
+ return STAT_BOGUS;
+
+- end = p + rdlen;
+-
+- /* canonicalise rdata and calculate length of same, use name buffer as workspace.
+- Note that name buffer is twice MAXDNAME long in DNSSEC mode. */
+- cp = p;
+- dp = rr_desc;
+- for (len = 0; (seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp)) != 0; len += seg);
+- len += end - cp;
+- len = htons(len);
++ /* canonicalise rdata and calculate length of same, use
++ name buffer as workspace for get_rdata. */
++ state.ip = p;
++ state.op = NULL;
++ state.desc = rr_desc;
++ state.buff = name;
++ state.end = p + rdlen;
++
++ for (j = 0; get_rdata(header, plen, &state); j++)
++ if (j < RRBUFLEN)
++ rrbuf[j] = *state.op;
++
++ len = htons((u16)j);
+ hash->update(ctx, 2, (unsigned char *)&len);
++
++ /* If the RR is shorter than RRBUFLEN (most of them, in practice)
++ then we can just digest it now. If it exceeds RRBUFLEN we have to
++ go back to the start and do it in chunks. */
++ if (j >= RRBUFLEN)
++ {
++ state.ip = p;
++ state.op = NULL;
++ state.desc = rr_desc;
++
++ for (j = 0; get_rdata(header, plen, &state); j++)
++ {
++ rrbuf[j] = *state.op;
++
++ if (j == RRBUFLEN - 1)
++ {
++ hash->update(ctx, RRBUFLEN, rrbuf);
++ j = -1;
++ }
++ }
++ }
+
+- /* Now canonicalise again and digest. */
+- cp = p;
+- dp = rr_desc;
+- while ((seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp)))
+- hash->update(ctx, seg, (unsigned char *)name);
+- if (cp != end)
+- hash->update(ctx, end - cp, cp);
++ if (j != 0)
++ hash->update(ctx, j, rrbuf);
+ }
+
+ hash->digest(ctx, hash->digest_size, digest);