From e5412459871a0d4095aa6058791d2b9005474645 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Sat, 6 Jan 2018 22:16:31 +0000 Subject: [PATCH] Handle duplicate RRs in DNSSEC validation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RFC 4034 says: [RFC2181] specifies that an RRset is not allowed to contain duplicate records (multiple RRs with the same owner name, class, type, and RDATA). Therefore, if an implementation detects duplicate RRs when putting the RRset in canonical form, it MUST treat this as a protocol error. If the implementation chooses to handle this protocol error in the spirit of the robustness principle (being liberal in what it accepts), it MUST remove all but one of the duplicate RR(s) for the purposes of calculating the canonical form of the RRset. We chose to handle this robustly, having found at least one recursive server in the wild which returns duplicate NSEC records in the AUTHORITY section of an answer generated from a wildcard record. sort_rrset() is therefore modified to delete duplicate RRs which are detected almost for free during the bubble-sort process. Thanks to Toralf Förster for helping to diagnose this problem. --- src/dnssec.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index 6abc1d7..81eaa9e 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -277,10 +277,10 @@ static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, leaving the following bytes as deciding the order. Hence the nasty left1 and left2 variables. */ -static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int rrsetidx, - unsigned char **rrset, char *buff1, char *buff2) +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; + int swap, quit, i, j; do { @@ -342,11 +342,21 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int 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--; + } else if (rc < 0) quit = 1; } } } while (swap); + + return rrsetidx; } static unsigned char **rrset = NULL, **sigs = NULL; @@ -491,7 +501,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in /* Sort RRset records into canonical order. Note that at this point keyname and daemon->workspacename buffs are unused, and used as workspace by the sort. */ - sort_rrset(header, plen, rr_desc, rrsetidx, rrset, daemon->workspacename, keyname); + rrsetidx = sort_rrset(header, plen, rr_desc, rrsetidx, rrset, daemon->workspacename, keyname); /* Now try all the sigs to try and find one which validates */ for (j = 0; j