From 65f9c1aca17e97ceed6937f0f09dfd1077f55c4a Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Sun, 19 Jan 2025 00:08:36 +0000 Subject: [PATCH] Case-sensitive matching of questions and answers. When checking that an answer is the answer to the question that we asked, compare the name in a case-sensitive manner. Clients can set the letters in a query to a random pattern of uppercase and lowercase to add more randomness as protection against cache-poisoning attacks, and we don't want to nullify that. This actually restores the status quo before commit ed6d29a78475f9ec91141120aba53490bc1dc39a since matching questions and answers using a checksum can't help but be case sensitive. This patch is a preparation for introducing DNS-0x20 in the dnsmasq query path. --- src/forward.c | 13 ++++++++----- src/rfc1035.c | 20 +++++++++++++++----- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/forward.c b/src/forward.c index 056a0c3..595e47b 100644 --- a/src/forward.c +++ b/src/forward.c @@ -1999,12 +1999,14 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet, continue; } - /* If the question section of the reply doesn't match the crc we sent, then + /* If the question section of the reply doesn't match the question we sent, then 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. + We compare the query name in a case sensitive manner, so that + DNS-0x20 encoding is effective. Try another server, or give up */ p = (unsigned char *)(header+1); - if (extract_name(header, rsize, &p, daemon->namebuff, 0, 4) != 1) + if (extract_name(header, rsize, &p, daemon->namebuff, -1, 4) != 1) continue; GETSHORT(rtype, p); GETSHORT(rclass, p); @@ -3045,8 +3047,9 @@ static struct frec *lookup_frec(char *target, int class, int rrtype, int id, int { unsigned char *p = (unsigned char *)(header+1); int hclass, hrrtype; - - if (extract_name(header, f->stash_len, &p, target, 0, 4) != 1) + + /* Case sensitive compare for DNS-0x20 encoding. */ + if (extract_name(header, f->stash_len, &p, target, -1, 4) != 1) continue; GETSHORT(hrrtype, p); diff --git a/src/rfc1035.c b/src/rfc1035.c index d669e6a..6eca371 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -16,14 +16,24 @@ #include "dnsmasq.h" +/* isExtract == 1 -> extract name + isExtract == 0 -> compare name, case insensitive + isExtract == -1 -> compare name, case sensitive + + return = 0 -> error + return = 1 -> extract OK, compare OK + return = 2 -> extract OK, compare failed. +*/ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, char *name, int isExtract, int extrabytes) { unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL; unsigned int j, l, namelen = 0, hops = 0; - int retvalue = 1; - - if (isExtract) + int retvalue = 1, case_insens = 1; + + if (isExtract == -1) + isExtract = case_insens = 0; + else if (isExtract) *cp = 0; while (1) @@ -107,13 +117,13 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, else { cp++; - if (c1 >= 'A' && c1 <= 'Z') + if (case_insens && c1 >= 'A' && c1 <= 'Z') c1 += 'a' - 'A'; if (c1 == NAME_ESCAPE) c1 = (*cp++)-1; - if (c2 >= 'A' && c2 <= 'Z') + if (case_insens && c2 >= 'A' && c2 <= 'Z') c2 += 'a' - 'A'; if (c1 != c2)