From db4c5dfdbca9ef6a9a91efa7fdaed2e0abeeca73 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 30 May 2024 16:35:15 +1000 Subject: [PATCH] Add 64-self to 48-self update policy rules These rules are intended to allow ISP customers to add delegations in the IPv6 reverse tree that correspond to the IPv6 prefix delegations that they have recieved from the ISP. This is similar to the existing 6to4-self rule which but for the other nibble boundaries in common use. --- doc/arm/reference.rst | 26 ++++++++- doc/misc/primary.zoneopt | 2 +- lib/dns/include/dns/ssu.h | 9 ++- lib/dns/ssu.c | 112 ++++++++++++++++++++++++++++++++------ lib/isccfg/check.c | 5 ++ lib/isccfg/namedconf.c | 7 ++- 6 files changed, 137 insertions(+), 24 deletions(-) diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index c4245cf869..0453ca1d5d 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -7349,7 +7349,7 @@ records are also written to the zone's filename. Each rule grants or denies privileges. Rules are checked in the order in which they are specified in the :any:`update-policy` statement. Once a message has successfully matched a rule, the operation is immediately - granted or denied, and no further rules are examined. There are 16 types + granted or denied, and no further rules are examined. There are 23 types of rules; the rule type is specified by the ``ruletype`` field, and the interpretation of other fields varies depending on the rule type. @@ -7405,11 +7405,12 @@ records are also written to the zone's filename. send EOF - The ruletype field has 18 values: ``name``, ``subdomain``, ``zonesub``, + The ruletype field has 23 values: ``name``, ``subdomain``, ``zonesub``, ``wildcard``, ``self``, ``selfsub``, ``selfwild``, ``ms-self``, ``ms-selfsub``, ``ms-subdomain``, ``ms-subdomain-self-rhs``, ``krb5-self``, ``krb5-selfsub``, ``krb5-subdomain``, ``krb5-subdomain-self-rhs``, - ``tcp-self``, ``6to4-self``, and ``external``. + ``tcp-self``, ``6to4-self``, ``64-self``, ``60-self``, ``56-self``, + ``52-self``, ``48-self`` and ``external``. ``name`` With exact-match semantics, this rule matches when the name being updated is identical to the contents of the ``name`` field. @@ -7496,6 +7497,25 @@ records are also written to the zone's filename. .. note:: It is theoretically possible to spoof these TCP sessions. + ``64-self``, ``60-self``, ``56-self``, ``52-self``, ``48-self`` + These allow records at reverse IPv6 names at the specified + nibble boundary to be updated if a TCP request comes from + an IPv6 address in the matching prefix. These are intended + to allow ISP customers to add delgation records when using + DHCPv6 prefix delegations or when prefix delegation is used + within a site. + + :: + + grant * 64-self * NS DS; + grant ISP-DHCPv6-SERVER-KEY zonesub ANY; + + Which will allow customers to add delegation records and the + DHCPv6 server to clean up when the lease expires. + + .. note:: + It is theoretically possible to spoof these TCP sessions. + ``external`` This rule allows :iscman:`named` to defer the decision of whether to allow a given update to an external daemon. diff --git a/doc/misc/primary.zoneopt b/doc/misc/primary.zoneopt index dfa2b79661..4c5ecb21af 100644 --- a/doc/misc/primary.zoneopt +++ b/doc/misc/primary.zoneopt @@ -58,7 +58,7 @@ zone [ ] { sig-signing-type ; sig-validity-interval [ ]; // obsolete update-check-ksk ; // obsolete - update-policy ( local | { ( deny | grant ) ( 6to4-self | external | krb5-self | krb5-selfsub | krb5-subdomain | krb5-subdomain-self-rhs | ms-self | ms-selfsub | ms-subdomain | ms-subdomain-self-rhs | name | self | selfsub | selfwild | subdomain | tcp-self | wildcard | zonesub ) [ ] ; ... } ); + update-policy ( local | { ( deny | grant ) ( 48-self | 52-self | 56-self | 60-self | 64-self | 6to4-self | external | krb5-self | krb5-selfsub | krb5-subdomain | krb5-subdomain-self-rhs | ms-self | ms-selfsub | ms-subdomain | ms-subdomain-self-rhs | name | self | selfsub | selfwild | subdomain | tcp-self | wildcard | zonesub ) [ ] ; ... } ); zero-no-soa-ttl ; zone-statistics ( full | terse | none | ); }; diff --git a/lib/dns/include/dns/ssu.h b/lib/dns/include/dns/ssu.h index e391043616..60ae003671 100644 --- a/lib/dns/include/dns/ssu.h +++ b/lib/dns/include/dns/ssu.h @@ -41,9 +41,14 @@ typedef enum { dns_ssumatchtype_selfsubkrb5 = 15, dns_ssumatchtype_subdomainselfkrb5rhs = 16, dns_ssumatchtype_subdomainselfmsrhs = 17, - dns_ssumatchtype_max = 17, /* max value */ + dns_ssumatchtype_64self = 18, + dns_ssumatchtype_60self = 19, + dns_ssumatchtype_56self = 20, + dns_ssumatchtype_52self = 21, + dns_ssumatchtype_48self = 22, + dns_ssumatchtype_max = 22, /* max value */ - dns_ssumatchtype_dlz = 18 /* intentionally higher than _max */ + dns_ssumatchtype_dlz = 23 /* intentionally higher than _max */ } dns_ssumatchtype_t; typedef struct dns_ssuruletype { diff --git a/lib/dns/ssu.c b/lib/dns/ssu.c index 29ca14ee68..4821df8341 100644 --- a/lib/dns/ssu.c +++ b/lib/dns/ssu.c @@ -174,6 +174,16 @@ mtypetostring(dns_ssumatchtype_t matchtype) { return "local"; case dns_ssumatchtype_dlz: return "dlz"; + case dns_ssumatchtype_64self: + return "64-self"; + case dns_ssumatchtype_60self: + return "60-self"; + case dns_ssumatchtype_56self: + return "56-self"; + case dns_ssumatchtype_52self: + return "52-self"; + case dns_ssumatchtype_48self: + return "48-self"; } return "UnknownMatchType"; } @@ -282,44 +292,72 @@ reverse_from_address(dns_name_t *tcpself, const isc_netaddr_t *tcpaddr) { RUNTIME_CHECK(result == ISC_R_SUCCESS); } -static void -stf_from_address(dns_name_t *stfself, const isc_netaddr_t *tcpaddr) { - char buf[sizeof("X.X.X.X.Y.Y.Y.Y.2.0.0.2.IP6.ARPA.")]; +static bool +stf_from_address(dns_name_t *stfself, dns_ssumatchtype_t matchtype, + const isc_netaddr_t *tcpaddr) { + char buf[sizeof("1.2.3.4.5.6.7.8.1.2.3.4.5.6.7.8.IP6.ARPA.")]; isc_result_t result; const unsigned char *ap; isc_buffer_t b; unsigned long l; + char *s = buf; + int n; switch (tcpaddr->family) { case AF_INET: + if (matchtype != dns_ssumatchtype_6to4self) { + return false; + } l = ntohl(tcpaddr->type.in.s_addr); - result = snprintf( + n = snprintf( buf, sizeof(buf), "%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx.2.0.0.2.IP6.ARPA.", l & 0xf, (l >> 4) & 0xf, (l >> 8) & 0xf, (l >> 12) & 0xf, (l >> 16) & 0xf, (l >> 20) & 0xf, (l >> 24) & 0xf, (l >> 28) & 0xf); - RUNTIME_CHECK(result < sizeof(buf)); + RUNTIME_CHECK(n != -1 && (size_t)n < sizeof(buf)); break; case AF_INET6: ap = tcpaddr->type.in6.s6_addr; - result = snprintf( + n = snprintf( buf, sizeof(buf), - "%x.%x.%x.%x.%x.%x.%x.%x." - "%x.%x.%x.%x.IP6.ARPA.", - ap[5] & 0x0f, (ap[5] >> 4) & 0x0f, ap[4] & 0x0f, - (ap[4] >> 4) & 0x0f, ap[3] & 0x0f, (ap[3] >> 4) & 0x0f, - ap[2] & 0x0f, (ap[2] >> 4) & 0x0f, ap[1] & 0x0f, - (ap[1] >> 4) & 0x0f, ap[0] & 0x0f, (ap[0] >> 4) & 0x0f); - RUNTIME_CHECK(result < sizeof(buf)); + "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.IP6." + "ARPA.", + ap[7] & 0x0f, (ap[7] >> 4) & 0x0f, ap[6] & 0x0f, + (ap[6] >> 4) & 0x0f, ap[5] & 0x0f, (ap[5] >> 4) & 0x0f, + ap[4] & 0x0f, (ap[4] >> 4) & 0x0f, ap[3] & 0x0f, + (ap[3] >> 4) & 0x0f, ap[2] & 0x0f, (ap[2] >> 4) & 0x0f, + ap[1] & 0x0f, (ap[1] >> 4) & 0x0f, ap[0] & 0x0f, + (ap[0] >> 4) & 0x0f); + RUNTIME_CHECK(n != -1 && (size_t)n < sizeof(buf)); + switch (matchtype) { + case dns_ssumatchtype_64self: + break; + case dns_ssumatchtype_60self: + s += 2; + break; + case dns_ssumatchtype_56self: + s += 4; + break; + case dns_ssumatchtype_52self: + s += 6; + break; + case dns_ssumatchtype_48self: + case dns_ssumatchtype_6to4self: + s += 8; + break; + default: + UNREACHABLE(); + } break; default: UNREACHABLE(); } - isc_buffer_init(&b, buf, strlen(buf)); - isc_buffer_add(&b, strlen(buf)); + isc_buffer_init(&b, s, strlen(s)); + isc_buffer_add(&b, strlen(s)); result = dns_name_fromtext(stfself, &b, dns_rootname, 0); RUNTIME_CHECK(result == ISC_R_SUCCESS); + return true; } bool @@ -402,10 +440,20 @@ dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer, mtypetostring(rule->matchtype), namebuf); break; + case dns_ssumatchtype_48self: + case dns_ssumatchtype_52self: + case dns_ssumatchtype_56self: + case dns_ssumatchtype_60self: + case dns_ssumatchtype_64self: case dns_ssumatchtype_6to4self: stfself = dns_fixedname_initname(&fixed); - stf_from_address(stfself, addr); + if (!stf_from_address(stfself, + rule->matchtype, + addr)) + { + break; + } dns_name_format(stfself, namebuf, sizeof(namebuf)); isc_log_write( @@ -492,6 +540,11 @@ dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer, } break; case dns_ssumatchtype_tcpself: + case dns_ssumatchtype_48self: + case dns_ssumatchtype_52self: + case dns_ssumatchtype_56self: + case dns_ssumatchtype_60self: + case dns_ssumatchtype_64self: case dns_ssumatchtype_6to4self: if (!tcp || addr == NULL) { if (logit) { @@ -844,9 +897,24 @@ dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer, continue; } break; + case dns_ssumatchtype_48self: + case dns_ssumatchtype_52self: + case dns_ssumatchtype_56self: + case dns_ssumatchtype_60self: + case dns_ssumatchtype_64self: case dns_ssumatchtype_6to4self: stfself = dns_fixedname_initname(&fixed); - stf_from_address(stfself, addr); + if (!stf_from_address(stfself, rule->matchtype, addr)) { + if (logit) { + isc_log_write( + DNS_LOGCATEGORY_UPDATE_POLICY, + DNS_LOGMODULE_SSU, + ISC_LOG_DEBUG(99), + "update-policy: next " + "rule: not IPv6 address"); + } + continue; + } if (dns_name_iswildcard(rule->identity)) { if (!dns_name_matcheswildcard(stfself, rule->identity)) @@ -1123,6 +1191,16 @@ dns_ssu_mtypefromstring(const char *str, dns_ssumatchtype_t *mtype) { *mtype = dns_ssumatchtype_subdomain; } else if (strcasecmp(str, "external") == 0) { *mtype = dns_ssumatchtype_external; + } else if (strcasecmp(str, "64-self") == 0) { + *mtype = dns_ssumatchtype_64self; + } else if (strcasecmp(str, "60-self") == 0) { + *mtype = dns_ssumatchtype_60self; + } else if (strcasecmp(str, "56-self") == 0) { + *mtype = dns_ssumatchtype_56self; + } else if (strcasecmp(str, "52-self") == 0) { + *mtype = dns_ssumatchtype_56self; + } else if (strcasecmp(str, "48-self") == 0) { + *mtype = dns_ssumatchtype_48self; } else { return ISC_R_NOTFOUND; } diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c index 2e8a4b2297..002f5f2475 100644 --- a/lib/isccfg/check.c +++ b/lib/isccfg/check.c @@ -2666,6 +2666,11 @@ check_update_policy(const cfg_obj_t *policy) { case dns_ssumatchtype_selfsubkrb5: case dns_ssumatchtype_selfsubms: case dns_ssumatchtype_tcpself: + case dns_ssumatchtype_48self: + case dns_ssumatchtype_52self: + case dns_ssumatchtype_56self: + case dns_ssumatchtype_60self: + case dns_ssumatchtype_64self: case dns_ssumatchtype_6to4self: if (tresult == ISC_R_SUCCESS && !dns_name_equal(dns_rootname, name)) diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 85d1c531c0..2465b81320 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -347,7 +347,12 @@ doc_matchname(cfg_printer_t *pctx, const cfg_type_t *type) { cfg_print_cstr(pctx, " ]"); } -static const char *matchtype_enums[] = { "6to4-self", +static const char *matchtype_enums[] = { "48-self", + "52-self", + "56-self", + "60-self", + "64-self", + "6to4-self", "external", "krb5-self", "krb5-selfsub",