From 919a9ece25be4ab8673bf104be7c18b7cef6a346 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 4 Jul 2019 15:24:20 +1000 Subject: [PATCH] enforce record count maximums --- lib/dns/include/dns/ssu.h | 13 +++++++++++++ lib/dns/ssu.c | 18 ++++++++++++++++++ lib/dns/win32/libdns.def.in | 3 ++- lib/ns/update.c | 31 +++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 1 deletion(-) diff --git a/lib/dns/include/dns/ssu.h b/lib/dns/include/dns/ssu.h index b3a6e829e0..cc463a9d6b 100644 --- a/lib/dns/include/dns/ssu.h +++ b/lib/dns/include/dns/ssu.h @@ -185,19 +185,32 @@ dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer, /*% Accessor functions to extract rule components */ bool dns_ssurule_isgrant(const dns_ssurule_t *rule); + /*% Accessor functions to extract rule components */ dns_name_t * dns_ssurule_identity(const dns_ssurule_t *rule); + /*% Accessor functions to extract rule components */ unsigned int dns_ssurule_matchtype(const dns_ssurule_t *rule); + /*% Accessor functions to extract rule components */ dns_name_t * dns_ssurule_name(const dns_ssurule_t *rule); + /*% Accessor functions to extract rule components */ unsigned int dns_ssurule_types(const dns_ssurule_t *rule, dns_ssuruletype_t **types); +unsigned int +dns_ssurule_max(const dns_ssurule_t *rule, dns_rdatatype_t type); +/*%< + * Returns the maximum number of records configured for type `type`. + * If no maximum has been configured for `type` but one has been + * configured for ANY, return that value instead. Otherwise, return + * zero, which implies "unlimited". + */ + isc_result_t dns_ssutable_firstrule(const dns_ssutable_t *table, dns_ssurule_t **rule); /*%< diff --git a/lib/dns/ssu.c b/lib/dns/ssu.c index 39de705776..9a601febe8 100644 --- a/lib/dns/ssu.c +++ b/lib/dns/ssu.c @@ -562,6 +562,24 @@ dns_ssurule_types(const dns_ssurule_t *rule, dns_ssuruletype_t **types) { return (rule->ntypes); } +unsigned int +dns_ssurule_max(const dns_ssurule_t *rule, dns_rdatatype_t type) { + unsigned int i; + unsigned int max = 0; + + REQUIRE(VALID_SSURULE(rule)); + + for (i = 0; i < rule->ntypes; i++) { + if (rule->types[i].type == dns_rdatatype_any) { + max = rule->types[i].max; + } + if (rule->types[i].type == type) { + return (rule->types[i].max); + } + } + return (max); +} + isc_result_t dns_ssutable_firstrule(const dns_ssutable_t *table, dns_ssurule_t **rule) { REQUIRE(VALID_SSUTABLE(table)); diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in index 629fe53333..2d1c23b2ed 100644 --- a/lib/dns/win32/libdns.def.in +++ b/lib/dns/win32/libdns.def.in @@ -1019,9 +1019,10 @@ dns_soa_setretry dns_soa_setserial dns_ssu_external_match dns_ssu_mtypefromstring -dns_ssurule_isgrant dns_ssurule_identity +dns_ssurule_isgrant dns_ssurule_matchtype +dns_ssurule_max dns_ssurule_name dns_ssurule_types dns_ssutable_firstrule diff --git a/lib/ns/update.c b/lib/ns/update.c index 2a4355a0a3..84e1c7e129 100644 --- a/lib/ns/update.c +++ b/lib/ns/update.c @@ -753,6 +753,17 @@ cleanup_node: typedef bool rr_predicate(dns_rdata_t *update_rr, dns_rdata_t *db_rr); +static isc_result_t +count_action(void *data, rr_t *rr) { + unsigned int *ui = (unsigned int *)data; + + UNUSED(rr); + + (*ui)++; + + return (ISC_R_SUCCESS); +} + /*% * Helper function for rrset_exists(). */ @@ -2878,6 +2889,8 @@ update_action(isc_task_t *task, isc_event_t *event) { &rdata, &covers, &ttl, &update_class); if (update_class == zoneclass) { + unsigned int max = 0; + /* * RFC1123 doesn't allow MF and MD in master zones. */ @@ -3003,6 +3016,24 @@ update_action(isc_task_t *task, isc_event_t *event) { } } + if (rules != NULL && rules[rule] != NULL) { + max = dns_ssurule_max(rules[rule], rdata.type); + } + if (max != 0) { + unsigned int count = 0; + CHECK(foreach_rr(db, ver, name, rdata.type, + covers, count_action, &count)); + if (count >= max) { + update_log(client, zone, + LOGLEVEL_PROTOCOL, + "attempt to add more " + "records than permitted by " + "policy max=%u", + max); + continue; + } + } + if (isc_log_wouldlog(ns_lctx, LOGLEVEL_PROTOCOL)) { char namestr[DNS_NAME_FORMATSIZE]; char typestr[DNS_RDATATYPE_FORMATSIZE];