Disable UPDATE and NOTIFY for non-IN classes

Return NOTIMP for UPDATE and NOTIFY requests received for views with a
class other than IN.  Only QUERY is now supported for non-IN views such
as CHAOS.

When running dns dns_rdata_tostruct() with types that are only defined
for class IN, ensure that the class is correct before proceeding.

Add an assertion that any zone being updated is of class IN. (Note
that previously, a DLZ zone could have its class value set incorrectly
to NONE; this has been fixed.)

This addresses YWH-PGM40640-70 and YWH-PGM40640-73 (as well as any
similar problems that might have occurred in the future) by minimizing
the code paths that can be reached by rdata classes other than IN, so it
is safe for the implementation to assume that rdatatypes that are only
defined for class IN, such as SVCB or WKS, have been parsed and
validated, and not accepted as unknown/opaque data.

Fixes: isc-projects/bind9#5777
Fixes: isc-projects/bind9#5779
This commit is contained in:
Evan Hunt 2026-03-04 13:24:52 -08:00 committed by Michał Kępień
parent bfb027fecd
commit 6ba5e87a08
No known key found for this signature in database
4 changed files with 34 additions and 22 deletions

View file

@ -1715,6 +1715,7 @@ dlzconfigure_callback(dns_view_t *view, dns_dlzdb_t *dlzdb, dns_zone_t *zone) {
dns_name_t *origin = dns_zone_getorigin(zone);
dns_rdataclass_t zclass = view->rdclass;
dns_zone_setclass(zone, zclass);
RETERR(dns_zonemgr_managezone(named_g_server->zonemgr, zone));
dns_zone_setstats(zone, named_g_server->zonestats);

View file

@ -566,6 +566,9 @@ import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset,
rdtype = rdataset->type;
REQUIRE(rdataset->rdclass == dns_rdataclass_in);
REQUIRE(dns_rdatatype_isaddr(rdtype));
switch (rdataset->trust) {
case dns_trust_glue:
case dns_trust_additional:
@ -584,8 +587,6 @@ import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset,
rdataset->ttl = ttlclamp(rdataset->ttl);
}
REQUIRE(dns_rdatatype_isaddr(rdtype));
DNS_RDATASET_FOREACH(rdataset) {
/* FIXME: Move to a separate function */
dns_adbnamehooklist_t *hookhead = NULL;

View file

@ -2459,6 +2459,10 @@ ns_client_request_continue(void *arg) {
break;
case dns_opcode_update:
CTRACE("update");
if (client->inner.view->rdclass != dns_rdataclass_in) {
ns_client_error(client, DNS_R_NOTIMP);
break;
}
#ifdef HAVE_DNSTAP
dns_dt_send(client->inner.view, DNS_DTTYPE_UQ,
&client->inner.peeraddr,
@ -2472,6 +2476,10 @@ ns_client_request_continue(void *arg) {
break;
case dns_opcode_notify:
CTRACE("notify");
if (client->inner.view->rdclass != dns_rdataclass_in) {
ns_client_error(client, DNS_R_NOTIMP);
break;
}
ns_client_settimeout(client, 60);
ns_notify_start(client, client->inner.handle);
break;

View file

@ -966,7 +966,9 @@ ssu_checkrr(void *data, rr_t *rr) {
RUNTIME_CHECK(result == ISC_R_SUCCESS);
target = &ptr.ptr;
}
if (rr->rdata.type == dns_rdatatype_srv) {
if (rr->rdata.rdclass == dns_rdataclass_in &&
rr->rdata.type == dns_rdatatype_srv)
{
result = dns_rdata_tostruct(&rr->rdata, &srv, NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
target = &srv.target;
@ -1311,7 +1313,10 @@ replaces_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
return true;
}
}
if (db_rr->type == dns_rdatatype_wks) {
if (db_rr->rdclass == dns_rdataclass_in &&
db_rr->type == dns_rdatatype_wks)
{
/*
* Compare the address and protocol fields only. These
* form the first five bytes of the RR data. Do a
@ -1451,9 +1456,8 @@ add_rr_prepare_action(void *data, rr_t *rr) {
* 'rdata', and 'ttl', respectively.
*/
static void
get_current_rr(dns_rdataclass_t zoneclass, dns_name_t *name, dns_rdata_t *rdata,
dns_rdatatype_t *covers, dns_ttl_t *ttl,
dns_rdataclass_t *update_class) {
get_current_rr(dns_name_t *name, dns_rdata_t *rdata, dns_rdatatype_t *covers,
dns_ttl_t *ttl, dns_rdataclass_t *update_class) {
dns_rdataset_t *rdataset;
isc_result_t result;
rdataset = ISC_LIST_HEAD(name->list);
@ -1466,7 +1470,7 @@ get_current_rr(dns_rdataclass_t zoneclass, dns_name_t *name, dns_rdata_t *rdata,
dns_rdataset_current(rdataset, rdata);
INSIST(dns_rdataset_next(rdataset) == ISC_R_NOMORE);
*update_class = rdata->rdclass;
rdata->rdclass = zoneclass;
rdata->rdclass = dns_rdataclass_in;
}
/*%
@ -1562,7 +1566,6 @@ send_update(ns_client_t *client, dns_zone_t *zone) {
dns_message_t *request = client->message;
isc_mem_t *mctx = client->manager->mctx;
dns_aclenv_t *env = client->manager->aclenv;
dns_rdataclass_t zoneclass;
dns_rdatatype_t covers;
dns_name_t *zonename = NULL;
unsigned int *maxbytype = NULL;
@ -1574,11 +1577,13 @@ send_update(ns_client_t *client, dns_zone_t *zone) {
CHECK(dns_zone_getdb(zone, &db));
zonename = dns_db_origin(db);
zoneclass = dns_db_class(db);
dns_zone_getssutable(zone, &ssutable);
options = dns_zone_getoptions(zone);
dns_db_currentversion(db, &ver);
/* Updates are only supported for class IN. */
INSIST(dns_zone_getclass(zone) == dns_rdataclass_in);
/*
* Update message processing can leak record existence information
* so check that we are allowed to query this zone. Additionally,
@ -1623,13 +1628,12 @@ send_update(ns_client_t *client, dns_zone_t *zone) {
dns_rdataclass_t update_class;
INSIST(ssutable == NULL || update < maxbytypelen);
get_current_rr(zoneclass, name, &rdata, &covers, &ttl,
&update_class);
get_current_rr(name, &rdata, &covers, &ttl, &update_class);
if (!dns_name_issubdomain(name, zonename)) {
FAILC(DNS_R_NOTZONE, "update RR is outside zone");
}
if (update_class == zoneclass) {
if (update_class == dns_rdataclass_in) {
/*
* Check for meta-RRs. The RFC2136 pseudocode says
* check for ANY|AXFR|MAILA|MAILB, but the text adds
@ -1643,6 +1647,7 @@ send_update(ns_client_t *client, dns_zone_t *zone) {
CLEANUP(DNS_R_REFUSED);
}
if ((options & DNS_ZONEOPT_CHECKSVCB) != 0 &&
rdata.rdclass == dns_rdataclass_in &&
rdata.type == dns_rdatatype_svcb)
{
result = dns_rdata_checksvcb(name, &rdata);
@ -1731,7 +1736,6 @@ send_update(ns_client_t *client, dns_zone_t *zone) {
}
if (update_class == dns_rdataclass_any &&
zoneclass == dns_rdataclass_in &&
(rdata.type == dns_rdatatype_ptr ||
rdata.type == dns_rdatatype_srv))
{
@ -2629,7 +2633,6 @@ update_action(void *arg) {
isc_mem_t *mctx = client->manager->mctx;
dns_rdatatype_t covers;
dns_message_t *request = client->message;
dns_rdataclass_t zoneclass;
dns_name_t *zonename = NULL;
dns_fixedname_t tmpnamefixed;
dns_name_t *tmpname = NULL;
@ -2646,9 +2649,10 @@ update_action(void *arg) {
CHECK(dns_zone_getdb(zone, &db));
zonename = dns_db_origin(db);
zoneclass = dns_db_class(db);
options = dns_zone_getoptions(zone);
INSIST(dns_zone_getclass(zone) == dns_rdataclass_in);
is_inline = (!dns_zone_israw(zone) && dns_zone_issecure(zone));
is_maintain = (dns_zone_getkasp(zone) != NULL) && !dns_zone_israw(zone);
is_signing = is_inline || is_maintain;
@ -2669,8 +2673,7 @@ update_action(void *arg) {
dns_rdataclass_t update_class;
bool flag;
get_current_rr(zoneclass, name, &rdata, &covers, &ttl,
&update_class);
get_current_rr(name, &rdata, &covers, &ttl, &update_class);
if (ttl != 0) {
PREREQFAILC(DNS_R_FORMERR,
@ -2733,7 +2736,7 @@ update_action(void *arg) {
"prerequisite not satisfied");
}
}
} else if (update_class == zoneclass) {
} else if (update_class == dns_rdataclass_in) {
/* "temp<rr.name, rr.type> += rr;" */
temp_append(&temp, name, &rdata);
} else {
@ -2784,10 +2787,9 @@ update_action(void *arg) {
INSIST(ssutable == NULL || maxidx < maxbytypelen);
get_current_rr(zoneclass, name, &rdata, &covers, &ttl,
&update_class);
get_current_rr(name, &rdata, &covers, &ttl, &update_class);
if (update_class == zoneclass) {
if (update_class == dns_rdataclass_in) {
/*
* RFC1123 doesn't allow MF and MD in master files.
*/