]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Fix zonemd check to allow unsupported algorithms to load.
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Fri, 8 Apr 2022 07:29:37 +0000 (09:29 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Fri, 8 Apr 2022 07:29:37 +0000 (09:29 +0200)
  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.

doc/Changelog
services/authzone.c
services/authzone.h
testcode/unitzonemd.c
testdata/auth_zonemd_file_unknown.rpl [new file with mode: 0644]

index 9d2d2604ef82485f1ebba611e990521f956a2d47..eee54dfeb62dd990268bc7c5f90af6137591bce3 100644 (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.
 
index b8ce8e73bdf7d1fe5dfc74a3dde96e1cbf83e2ac..9b20c3a7b332f73f9c1a0ba585eba8d4faf45854 100644 (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");
        }
 }
index d24e569d3b85aded492aeed9d0e99c92209dbc40..07614ed82963d33b37891b20a781db318be2843a 100644 (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,
index 5caa68a102c3d4079ecd7516d5249bb96e719396..23c9f70106443d13fe87fc754f9e0264eafe7371 100644 (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);
diff --git a/testdata/auth_zonemd_file_unknown.rpl b/testdata/auth_zonemd_file_unknown.rpl
new file mode 100644 (file)
index 0000000..f5c5f27
--- /dev/null
@@ -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