From 254d55749ccb1129e7d021a51d0c3b7d3da26ee1 Mon Sep 17 00:00:00 2001 From: Sona Sarmadi Date: Tue, 12 Sep 2017 14:13:28 +0200 Subject: [PATCH] CVE-2016-9444 An unusually-formed DS record response could cause an assertion failure CVE: CVE-2016-9444 Upstream-Status: Backport [backport from remotes/origin/v9_10_6_patch https://source.isc.org/cgi-bin/gitweb.cgi?p=bind9.git;a=commit;h=04c7ee66b1eda851737cc7582a2a88193a0b4118] Signed-off-by: Sona Sarmadi --- CHANGES | 4 +++ lib/dns/message.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++--- lib/dns/resolver.c | 21 ++++++--------- 3 files changed, 85 insertions(+), 16 deletions(-) diff --git a/CHANGES b/CHANGES index 97d2e60..5760ca5 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +4517. [security] Named could mishandle authority sections that were + missing RRSIGs triggering an assertion failure. + (CVE-2016-9444) [RT # 43632] + 4504. [security] Allow the maximum number of records in a zone to be specified. This provides a control for issues raised in CVE-2016-6170. [RT #42143] diff --git a/lib/dns/message.c b/lib/dns/message.c index 0dd4c77..2e37dac 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -1171,6 +1171,63 @@ update(dns_section_t section, dns_rdataclass_t rdclass) { return (ISC_FALSE); } +/* + * Check to confirm that all DNSSEC records (DS, NSEC, NSEC3) have + * covering RRSIGs. + */ +static isc_boolean_t +auth_signed(dns_namelist_t *section) { + dns_name_t *name; + + for (name = ISC_LIST_HEAD(*section); + name != NULL; + name = ISC_LIST_NEXT(name, link)) + { + int auth_dnssec = 0, auth_rrsig = 0; + dns_rdataset_t *rds; + + for (rds = ISC_LIST_HEAD(name->list); + rds != NULL; + rds = ISC_LIST_NEXT(rds, link)) + { + switch (rds->type) { + case dns_rdatatype_ds: + auth_dnssec |= 0x1; + break; + case dns_rdatatype_nsec: + auth_dnssec |= 0x2; + break; + case dns_rdatatype_nsec3: + auth_dnssec |= 0x4; + break; + case dns_rdatatype_rrsig: + break; + default: + continue; + } + + switch (rds->covers) { + case dns_rdatatype_ds: + auth_rrsig |= 0x1; + break; + case dns_rdatatype_nsec: + auth_rrsig |= 0x2; + break; + case dns_rdatatype_nsec3: + auth_rrsig |= 0x4; + break; + default: + break; + } + } + + if (auth_dnssec != auth_rrsig) + return (ISC_FALSE); + } + + return (ISC_TRUE); +} + static isc_result_t getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, dns_section_t sectionid, unsigned int options) @@ -1196,12 +1253,12 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, best_effort = ISC_TF(options & DNS_MESSAGEPARSE_BESTEFFORT); seen_problem = ISC_FALSE; + section = &msg->sections[sectionid]; + for (count = 0; count < msg->counts[sectionid]; count++) { int recstart = source->current; isc_boolean_t skip_name_search, skip_type_search; - section = &msg->sections[sectionid]; - skip_name_search = ISC_FALSE; skip_type_search = ISC_FALSE; free_rdataset = ISC_FALSE; @@ -1364,7 +1421,7 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, goto cleanup; rdata->rdclass = rdclass; issigzero = ISC_FALSE; - if (rdtype == dns_rdatatype_rrsig && + if (rdtype == dns_rdatatype_rrsig && rdata->flags == 0) { covers = dns_rdata_covers(rdata); if (covers == 0) @@ -1575,6 +1632,19 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, INSIST(free_rdataset == ISC_FALSE); } + /* + * If any of DS, NSEC or NSEC3 appeared in the + * authority section of a query response without + * a covering RRSIG, FORMERR + */ + if (sectionid == DNS_SECTION_AUTHORITY && + msg->opcode == dns_opcode_query && + ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) && + ((msg->flags & DNS_MESSAGEFLAG_TC) == 0) && + !preserve_order && + !auth_signed(section)) + DO_ERROR(DNS_R_FORMERR); + if (seen_problem) return (DNS_R_RECOVERABLE); return (ISC_R_SUCCESS); diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index b8fa6b3..017b4ba 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -5435,16 +5435,13 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo, rdataset->type, &noqname); if (tresult == ISC_R_SUCCESS && - noqname != NULL) { - tresult = - dns_rdataset_addnoqname( + noqname != NULL) + (void) dns_rdataset_addnoqname( rdataset, noqname); - RUNTIME_CHECK(tresult == - ISC_R_SUCCESS); - } } - if ((fctx->options & DNS_FETCHOPT_PREFETCH) != 0) - options = DNS_DBADD_PREFETCH; + if ((fctx->options & + DNS_FETCHOPT_PREFETCH) != 0) + options = DNS_DBADD_PREFETCH; addedrdataset = ardataset; result = dns_db_addrdataset(fctx->cache, node, NULL, now, rdataset, @@ -5584,11 +5581,9 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo, tresult = findnoqname(fctx, name, rdataset->type, &noqname); if (tresult == ISC_R_SUCCESS && - noqname != NULL) { - tresult = dns_rdataset_addnoqname( - rdataset, noqname); - RUNTIME_CHECK(tresult == ISC_R_SUCCESS); - } + noqname != NULL) + (void) dns_rdataset_addnoqname( + rdataset, noqname); } /* -- 1.9.1