From: Michael Tremer <michael.tremer@ipfire.org>
To: development@lists.ipfire.org
Subject: Re: [PATCH] dnsmasq 2.75: latest upstream patches ;-)
Date: Fri, 18 Dec 2015 15:30:21 +0000 [thread overview]
Message-ID: <1450452621.31655.209.camel@ipfire.org> (raw)
In-Reply-To: <1450447885-12407-1-git-send-email-matthias.fischer@ipfire.org>
[-- Attachment #1: Type: text/plain, Size: 33354 bytes --]
Hi,
I merged them both since we are still having stability issues with
dnsmasq. Some of the cleanup patches look quite promising.
All give that some good testing, please.
-Michael
On Fri, 2015-12-18 at 15:11 +0100, Matthias Fischer wrote:
> The neverending story continues...
>
> Signed-off-by: Matthias Fischer <matthias.fischer(a)ipfire.org>
> ---
> lfs/dnsmasq | 4 +
> ...1-Tweaks_to_EDNS0_handling_in_DNS_replies.patch | 133 +++++++
> ..._code_Check_zone_status_is_NSEC_proof_bad.patch | 409
> +++++++++++++++++++++
> ...023-Fix_brace_botch_in_dnssec_validate_ds.patch | 98 +++++
> ...ning_which_DNSSEC_sig_algos_are_supported.patch | 145 ++++++++
> 5 files changed, 789 insertions(+)
> create mode 100644 src/patches/dnsmasq/021
> -Tweaks_to_EDNS0_handling_in_DNS_replies.patch
> create mode 100644 src/patches/dnsmasq/022-Tidy_up_DNSSEC_non
> -existence_code_Check_zone_status_is_NSEC_proof_bad.patch
> create mode 100644 src/patches/dnsmasq/023
> -Fix_brace_botch_in_dnssec_validate_ds.patch
> create mode 100644 src/patches/dnsmasq/024
> -Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.
> patch
>
> diff --git a/lfs/dnsmasq b/lfs/dnsmasq
> index eeb7e03..c8fd7db 100644
> --- a/lfs/dnsmasq
> +++ b/lfs/dnsmasq
> @@ -93,6 +93,10 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
> cd $(DIR_APP) && patch -Np1 -i
> $(DIR_SRC)/src/patches/dnsmasq/018
> -Move_code_which_caches_DS_records_to_a_more_logical_place.patch
> cd $(DIR_APP) && patch -Np1 -i
> $(DIR_SRC)/src/patches/dnsmasq/019-Generalise_RR
> -filtering_code_for_use_with_EDNS0.patch
> cd $(DIR_APP) && patch -Np1 -i
> $(DIR_SRC)/src/patches/dnsmasq/020-DNSSEC_validation_tweak.patch
> + cd $(DIR_APP) && patch -Np1 -i
> $(DIR_SRC)/src/patches/dnsmasq/021
> -Tweaks_to_EDNS0_handling_in_DNS_replies.patch
> + cd $(DIR_APP) && patch -Np1 -i
> $(DIR_SRC)/src/patches/dnsmasq/022-Tidy_up_DNSSEC_non
> -existence_code_Check_zone_status_is_NSEC_proof_bad.patch
> + cd $(DIR_APP) && patch -Np1 -i
> $(DIR_SRC)/src/patches/dnsmasq/023
> -Fix_brace_botch_in_dnssec_validate_ds.patch
> + cd $(DIR_APP) && patch -Np1 -i
> $(DIR_SRC)/src/patches/dnsmasq/024
> -Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.
> patch
> cd $(DIR_APP) && patch -Np1 -i
> $(DIR_SRC)/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease
> -file.patch
>
> cd $(DIR_APP) && sed -i src/config.h \
> diff --git a/src/patches/dnsmasq/021
> -Tweaks_to_EDNS0_handling_in_DNS_replies.patch
> b/src/patches/dnsmasq/021
> -Tweaks_to_EDNS0_handling_in_DNS_replies.patch
> new file mode 100644
> index 0000000..c3c74cc
> --- /dev/null
> +++ b/src/patches/dnsmasq/021
> -Tweaks_to_EDNS0_handling_in_DNS_replies.patch
> @@ -0,0 +1,133 @@
> +From dd4ad9ac7ea6d51dcc34a1f2cd2da14efbb87714 Mon Sep 17 00:00:00
> 2001
> +From: Simon Kelley <simon(a)thekelleys.org.uk>
> +Date: Thu, 17 Dec 2015 10:44:58 +0000
> +Subject: [PATCH] Tweaks to EDNS0 handling in DNS replies.
> +
> +---
> + src/dnssec.c | 20 +++++++++-----------
> + src/rfc1035.c | 57 +++++++++++++++++++++++++++++++++-------------
> -----------
> + 2 files changed, 42 insertions(+), 35 deletions(-)
> +
> +diff --git a/src/dnssec.c b/src/dnssec.c
> +index dc563e0..012b2a6 100644
> +--- a/src/dnssec.c
> ++++ b/src/dnssec.c
> +@@ -2129,18 +2129,16 @@ int dnssec_validate_reply(time_t now, struct
> dns_header *header, size_t plen, ch
> + /* Empty DS without NSECS */
> + if (qtype == T_DS)
> + return STAT_BOGUS;
> +- else
> ++
> ++ rc = zone_status(name, qclass, keyname, now);
> ++ if (rc != STAT_SECURE)
> + {
> +- rc = zone_status(name, qclass, keyname, now);
> +- if (rc != STAT_SECURE)
> +- {
> +- if (class)
> +- *class = qclass; /* Class for NEED_DS or
> NEED_DNSKEY */
> +- return rc;
> +- }
> +-
> +- return STAT_BOGUS; /* signed zone, no NSECs */
> +- }
> ++ if (class)
> ++ *class = qclass; /* Class for NEED_DS or
> NEED_DNSKEY */
> ++ return rc;
> ++ }
> ++
> ++ return STAT_BOGUS; /* signed zone, no NSECs */
> + }
> +
> + if (nsec_type == T_NSEC)
> +diff --git a/src/rfc1035.c b/src/rfc1035.c
> +index def8fa0..188d05f 100644
> +--- a/src/rfc1035.c
> ++++ b/src/rfc1035.c
> +@@ -1539,7 +1539,13 @@ size_t answer_request(struct dns_header
> *header, char *limit, size_t qlen,
> + int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
> + struct mx_srv_record *rec;
> + size_t len;
> +-
> ++
> ++ if (ntohs(header->ancount) != 0 ||
> ++ ntohs(header->nscount) != 0 ||
> ++ ntohs(header->qdcount) == 0 ||
> ++ OPCODE(header) != QUERY )
> ++ return 0;
> ++
> + /* Don't return AD set if checking disabled. */
> + if (header->hb4 & HB4_CD)
> + sec_data = 0;
> +@@ -1548,33 +1554,32 @@ size_t answer_request(struct dns_header
> *header, char *limit, size_t qlen,
> + *ad_reqd = header->hb4 & HB4_AD;
> + *do_bit = 0;
> +
> +- /* If there is an RFC2671 pseudoheader then it will be
> overwritten by
> ++ /* If there is an additional data section then it will be
> overwritten by
> + partial replies, so we have to do a dry run to see if we can
> answer
> +- the query. We check to see if the do bit is set, if so we
> always
> +- forward rather than answering from the cache, which doesn't
> include
> +- security information, unless we're in DNSSEC validation mode.
> */
> ++ the query. */
> +
> +- if (find_pseudoheader(header, qlen, NULL, &pheader, NULL))
> +- {
> +- unsigned short flags;
> +-
> +- have_pseudoheader = 1;
> ++ if (ntohs(header->arcount) != 0)
> ++ {
> ++ dryrun = 1;
> +
> +- pheader += 4; /* udp size, ext_rcode */
> +- GETSHORT(flags, pheader);
> +-
> +- if ((sec_reqd = flags & 0x8000))
> +- {
> +- *do_bit = 1;/* do bit */
> +- *ad_reqd = 1;
> ++ /* If there's an additional section, there might be an
> EDNS(0) pseudoheader */
> ++ if (find_pseudoheader(header, qlen, NULL, &pheader, NULL))
> ++ {
> ++ unsigned short flags;
> ++
> ++ have_pseudoheader = 1;
> ++
> ++ pheader += 4; /* udp size, ext_rcode */
> ++ GETSHORT(flags, pheader);
> ++
> ++ if ((sec_reqd = flags & 0x8000))
> ++ {
> ++ *do_bit = 1;/* do bit */
> ++ *ad_reqd = 1;
> ++ }
> + }
> +-
> +- dryrun = 1;
> + }
> +
> +- if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
> +- return 0;
> +-
> + for (rec = daemon->mxnames; rec; rec = rec->next)
> + rec->offset = 0;
> +
> +@@ -1730,8 +1735,12 @@ size_t answer_request(struct dns_header
> *header, char *limit, size_t qlen,
> + }
> + else if ((crecp = cache_find_by_addr(NULL, &addr,
> now, is_arpa)))
> + {
> +- /* Don't use cache when DNSSEC data required. */
> +- if ((crecp->flags & (F_HOSTS | F_DHCP |
> F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK))
> ++ /* Don't use cache when DNSSEC data required,
> unless we know that
> ++ the zone is unsigned, which implies that we're
> doing
> ++ validation. */
> ++ if ((crecp->flags & (F_HOSTS | F_DHCP |
> F_CONFIG)) ||
> ++ !sec_reqd ||
> ++ (option_bool(OPT_DNSSEC_VALID) && !(crecp
> ->flags & F_DNSSECOK)))
> + {
> + do
> + {
> +--
> +1.7.10.4
> +
> diff --git a/src/patches/dnsmasq/022-Tidy_up_DNSSEC_non
> -existence_code_Check_zone_status_is_NSEC_proof_bad.patch
> b/src/patches/dnsmasq/022-Tidy_up_DNSSEC_non
> -existence_code_Check_zone_status_is_NSEC_proof_bad.patch
> new file mode 100644
> index 0000000..60503e9
> --- /dev/null
> +++ b/src/patches/dnsmasq/022-Tidy_up_DNSSEC_non
> -existence_code_Check_zone_status_is_NSEC_proof_bad.patch
> @@ -0,0 +1,409 @@
> +From b40f26c0199235073abc37e1e1d6ed93bed372f5 Mon Sep 17 00:00:00
> 2001
> +From: Simon Kelley <simon(a)thekelleys.org.uk>
> +Date: Thu, 17 Dec 2015 11:57:26 +0000
> +Subject: [PATCH] Tidy up DNSSEC non-existence code. Check zone
> status is NSEC
> + proof bad.
> +
> +---
> + src/dnssec.c | 207 +++++++++++++++++++++++++----------------------
> -----------
> + 1 file changed, 90 insertions(+), 117 deletions(-)
> +
> +diff --git a/src/dnssec.c b/src/dnssec.c
> +index 012b2a6..ddae497 100644
> +--- a/src/dnssec.c
> ++++ b/src/dnssec.c
> +@@ -1367,59 +1367,6 @@ static int hostname_cmp(const char *a, const
> char *b)
> + }
> + }
> +
> +-/* Find all the NSEC or NSEC3 records in a reply.
> +- return an array of pointers to them. */
> +-static int find_nsec_records(struct dns_header *header, size_t
> plen, unsigned char ***nsecsetp, int *nsecsetl, int class_reqd)
> +-{
> +- static unsigned char **nsecset = NULL;
> +- static int nsecset_sz = 0;
> +-
> +- int type_found = 0;
> +- unsigned char *p = skip_questions(header, plen);
> +- int type, class, rdlen, i, nsecs_found;
> +-
> +- /* Move to NS section */
> +- if (!p || !(p = skip_section(p, ntohs(header->ancount), header,
> plen)))
> +- return 0;
> +-
> +- for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
> +- {
> +- unsigned char *pstart = p;
> +-
> +- if (!(p = skip_name(p, header, plen, 10)))
> +- return 0;
> +-
> +- GETSHORT(type, p);
> +- GETSHORT(class, p);
> +- p += 4; /* TTL */
> +- GETSHORT(rdlen, p);
> +-
> +- if (class == class_reqd && (type == T_NSEC || type ==
> T_NSEC3))
> +- {
> +- /* No mixed NSECing 'round here, thankyouverymuch */
> +- if (type_found == T_NSEC && type == T_NSEC3)
> +- return 0;
> +- if (type_found == T_NSEC3 && type == T_NSEC)
> +- return 0;
> +-
> +- type_found = type;
> +-
> +- if (!expand_workspace(&nsecset, &nsecset_sz,
> nsecs_found))
> +- return 0;
> +-
> +- nsecset[nsecs_found++] = pstart;
> +- }
> +-
> +- if (!ADD_RDLEN(header, p, plen, rdlen))
> +- return 0;
> +- }
> +-
> +- *nsecsetp = nsecset;
> +- *nsecsetl = nsecs_found;
> +-
> +- return type_found;
> +-}
> +-
> + static int prove_non_existence_nsec(struct dns_header *header,
> size_t plen, unsigned char **nsecs, int nsec_count,
> + char *workspace1, char
> *workspace2, char *name, int type, int *nons)
> + {
> +@@ -1436,12 +1383,12 @@ static int prove_non_existence_nsec(struct
> dns_header *header, size_t plen, unsi
> + {
> + p = nsecs[i];
> + if (!extract_name(header, plen, &p, workspace1, 1, 10))
> +- return STAT_BOGUS;
> ++ return 0;
> + p += 8; /* class, type, TTL */
> + GETSHORT(rdlen, p);
> + psave = p;
> + if (!extract_name(header, plen, &p, workspace2, 1, 10))
> +- return STAT_BOGUS;
> ++ return 0;
> +
> + rc = hostname_cmp(workspace1, name);
> +
> +@@ -1449,7 +1396,7 @@ static int prove_non_existence_nsec(struct
> dns_header *header, size_t plen, unsi
> + {
> + /* 4035 para 5.4. Last sentence */
> + if (type == T_NSEC || type == T_RRSIG)
> +- return STAT_SECURE;
> ++ return 1;
> +
> + /* NSEC with the same name as the RR we're testing, check
> + that the type in question doesn't appear in the type
> map */
> +@@ -1465,24 +1412,24 @@ static int prove_non_existence_nsec(struct
> dns_header *header, size_t plen, unsi
> + /* A CNAME answer would also be valid, so if there's
> a CNAME is should
> + have been returned. */
> + if ((p[2] & (0x80 >> T_CNAME)) != 0)
> +- return STAT_BOGUS;
> ++ return 0;
> +
> + /* If the SOA bit is set for a DS record, then we
> have the
> + DS from the wrong side of the delegation. */
> + if (type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
> +- return STAT_BOGUS;
> ++ return 0;
> + }
> +
> + while (rdlen >= 2)
> + {
> + if (!CHECK_LEN(header, p, plen, rdlen))
> +- return STAT_BOGUS;
> ++ return 0;
> +
> + if (p[0] == type >> 8)
> + {
> + /* Does the NSEC say our type exists? */
> + if (offset < p[1] && (p[offset+2] & mask) != 0)
> +- return STAT_BOGUS;
> ++ return 0;
> +
> + break; /* finshed checking */
> + }
> +@@ -1491,24 +1438,24 @@ static int prove_non_existence_nsec(struct
> dns_header *header, size_t plen, unsi
> + p += p[1];
> + }
> +
> +- return STAT_SECURE;
> ++ return 1;
> + }
> + else if (rc == -1)
> + {
> + /* Normal case, name falls between NSEC name and next
> domain name,
> + wrap around case, name falls between NSEC name (rc ==
> -1) and end */
> + if (hostname_cmp(workspace2, name) >= 0 ||
> hostname_cmp(workspace1, workspace2) >= 0)
> +- return STAT_SECURE;
> ++ return 1;
> + }
> + else
> + {
> + /* wrap around case, name falls between start and next
> domain name */
> + if (hostname_cmp(workspace1, workspace2) >= 0 &&
> hostname_cmp(workspace2, name) >=0 )
> +- return STAT_SECURE;
> ++ return 1;
> + }
> + }
> +
> +- return STAT_BOGUS;
> ++ return 0;
> + }
> +
> + /* return digest length, or zero on error */
> +@@ -1701,7 +1648,7 @@ static int prove_non_existence_nsec3(struct
> dns_header *header, size_t plen, uns
> + for (i = 0; i < nsec_count; i++)
> + {
> + if (!(p = skip_name(nsecs[i], header, plen, 15)))
> +- return STAT_BOGUS; /* bad packet */
> ++ return 0; /* bad packet */
> +
> + p += 10; /* type, class, TTL, rdlen */
> + algo = *p++;
> +@@ -1712,14 +1659,14 @@ static int prove_non_existence_nsec3(struct
> dns_header *header, size_t plen, uns
> +
> + /* No usable NSEC3s */
> + if (i == nsec_count)
> +- return STAT_BOGUS;
> ++ return 0;
> +
> + p++; /* flags */
> + GETSHORT (iterations, p);
> + salt_len = *p++;
> + salt = p;
> + if (!CHECK_LEN(header, salt, plen, salt_len))
> +- return STAT_BOGUS; /* bad packet */
> ++ return 0; /* bad packet */
> +
> + /* Now prune so we only have NSEC3 records with same iterations,
> salt and algo */
> + for (i = 0; i < nsec_count; i++)
> +@@ -1730,7 +1677,7 @@ static int prove_non_existence_nsec3(struct
> dns_header *header, size_t plen, uns
> + nsecs[i] = NULL; /* Speculative, will be restored if OK. */
> +
> + if (!(p = skip_name(nsec3p, header, plen, 15)))
> +- return STAT_BOGUS; /* bad packet */
> ++ return 0; /* bad packet */
> +
> + p += 10; /* type, class, TTL, rdlen */
> +
> +@@ -1747,7 +1694,7 @@ static int prove_non_existence_nsec3(struct
> dns_header *header, size_t plen, uns
> + continue;
> +
> + if (!CHECK_LEN(header, p, plen, salt_len))
> +- return STAT_BOGUS; /* bad packet */
> ++ return 0; /* bad packet */
> +
> + if (memcmp(p, salt, salt_len) != 0)
> + continue;
> +@@ -1758,13 +1705,13 @@ static int prove_non_existence_nsec3(struct
> dns_header *header, size_t plen, uns
> +
> + /* Algo is checked as 1 above */
> + if (!(hash = hash_find("sha1")))
> +- return STAT_BOGUS;
> ++ return 0;
> +
> + if ((digest_len = hash_name(name, &digest, hash, salt, salt_len,
> iterations)) == 0)
> +- return STAT_BOGUS;
> ++ return 0;
> +
> + if (check_nsec3_coverage(header, plen, digest_len, digest, type,
> workspace1, workspace2, nsecs, nsec_count, nons))
> +- return STAT_SECURE;
> ++ return 1;
> +
> + /* Can't find an NSEC3 which covers the name directly, we need
> the "closest encloser NSEC3"
> + or an answer inferred from a wildcard record. */
> +@@ -1780,14 +1727,14 @@ static int prove_non_existence_nsec3(struct
> dns_header *header, size_t plen, uns
> + break;
> +
> + if ((digest_len = hash_name(closest_encloser, &digest, hash,
> salt, salt_len, iterations)) == 0)
> +- return STAT_BOGUS;
> ++ return 0;
> +
> + for (i = 0; i < nsec_count; i++)
> + if ((p = nsecs[i]))
> + {
> + if (!extract_name(header, plen, &p, workspace1, 1, 0)
> ||
> + !(base32_len = base32_decode(workspace1, (unsigned
> char *)workspace2)))
> +- return STAT_BOGUS;
> ++ return 0;
> +
> + if (digest_len == base32_len &&
> + memcmp(digest, workspace2, digest_len) == 0)
> +@@ -1802,32 +1749,81 @@ static int prove_non_existence_nsec3(struct
> dns_header *header, size_t plen, uns
> + while ((closest_encloser = strchr(closest_encloser, '.')));
> +
> + if (!closest_encloser)
> +- return STAT_BOGUS;
> ++ return 0;
> +
> + /* Look for NSEC3 that proves the non-existence of the next
> -closest encloser */
> + if ((digest_len = hash_name(next_closest, &digest, hash, salt,
> salt_len, iterations)) == 0)
> +- return STAT_BOGUS;
> ++ return 0;
> +
> + if (!check_nsec3_coverage(header, plen, digest_len, digest, type,
> workspace1, workspace2, nsecs, nsec_count, NULL))
> +- return STAT_BOGUS;
> ++ return 0;
> +
> + /* Finally, check that there's no seat of wildcard synthesis */
> + if (!wildname)
> + {
> + if (!(wildcard = strchr(next_closest, '.')) || wildcard ==
> next_closest)
> +- return STAT_BOGUS;
> ++ return 0;
> +
> + wildcard--;
> + *wildcard = '*';
> +
> + if ((digest_len = hash_name(wildcard, &digest, hash, salt,
> salt_len, iterations)) == 0)
> +- return STAT_BOGUS;
> ++ return 0;
> +
> + if (!check_nsec3_coverage(header, plen, digest_len, digest,
> type, workspace1, workspace2, nsecs, nsec_count, NULL))
> +- return STAT_BOGUS;
> ++ return 0;
> + }
> +
> +- return STAT_SECURE;
> ++ return 1;
> ++}
> ++
> ++static int prove_non_existence(struct dns_header *header, size_t
> plen, char *keyname, char *name, int qtype, int qclass, char
> *wildname, int *nons)
> ++{
> ++ static unsigned char **nsecset = NULL;
> ++ static int nsecset_sz = 0;
> ++
> ++ int type_found = 0;
> ++ unsigned char *p = skip_questions(header, plen);
> ++ int type, class, rdlen, i, nsecs_found;
> ++
> ++ /* Move to NS section */
> ++ if (!p || !(p = skip_section(p, ntohs(header->ancount), header,
> plen)))
> ++ return 0;
> ++
> ++ for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
> ++ {
> ++ unsigned char *pstart = p;
> ++
> ++ if (!(p = skip_name(p, header, plen, 10)))
> ++ return 0;
> ++
> ++ GETSHORT(type, p);
> ++ GETSHORT(class, p);
> ++ p += 4; /* TTL */
> ++ GETSHORT(rdlen, p);
> ++
> ++ if (class == qclass && (type == T_NSEC || type == T_NSEC3))
> ++ {
> ++ /* No mixed NSECing 'round here, thankyouverymuch */
> ++ if (type_found != 0 && type_found != type)
> ++ return 0;
> ++
> ++ type_found = type;
> ++
> ++ if (!expand_workspace(&nsecset, &nsecset_sz,
> nsecs_found))
> ++ return 0;
> ++
> ++ nsecset[nsecs_found++] = pstart;
> ++ }
> ++
> ++ if (!ADD_RDLEN(header, p, plen, rdlen))
> ++ return 0;
> ++ }
> ++
> ++ if (type_found == T_NSEC)
> ++ return prove_non_existence_nsec(header, plen, nsecset,
> nsecs_found, daemon->workspacename, keyname, name, qtype, nons);
> ++ else
> ++ return prove_non_existence_nsec3(header, plen, nsecset,
> nsecs_found, daemon->workspacename, keyname, name, qtype, wildname,
> nons);
> + }
> +
> + /* Check signing status of name.
> +@@ -1925,10 +1921,9 @@ int dnssec_validate_reply(time_t now, struct
> dns_header *header, size_t plen, ch
> + static unsigned char **targets = NULL;
> + static int target_sz = 0;
> +
> +- unsigned char *ans_start, *p1, *p2, **nsecs;
> ++ unsigned char *ans_start, *p1, *p2;
> + int type1, class1, rdlen1, type2, class2, rdlen2, qclass, qtype,
> targetidx;
> +- int i, j, rc, nsec_count;
> +- int nsec_type;
> ++ int i, j, rc;
> +
> + if (neganswer)
> + *neganswer = 0;
> +@@ -2080,28 +2075,15 @@ int dnssec_validate_reply(time_t now, struct
> dns_header *header, size_t plen, ch
> + targets[j] = NULL;
> + }
> +
> +- if (rc == STAT_SECURE_WILDCARD)
> +- {
> +- /* An attacker replay a wildcard answer with
> a different
> +- answer and overlay a genuine RR. To prove
> this
> +- hasn't happened, the answer must prove
> that
> +- the gennuine record doesn't exist. Check
> that here. */
> +- if (!(nsec_type = find_nsec_records(header,
> plen, &nsecs, &nsec_count, class1)))
> +- return STAT_BOGUS; /* No NSECs or bad
> packet */
> +-
> +- /* Note that we may not yet have validated
> the NSEC/NSEC3 RRsets. Since the check
> +- below returns either SECURE or BOGUS,
> that's not a problem. If the RRsets later fail
> +- we'll return BOGUS then. */
> +-
> +- if (nsec_type == T_NSEC)
> +- rc = prove_non_existence_nsec(header, plen,
> nsecs, nsec_count, daemon->workspacename, keyname, name, type1,
> NULL);
> +- else
> +- rc = prove_non_existence_nsec3(header,
> plen, nsecs, nsec_count, daemon->workspacename,
> +- keyname,
> name, type1, wildname, NULL);
> +-
> +- if (rc == STAT_BOGUS)
> +- return rc;
> +- }
> ++ /* An attacker replay a wildcard answer with a
> different
> ++ answer and overlay a genuine RR. To prove
> this
> ++ hasn't happened, the answer must prove that
> ++ the gennuine record doesn't exist. Check that
> here.
> ++ Note that we may not yet have validated the
> NSEC/NSEC3 RRsets.
> ++ That's not a problem since if the RRsets
> later fail
> ++ we'll return BOGUS then. */
> ++ if (rc == STAT_SECURE_WILDCARD &&
> !prove_non_existence(header, plen, keyname, name, type1, class1,
> wildname, NULL))
> ++ return STAT_BOGUS;
> + }
> + }
> + }
> +@@ -2124,14 +2106,13 @@ int dnssec_validate_reply(time_t now, struct
> dns_header *header, size_t plen, ch
> +
> + /* For anything other than a DS record, this situation is
> OK if either
> + the answer is in an unsigned zone, or there's a NSEC
> records. */
> +- if (!(nsec_type = find_nsec_records(header, plen, &nsecs,
> &nsec_count, qclass)))
> ++ if (!prove_non_existence(header, plen, keyname, name,
> qtype, qclass, NULL, nons))
> + {
> + /* Empty DS without NSECS */
> + if (qtype == T_DS)
> + return STAT_BOGUS;
> +
> +- rc = zone_status(name, qclass, keyname, now);
> +- if (rc != STAT_SECURE)
> ++ if ((rc = zone_status(name, qclass, keyname, now)) !=
> STAT_SECURE)
> + {
> + if (class)
> + *class = qclass; /* Class for NEED_DS or
> NEED_DNSKEY */
> +@@ -2140,14 +2121,6 @@ int dnssec_validate_reply(time_t now, struct
> dns_header *header, size_t plen, ch
> +
> + return STAT_BOGUS; /* signed zone, no NSECs */
> + }
> +-
> +- if (nsec_type == T_NSEC)
> +- rc = prove_non_existence_nsec(header, plen, nsecs,
> nsec_count, daemon->workspacename, keyname, name, qtype, nons);
> +- else
> +- rc = prove_non_existence_nsec3(header, plen, nsecs,
> nsec_count, daemon->workspacename, keyname, name, qtype, NULL, nons);
> +-
> +- if (rc != STAT_SECURE)
> +- return rc;
> + }
> +
> + return STAT_SECURE;
> +--
> +1.7.10.4
> +
> diff --git a/src/patches/dnsmasq/023
> -Fix_brace_botch_in_dnssec_validate_ds.patch
> b/src/patches/dnsmasq/023-Fix_brace_botch_in_dnssec_validate_ds.patch
> new file mode 100644
> index 0000000..eda6fbd
> --- /dev/null
> +++ b/src/patches/dnsmasq/023
> -Fix_brace_botch_in_dnssec_validate_ds.patch
> @@ -0,0 +1,98 @@
> +From 3b799c826db05fc2da1c6d15cbe372e394209d27 Mon Sep 17 00:00:00
> 2001
> +From: Simon Kelley <simon(a)thekelleys.org.uk>
> +Date: Thu, 17 Dec 2015 16:58:04 +0000
> +Subject: [PATCH] Fix brace botch in dnssec_validate_ds()
> +MIME-Version: 1.0
> +Content-Type: text/plain; charset=utf8
> +Content-Transfer-Encoding: 8bit
> +
> +Thanks to Michał Kępień for spotting this.
> +---
> + src/dnssec.c | 34 +++++++++++++++++-----------------
> + 1 file changed, 17 insertions(+), 17 deletions(-)
> +
> +diff --git a/src/dnssec.c b/src/dnssec.c
> +index ddae497..1f8c954 100644
> +--- a/src/dnssec.c
> ++++ b/src/dnssec.c
> +@@ -923,11 +923,11 @@ static int validate_rrset(time_t now, struct
> dns_header *header, size_t plen, in
> + /* The DNS packet is expected to contain the answer to a DNSKEY
> query.
> + Put all DNSKEYs in the answer which are valid into the cache.
> + return codes:
> +- STAT_OK Done, key(s) in cache.
> +- STAT_BOGUS No DNSKEYs found, which can be
> validated with DS,
> +- or self-sign for DNSKEY RRset is not
> valid, bad packet.
> +- STAT_NEED_DS DS records to validate a key not found,
> name in keyname
> +- STAT_NEED_DNSKEY DNSKEY records to validate a key not
> found, name in keyname
> ++ STAT_OK Done, key(s) in cache.
> ++ STAT_BOGUS No DNSKEYs found, which can be validated
> with DS,
> ++ or self-sign for DNSKEY RRset is not valid,
> bad packet.
> ++ STAT_NEED_DS DS records to validate a key not found,
> name in keyname
> ++ STAT_NEED_KEY DNSKEY records to validate a key not found,
> name in keyname
> + */
> + int dnssec_validate_by_ds(time_t now, struct dns_header *header,
> size_t plen, char *name, char *keyname, int class)
> + {
> +@@ -1224,13 +1224,13 @@ int dnssec_validate_ds(time_t now, struct
> dns_header *header, size_t plen, char
> + }
> +
> + p = psave;
> +-
> +- if (!ADD_RDLEN(header, p, plen, rdlen))
> +- return STAT_BOGUS; /* bad packet */
> + }
> +-
> +- cache_end_insert();
> ++ if (!ADD_RDLEN(header, p, plen, rdlen))
> ++ return STAT_BOGUS; /* bad packet */
> + }
> ++
> ++ cache_end_insert();
> ++
> + }
> + else
> + {
> +@@ -1828,10 +1828,10 @@ static int prove_non_existence(struct
> dns_header *header, size_t plen, char *key
> +
> + /* Check signing status of name.
> + returns:
> +- STAT_SECURE zone is signed.
> +- STAT_INSECURE zone proved unsigned.
> +- STAT_NEED_DS require DS record of name returned in keyname.
> +- STAT_NEED_DNSKEY require DNSKEY record of name returned in
> keyname.
> ++ STAT_SECURE zone is signed.
> ++ STAT_INSECURE zone proved unsigned.
> ++ STAT_NEED_DS require DS record of name returned in keyname.
> ++ STAT_NEED_KEY require DNSKEY record of name returned in keyname.
> + name returned unaltered.
> + */
> + static int zone_status(char *name, int class, char *keyname, time_t
> now)
> +@@ -2028,7 +2028,7 @@ int dnssec_validate_reply(time_t now, struct
> dns_header *header, size_t plen, ch
> + if (rc == STAT_SECURE)
> + rc = STAT_BOGUS;
> + if (class)
> +- *class = class1; /* Class for NEED_DS or
> NEED_DNSKEY */
> ++ *class = class1; /* Class for NEED_DS or
> NEED_KEY */
> + }
> + else
> + rc = STAT_INSECURE;
> +@@ -2045,7 +2045,7 @@ int dnssec_validate_reply(time_t now, struct
> dns_header *header, size_t plen, ch
> + {
> + /* Zone is insecure, don't need to validate RRset
> */
> + if (class)
> +- *class = class1; /* Class for NEED_DS or
> NEED_DNSKEY */
> ++ *class = class1; /* Class for NEED_DS or
> NEED_KEY */
> + return rc;
> + }
> +
> +@@ -2115,7 +2115,7 @@ int dnssec_validate_reply(time_t now, struct
> dns_header *header, size_t plen, ch
> + if ((rc = zone_status(name, qclass, keyname, now)) !=
> STAT_SECURE)
> + {
> + if (class)
> +- *class = qclass; /* Class for NEED_DS or
> NEED_DNSKEY */
> ++ *class = qclass; /* Class for NEED_DS or NEED_KEY
> */
> + return rc;
> + }
> +
> +--
> +1.7.10.4
> +
> diff --git a/src/patches/dnsmasq/024
> -Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.
> patch b/src/patches/dnsmasq/024
> -Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.
> patch
> new file mode 100644
> index 0000000..abcae5c
> --- /dev/null
> +++ b/src/patches/dnsmasq/024
> -Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.
> patch
> @@ -0,0 +1,145 @@
> +From 14a4ae883d51130d33da7133287e8867c64bab65 Mon Sep 17 00:00:00
> 2001
> +From: Simon Kelley <simon(a)thekelleys.org.uk>
> +Date: Thu, 17 Dec 2015 17:23:03 +0000
> +Subject: [PATCH] Do a better job of determining which DNSSEC sig
> algos are
> + supported.
> +
> +---
> + src/dnssec.c | 52 +++++++++++++++++++++++++++++++++++++----------
> -----
> + 1 file changed, 37 insertions(+), 15 deletions(-)
> +
> +diff --git a/src/dnssec.c b/src/dnssec.c
> +index 1f8c954..82394ee 100644
> +--- a/src/dnssec.c
> ++++ b/src/dnssec.c
> +@@ -65,10 +65,9 @@ static char *algo_digest_name(int algo)
> + case 8: return "sha256";
> + case 10: return "sha512";
> + case 12: return "gosthash94";
> +-#ifndef NO_NETTLE_ECC
> + case 13: return "sha256";
> + case 14: return "sha384";
> +-#endif
> ++
> + default: return NULL;
> + }
> + }
> +@@ -129,13 +128,15 @@ static int hash_init(const struct nettle_hash
> *hash, void **ctxp, unsigned char
> + }
> +
> + static int dnsmasq_rsa_verify(struct blockdata *key_data, unsigned
> int key_len, unsigned char *sig, size_t sig_len,
> +- unsigned char *digest, int algo)
> ++ unsigned char *digest, size_t
> digest_len, int algo)
> + {
> + unsigned char *p;
> + size_t exp_len;
> +
> + static struct rsa_public_key *key = NULL;
> + static mpz_t sig_mpz;
> ++
> ++ (void)digest_len;
> +
> + if (key == NULL)
> + {
> +@@ -181,7 +182,7 @@ static int dnsmasq_rsa_verify(struct blockdata
> *key_data, unsigned int key_len,
> + }
> +
> + static int dnsmasq_dsa_verify(struct blockdata *key_data, unsigned
> int key_len, unsigned char *sig, size_t sig_len,
> +- unsigned char *digest, int algo)
> ++ unsigned char *digest, size_t
> digest_len, int algo)
> + {
> + unsigned char *p;
> + unsigned int t;
> +@@ -189,6 +190,8 @@ static int dnsmasq_dsa_verify(struct blockdata
> *key_data, unsigned int key_len,
> + static struct dsa_public_key *key = NULL;
> + static struct dsa_signature *sig_struct;
> +
> ++ (void)digest_len;
> ++
> + if (key == NULL)
> + {
> + if (!(sig_struct = whine_malloc(sizeof(struct
> dsa_signature))) ||
> +@@ -292,26 +295,45 @@ static int dnsmasq_ecdsa_verify(struct
> blockdata *key_data, unsigned int key_len
> + }
> + #endif
> +
> +-static int 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)
> ++static int (*verify_func(int algo))(struct blockdata *key_data,
> unsigned int key_len, unsigned char *sig, size_t sig_len,
> ++ unsigned char *digest, size_t
> digest_len, int algo)
> + {
> +- (void)digest_len;
> +-
> ++
> ++ /* Enure at runtime that we have support for this digest */
> ++ if (!hash_find(algo_digest_name(algo)))
> ++ return NULL;
> ++
> ++ /* This switch defines which sig algorithms we support, can't
> introspect Nettle for that. */
> + switch (algo)
> + {
> + case 1: case 5: case 7: case 8: case 10:
> +- return dnsmasq_rsa_verify(key_data, key_len, sig, sig_len,
> digest, algo);
> ++ return dnsmasq_rsa_verify;
> +
> + case 3: case 6:
> +- return dnsmasq_dsa_verify(key_data, key_len, sig, sig_len,
> digest, algo);
> ++ return dnsmasq_dsa_verify;
> +
> + #ifndef NO_NETTLE_ECC
> + case 13: case 14:
> +- return dnsmasq_ecdsa_verify(key_data, key_len, sig, sig_len,
> digest, digest_len, algo);
> ++ return dnsmasq_ecdsa_verify;
> + #endif
> + }
> +
> +- return 0;
> ++ return NULL;
> ++}
> ++
> ++static int 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)
> ++{
> ++
> ++ int (*func)(struct blockdata *key_data, unsigned int key_len,
> unsigned char *sig, size_t sig_len,
> ++ unsigned char *digest, size_t digest_len, int algo);
> ++
> ++ func = verify_func(algo);
> ++
> ++ if (!func)
> ++ return 0;
> ++
> ++ return (*func)(key_data, key_len, sig, sig_len, digest,
> digest_len, algo);
> + }
> +
> + /* Convert from presentation format to wire format, in place.
> +@@ -732,7 +754,7 @@ static int explore_rrset(struct dns_header
> *header, size_t plen, int class, int
> + if (check_date_range(sig_inception, sig_expiration)
> &&
> + labels <= name_labels &&
> + type_covered == type &&
> +- algo_digest_name(algo))
> ++ verify_func(algo))
> + {
> + if (!expand_workspace(&sigs, &sig_sz, sigidx))
> + return 0;
> +@@ -1865,7 +1887,7 @@ static int zone_status(char *name, int class,
> char *keyname, time_t now)
> + if (crecp->flags & F_DNSSECOK)
> + return STAT_INSECURE; /* proved no DS here
> */
> + }
> +- else if (!ds_digest_name(crecp->addr.ds.digest)
> || !algo_digest_name(crecp->addr.ds.algo))
> ++ else if (!hash_find(ds_digest_name(crecp
> ->addr.ds.digest)) || !verify_func(crecp->addr.ds.algo))
> + return STAT_INSECURE; /* algo we can't use -
> insecure */
> + else
> + secure_ds = 1;
> +@@ -1887,7 +1909,7 @@ static int zone_status(char *name, int class,
> char *keyname, time_t now)
> +
> + do
> + {
> +- if (crecp->uid == (unsigned int)class &&
> !algo_digest_name(crecp->addr.key.algo))
> ++ if (crecp->uid == (unsigned int)class &&
> !verify_func(crecp->addr.key.algo))
> + return STAT_INSECURE;
> + }
> + while ((crecp = cache_find_by_name(crecp, keyname, now,
> F_DNSKEY)));
> +--
> +1.7.10.4
> +
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
next prev parent reply other threads:[~2015-12-18 15:30 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-12-18 14:11 Matthias Fischer
2015-12-18 15:30 ` Michael Tremer [this message]
2015-12-18 16:58 ` Matthias Fischer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1450452621.31655.209.camel@ipfire.org \
--to=michael.tremer@ipfire.org \
--cc=development@lists.ipfire.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox