+8 April 2022: Wouter
+ - Fix zonemd check to allow unsupported algorithms to load.
+ If there are only unsupported algorithms, or unsupported schemes,
+ and no failed or successful other ZONEMD records, or malformed
+ or bad ZONEMD records, the unsupported records allow the zone load.
+
25 March 2022: Wouter
- Fix spelling error in comment in sldns_str2wire_svcparam_key_lookup.
struct regional* region = NULL;
struct sldns_buffer* buf = NULL;
uint32_t soa_serial = 0;
+ char* unsupported_reason = NULL;
+ int only_unsupported = 1;
region = env->scratch;
regional_free_all(region);
buf = env->scratch_buffer;
&hashalgo, &hash, &hashlen)) {
/* malformed RR */
*reason = "ZONEMD rdata malformed";
+ only_unsupported = 0;
continue;
}
/* check for duplicates */
* is not allowed. */
*reason = "ZONEMD RRSet contains more than one RR "
"with the same scheme and hash algorithm";
+ only_unsupported = 0;
continue;
}
regional_free_all(region);
if(serial != soa_serial) {
*reason = "ZONEMD serial is wrong";
+ only_unsupported = 0;
continue;
}
if(auth_zone_generate_zonemd_check(z, scheme, hashalgo,
hash, hashlen, region, buf, reason)) {
/* success */
+ if(*reason) {
+ if(!unsupported_reason)
+ unsupported_reason = *reason;
+ /* continue to check for valid ZONEMD */
+ if(verbosity >= VERB_ALGO) {
+ char zstr[255+1];
+ dname_str(z->name, zstr);
+ verbose(VERB_ALGO, "auth-zone %s ZONEMD %d %d is unsupported: %s", zstr, (int)scheme, (int)hashalgo, *reason);
+ }
+ continue;
+ }
+ only_unsupported = 0;
if(verbosity >= VERB_ALGO) {
char zstr[255+1];
dname_str(z->name, zstr);
- verbose(VERB_ALGO, "auth-zone %s ZONEMD hash is correct", zstr);
+ if(!reason)
+ verbose(VERB_ALGO, "auth-zone %s ZONEMD hash is correct", zstr);
}
return 1;
}
/* try next one */
}
+ /* have we seen no failures but only unsupported algo,
+ * and one unsupported algorithm, or more. */
+ if(only_unsupported && unsupported_reason) {
+ /* only unsupported algorithms, with valid serial, not
+ * malformed. Did not see supported algorithms, failed or
+ * successful ones. */
+ *reason = unsupported_reason;
+ return 1;
+ }
/* fail, we may have reason */
if(!*reason)
*reason = "no ZONEMD records found";
{
uint8_t gen[512];
size_t genlen = 0;
+ *reason = NULL;
if(!zonemd_hashalgo_supported(hashalgo)) {
+ /* allow it */
*reason = "unsupported algorithm";
- return 0;
+ return 1;
}
if(!zonemd_scheme_supported(scheme)) {
+ /* allow it */
*reason = "unsupported scheme";
- return 0;
+ return 1;
}
if(hashlen < 12) {
/* the ZONEMD draft requires digests to fail if too small */
}
/* success! log the success */
- auth_zone_log(z->name, VERB_ALGO, "ZONEMD verification successful");
+ if(reason)
+ auth_zone_log(z->name, VERB_ALGO, "ZONEMD %s", reason);
+ else auth_zone_log(z->name, VERB_ALGO, "ZONEMD verification successful");
if(result) {
- *result = strdup("ZONEMD verification successful");
+ if(reason)
+ *result = strdup(reason);
+ else *result = strdup("ZONEMD verification successful");
if(!*result) log_err("out of memory");
}
}
* @param region: temp region for allocs during canonicalisation.
* @param buf: temp buffer during canonicalisation.
* @param reason: string returned with failure reason.
+ * If the hash cannot be checked, but it is allowed, for unknown
+ * algorithms, the routine returns success, and the reason is nonNULL,
+ * with the allowance reason.
* @return false on failure.
*/
int auth_zone_generate_zonemd_check(struct auth_zone* z, int scheme,
unit_assert(result && reason == NULL);
result = auth_zone_generate_zonemd_check(z, 241, hashalgo,
hash, hashlen, region, buf, &reason);
- unit_assert(!result && strcmp(reason, "unsupported scheme")==0);
+ unit_assert(result && strcmp(reason, "unsupported scheme")==0);
result = auth_zone_generate_zonemd_check(z, scheme, 242,
hash, hashlen, region, buf, &reason);
- unit_assert(!result && strcmp(reason, "unsupported algorithm")==0);
+ unit_assert(result && strcmp(reason, "unsupported algorithm")==0);
result = auth_zone_generate_zonemd_check(z, scheme, hashalgo,
hash, 2, region, buf, &reason);
unit_assert(!result && strcmp(reason, "digest length too small, less than 12")==0);
--- /dev/null
+; config options
+server:
+ target-fetch-policy: "0 0 0 0 0"
+
+auth-zone:
+ name: "example.com."
+ ## zonefile (or none).
+ ## zonefile: "example.com.zone"
+ ## master by IP address or hostname
+ ## can list multiple masters, each on one line.
+ ## master:
+ ## url for http fetch
+ ## url:
+ ## queries from downstream clients get authoritative answers.
+ ## for-downstream: yes
+ for-downstream: no
+ ## queries are used to fetch authoritative answers from this zone,
+ ## instead of unbound itself sending queries there.
+ ## for-upstream: yes
+ for-upstream: yes
+ ## on failures with for-upstream, fallback to sending queries to
+ ## the authority servers
+ ## fallback-enabled: no
+ zonemd-check: yes
+
+ ## this line generates zonefile: \n"/tmp/xxx.example.com"\n
+ zonefile:
+TEMPFILE_NAME example.com
+ ## this is the inline file /tmp/xxx.example.com
+ ## the tempfiles are deleted when the testrun is over.
+TEMPFILE_CONTENTS example.com
+example.com. IN SOA ns.example.com. hostmaster.example.com. 200154054 28800 7200 604800 3600
+example.com. IN NS ns.example.com.
+example.com. IN ZONEMD 200154054 1 22 EFAA5B78B38AB1C45DE57B8167BCCE906451D0E72118E1F5E80B5F0C3CF04BFFC65D53C011185528EAD439D6F3A02F511961E090E5E4E0DFA013BD276D728B22
+example.com. IN ZONEMD 200154054 21 2 EFAA5B78B38AB1C45DE57B8167BCCE906451D0E72118E1F5E80B5F0C3CF04BFFC65D53C011185528EAD439D6F3A02F511961E090E5E4E0DFA013BD276D728B22
+www.example.com. IN A 127.0.0.1
+ns.example.com. IN A 127.0.0.1
+bar.example.com. IN A 1.2.3.4
+ding.example.com. IN A 1.2.3.4
+foo.example.com. IN A 1.2.3.4
+TEMPFILE_END
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test authority zone with ZONEMD with unknown algo from zonefile
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.44
+ENTRY_END
+RANGE_END
+
+; ns.example.net.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.44
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.net. IN NS
+SECTION ANSWER
+example.net. IN NS ns.example.net.
+SECTION ADDITIONAL
+ns.example.net. IN A 1.2.3.44
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+ns.example.net. IN A
+SECTION ANSWER
+ns.example.net. IN A 1.2.3.44
+SECTION AUTHORITY
+example.net. IN NS ns.example.net.
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+ns.example.net. IN AAAA
+SECTION AUTHORITY
+example.net. IN NS ns.example.net.
+SECTION ADDITIONAL
+www.example.net. IN A 1.2.3.44
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. IN NS ns.example.net.
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; recursion happens here.
+STEP 20 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 127.0.0.1
+ENTRY_END
+
+SCENARIO_END