diff --git a/CHANGES b/CHANGES index ad7b586591..2798122996 100644 --- a/CHANGES +++ b/CHANGES @@ -45,7 +45,12 @@ 5616. [placeholder] -5615. [placeholder] +5615. [security] Insufficient IXFR checks could result in named serving a + zone without an SOA record at the apex, leading to a + RUNTIME_CHECK assertion failure when the zone was + subsequently refreshed. This has been fixed by adding an + owner name check for all SOA records which are included + in a zone transfer. (CVE-2021-25214) [GL #2467] 5614. [bug] Ensure all resources are properly cleaned up when a call to gss_accept_sec_context() fails. [GL #2620] diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index 23f122f192..b559bccf92 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -14,7 +14,11 @@ Notes for BIND 9.17.12 Security Fixes ~~~~~~~~~~~~~~ -- None. +- A malformed incoming IXFR transfer could trigger an assertion failure + in ``named``, causing it to quit abnormally. (CVE-2021-25214) + + ISC would like to thank Greg Kuechle of SaskTel for bringing this + vulnerability to our attention. [GL #2467] Known Issues ~~~~~~~~~~~~ diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index f9c93416ed..0e4872d1bc 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -6798,6 +6798,13 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb); if (rbtdb->common.methods == &zone_methods) { + /* + * SOA records are only allowed at top of zone. + */ + if (rdataset->type == dns_rdatatype_soa && + node != rbtdb->origin_node) { + return (DNS_R_NOTZONETOP); + } RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); REQUIRE(((rbtnode->nsec == DNS_RBT_NSEC_NSEC3 && (rdataset->type == dns_rdatatype_nsec3 || diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index 5a950ae0eb..044acc09c3 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -498,6 +498,20 @@ xfr_rr(dns_xfrin_ctx_t *xfr, dns_name_t *name, uint32_t ttl, FAIL(DNS_R_FORMERR); } + /* + * Immediately reject the entire transfer if the RR that is currently + * being processed is an SOA record that is not placed at the zone + * apex. + */ + if (rdata->type == dns_rdatatype_soa && + !dns_name_equal(&xfr->name, name)) { + char namebuf[DNS_NAME_FORMATSIZE]; + dns_name_format(name, namebuf, sizeof(namebuf)); + xfrin_log(xfr, ISC_LOG_DEBUG(3), "SOA name mismatch: '%s'", + namebuf); + FAIL(DNS_R_NOTZONETOP); + } + redo: switch (xfr->state) { case XFRST_SOAQUERY: diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 24d5849f61..00e6dd2f4e 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -17156,9 +17156,16 @@ again: if (soacount != 1) { dns_zone_log(zone, ISC_LOG_ERROR, "transferred zone " - "has %d SOA record%s", - soacount, - (soacount != 0) ? "s" : ""); + "has %d SOA records", + soacount); + if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_HAVETIMERS)) + { + zone->refresh = DNS_ZONE_DEFAULTREFRESH; + zone->retry = DNS_ZONE_DEFAULTRETRY; + } + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_HAVETIMERS); + zone_unload(zone); + goto next_master; } if (nscount == 0) { dns_zone_log(zone, ISC_LOG_ERROR,