- 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.
This commit is contained in:
W.C.A. Wijngaards 2022-04-08 09:29:37 +02:00
parent 8f8a8a341a
commit e4ca71e85b
5 changed files with 234 additions and 7 deletions

View File

@ -1,3 +1,9 @@
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.

View File

@ -1882,6 +1882,8 @@ static int auth_zone_zonemd_check_hash(struct auth_zone* z,
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;
@ -1911,6 +1913,7 @@ static int auth_zone_zonemd_check_hash(struct auth_zone* z,
&hashalgo, &hash, &hashlen)) {
/* malformed RR */
*reason = "ZONEMD rdata malformed";
only_unsupported = 0;
continue;
}
/* check for duplicates */
@ -1920,25 +1923,49 @@ static int auth_zone_zonemd_check_hash(struct auth_zone* z,
* 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";
@ -7659,13 +7686,16 @@ int auth_zone_generate_zonemd_check(struct auth_zone* z, int scheme,
{
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 */
@ -8030,9 +8060,13 @@ auth_zone_verify_zonemd_with_key(struct auth_zone* z, struct module_env* env,
}
/* 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");
}
}

View File

@ -747,6 +747,9 @@ int zonemd_scheme_supported(int scheme);
* @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,

View File

@ -221,10 +221,10 @@ static void zonemd_check_test(void)
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);

184
testdata/auth_zonemd_file_unknown.rpl vendored Normal file
View File

@ -0,0 +1,184 @@
; 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