]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/resolve/resolved-dns-dnssec.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / resolve / resolved-dns-dnssec.c
index 8e3c78e7bfc7faa6c6f05953846e84b210a19a8c..f04b246a3d6a9a3c0a64f7ee1f0f30424e4075ce 100644 (file)
@@ -1,5 +1,4 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#if HAVE_GCRYPT
 #include <gcrypt.h>
+#endif
 
 #include "alloc-util.h"
 #include "dns-domain.h"
+#include "gcrypt-util.h"
 #include "hexdecoct.h"
 #include "resolved-dns-dnssec.h"
 #include "resolved-dns-packet.h"
 #include "string-table.h"
 
-/* Open question:
- *
- * How does the DNSSEC canonical form of a hostname with a label
- * containing a dot look like, the way DNS-SD does it?
- *
- * TODO:
- *
- *   - enable by default
- *   - Allow clients to request DNSSEC even if DNSSEC is off
- *   - make sure when getting an NXDOMAIN response through CNAME, we still process the first CNAMEs in the packet
- * */
-
 #define VERIFY_RRS_MAX 256
 #define MAX_KEY_SIZE (32*1024)
 
  *            Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
  */
 
-static void initialize_libgcrypt(void) {
-        const char *p;
-
-        if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
-                return;
-
-        p = gcry_check_version("1.4.5");
-        assert(p);
-
-        gcry_control(GCRYCTL_DISABLE_SECMEM);
-        gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
-}
-
 uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) {
         const uint8_t *p;
         uint32_t sum, f;
@@ -100,6 +77,57 @@ uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) {
         return sum & UINT32_C(0xFFFF);
 }
 
+int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
+        size_t c = 0;
+        int r;
+
+        /* Converts the specified hostname into DNSSEC canonicalized
+         * form. */
+
+        if (buffer_max < 2)
+                return -ENOBUFS;
+
+        for (;;) {
+                r = dns_label_unescape(&n, buffer, buffer_max);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                if (buffer_max < (size_t) r + 2)
+                        return -ENOBUFS;
+
+                /* The DNSSEC canonical form is not clear on what to
+                 * do with dots appearing in labels, the way DNS-SD
+                 * does it. Refuse it for now. */
+
+                if (memchr(buffer, '.', r))
+                        return -EINVAL;
+
+                ascii_strlower_n(buffer, (size_t) r);
+                buffer[r] = '.';
+
+                buffer += r + 1;
+                c += r + 1;
+
+                buffer_max -= r + 1;
+        }
+
+        if (c <= 0) {
+                /* Not even a single label: this is the root domain name */
+
+                assert(buffer_max > 2);
+                buffer[0] = '.';
+                buffer[1] = 0;
+
+                return 1;
+        }
+
+        return (int) c;
+}
+
+#if HAVE_GCRYPT
+
 static int rr_compare(const void *a, const void *b) {
         DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b;
         size_t m;
@@ -440,7 +468,7 @@ static int dnssec_rrsig_prepare(DnsResourceRecord *rrsig) {
         if (rrsig->rrsig.inception > rrsig->rrsig.expiration)
                 return -EINVAL;
 
-        name = DNS_RESOURCE_KEY_NAME(rrsig->key);
+        name = dns_resource_key_name(rrsig->key);
 
         n_key_labels = dns_name_count_labels(name);
         if (n_key_labels < 0)
@@ -596,9 +624,9 @@ int dnssec_verify_rrset(
         assert(rrsig->key->type == DNS_TYPE_RRSIG);
         assert(dnskey->key->type == DNS_TYPE_DNSKEY);
 
-        /* Verifies the the RRSet matching the specified "key" in "a",
+        /* Verifies that the RRSet matches the specified "key" in "a",
          * using the signature "rrsig" and the key "dnskey". It's
-         * assumed the RRSIG and DNSKEY match. */
+         * assumed that RRSIG and DNSKEY match. */
 
         md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm);
         if (md_algorithm == -EOPNOTSUPP) {
@@ -624,7 +652,7 @@ int dnssec_verify_rrset(
                 return 0;
         }
 
-        name = DNS_RESOURCE_KEY_NAME(key);
+        name = dns_resource_key_name(key);
 
         /* Some keys may only appear signed in the zone apex, and are invalid anywhere else. (SOA, NS...) */
         if (dns_type_apex_only(rrsig->rrsig.type_covered)) {
@@ -698,7 +726,7 @@ int dnssec_verify_rrset(
         qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
 
         /* OK, the RRs are now in canonical order. Let's calculate the digest */
-        initialize_libgcrypt();
+        initialize_libgcrypt(false);
 
         hash_size = gcry_md_get_algo_dlen(md_algorithm);
         assert(hash_size > 0);
@@ -824,7 +852,7 @@ int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnske
         if (dnssec_keytag(dnskey, false) != rrsig->rrsig.key_tag)
                 return 0;
 
-        return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
+        return dns_name_equal(dns_resource_key_name(dnskey->key), rrsig->rrsig.signer);
 }
 
 int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
@@ -840,7 +868,7 @@ int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig)
         if (rrsig->rrsig.type_covered != key->type)
                 return 0;
 
-        return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
+        return dns_name_equal(dns_resource_key_name(rrsig->key), dns_resource_key_name(key));
 }
 
 int dnssec_verify_rrset_search(
@@ -983,55 +1011,6 @@ int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
         return 0;
 }
 
-int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
-        size_t c = 0;
-        int r;
-
-        /* Converts the specified hostname into DNSSEC canonicalized
-         * form. */
-
-        if (buffer_max < 2)
-                return -ENOBUFS;
-
-        for (;;) {
-                r = dns_label_unescape(&n, buffer, buffer_max);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        break;
-
-                if (buffer_max < (size_t) r + 2)
-                        return -ENOBUFS;
-
-                /* The DNSSEC canonical form is not clear on what to
-                 * do with dots appearing in labels, the way DNS-SD
-                 * does it. Refuse it for now. */
-
-                if (memchr(buffer, '.', r))
-                        return -EINVAL;
-
-                ascii_strlower_n(buffer, (size_t) r);
-                buffer[r] = '.';
-
-                buffer += r + 1;
-                c += r + 1;
-
-                buffer_max -= r + 1;
-        }
-
-        if (c <= 0) {
-                /* Not even a single label: this is the root domain name */
-
-                assert(buffer_max > 2);
-                buffer[0] = '.';
-                buffer[1] = 0;
-
-                return 1;
-        }
-
-        return (int) c;
-}
-
 static int digest_to_gcrypt_md(uint8_t algorithm) {
 
         /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
@@ -1080,7 +1059,7 @@ int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds,
         if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag)
                 return 0;
 
-        initialize_libgcrypt();
+        initialize_libgcrypt(false);
 
         md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
         if (md_algorithm < 0)
@@ -1092,7 +1071,7 @@ int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds,
         if (ds->ds.digest_size != hash_size)
                 return 0;
 
-        r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
+        r = dnssec_canonicalize(dns_resource_key_name(dnskey->key), owner_name, sizeof(owner_name));
         if (r < 0)
                 return r;
 
@@ -1142,7 +1121,7 @@ int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *vali
                 if (ds->key->class != dnskey->key->class)
                         continue;
 
-                r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), DNS_RESOURCE_KEY_NAME(ds->key));
+                r = dns_name_equal(dns_resource_key_name(dnskey->key), dns_resource_key_name(ds->key));
                 if (r < 0)
                         return r;
                 if (r == 0)
@@ -1199,7 +1178,7 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
         if (algorithm < 0)
                 return algorithm;
 
-        initialize_libgcrypt();
+        initialize_libgcrypt(false);
 
         hash_size = gcry_md_get_algo_dlen(algorithm);
         assert(hash_size > 0);
@@ -1256,7 +1235,7 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
         if (rr->key->type != DNS_TYPE_NSEC3)
                 return 0;
 
-        /* RFC  5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
+        /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
         if (!IN_SET(rr->nsec3.flags, 0, 1))
                 return 0;
 
@@ -1269,10 +1248,10 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
 
         /* Ignore NSEC3 RRs generated from wildcards. If these NSEC3 RRs weren't correctly signed we can't make this
          * check (since rr->n_skip_labels_source is -1), but that's OK, as we won't trust them anyway in that case. */
-        if (rr->n_skip_labels_source != 0 && rr->n_skip_labels_source != (unsigned) -1)
+        if (!IN_SET(rr->n_skip_labels_source, 0, (unsigned) -1))
                 return 0;
         /* Ignore NSEC3 RRs that are located anywhere else than one label below the zone */
-        if (rr->n_skip_labels_signer != 1 && rr->n_skip_labels_signer != (unsigned) -1)
+        if (!IN_SET(rr->n_skip_labels_signer, 1, (unsigned) -1))
                 return 0;
 
         if (!nsec3)
@@ -1294,14 +1273,14 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
         if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
                 return 0;
 
-        a = DNS_RESOURCE_KEY_NAME(rr->key);
+        a = dns_resource_key_name(rr->key);
         r = dns_name_parent(&a); /* strip off hash */
         if (r < 0)
                 return r;
         if (r == 0)
                 return 0;
 
-        b = DNS_RESOURCE_KEY_NAME(nsec3->key);
+        b = dns_resource_key_name(nsec3->key);
         r = dns_name_parent(&b); /* strip off hash */
         if (r < 0)
                 return r;
@@ -1325,7 +1304,7 @@ static int nsec3_hashed_domain_format(const uint8_t *hashed, size_t hashed_size,
         if (!l)
                 return -ENOMEM;
 
-        j = strjoin(l, ".", zone, NULL);
+        j = strjoin(l, ".", zone);
         if (!j)
                 return -ENOMEM;
 
@@ -1375,7 +1354,7 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR
          * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3
          * records from a given zone in a response must use the same
          * parameters. */
-        zone = DNS_RESOURCE_KEY_NAME(key);
+        zone = dns_resource_key_name(key);
         for (;;) {
                 DNS_ANSWER_FOREACH_FLAGS(zone_rr, flags, answer) {
                         r = nsec3_is_good(zone_rr, NULL);
@@ -1384,7 +1363,7 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR
                         if (r == 0)
                                 continue;
 
-                        r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(zone_rr->key), 1, zone);
+                        r = dns_name_equal_skip(dns_resource_key_name(zone_rr->key), 1, zone);
                         if (r < 0)
                                 return r;
                         if (r > 0)
@@ -1404,7 +1383,7 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR
 
 found_zone:
         /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
-        p = DNS_RESOURCE_KEY_NAME(key);
+        p = dns_resource_key_name(key);
         for (;;) {
                 _cleanup_free_ char *hashed_domain = NULL;
 
@@ -1427,7 +1406,7 @@ found_zone:
                         if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
                                 continue;
 
-                        r = dns_name_equal(DNS_RESOURCE_KEY_NAME(enclosure_rr->key), hashed_domain);
+                        r = dns_name_equal(dns_resource_key_name(enclosure_rr->key), hashed_domain);
                         if (r < 0)
                                 return r;
                         if (r > 0) {
@@ -1526,7 +1505,7 @@ found_closest_encloser:
                 if (r < 0)
                         return r;
 
-                r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain);
+                r = dns_name_between(dns_resource_key_name(rr->key), next_closer_domain, next_hashed_domain);
                 if (r < 0)
                         return r;
                 if (r > 0) {
@@ -1538,7 +1517,7 @@ found_closest_encloser:
                         no_closer = true;
                 }
 
-                r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain);
+                r = dns_name_equal(dns_resource_key_name(rr->key), wildcard_domain);
                 if (r < 0)
                         return r;
                 if (r > 0) {
@@ -1547,7 +1526,7 @@ found_closest_encloser:
                         wildcard_rr = rr;
                 }
 
-                r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain, next_hashed_domain);
+                r = dns_name_between(dns_resource_key_name(rr->key), wildcard_domain, next_hashed_domain);
                 if (r < 0)
                         return r;
                 if (r > 0) {
@@ -1626,7 +1605,7 @@ static int dnssec_nsec_wildcard_equal(DnsResourceRecord *rr, const char *name) {
         if (rr->n_skip_labels_source != 1)
                 return 0;
 
-        n = DNS_RESOURCE_KEY_NAME(rr->key);
+        n = dns_resource_key_name(rr->key);
         r = dns_label_unescape(&n, label, sizeof(label));
         if (r <= 0)
                 return r;
@@ -1664,8 +1643,8 @@ static int dnssec_nsec_in_path(DnsResourceRecord *rr, const char *name) {
         if (r <= 0)
                 return r;
 
-        /* If the name we we are interested in is not a prefix of the common suffix of the NSEC RR's owner and next domain names, then we can't say anything either. */
-        r = dns_name_common_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rr->nsec.next_domain_name, &common_suffix);
+        /* If the name we are interested in is not a prefix of the common suffix of the NSEC RR's owner and next domain names, then we can't say anything either. */
+        r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix);
         if (r < 0)
                 return r;
 
@@ -1684,7 +1663,7 @@ static int dnssec_nsec_from_parent_zone(DnsResourceRecord *rr, const char *name)
         if (r <= 0)
                 return r;
 
-        r = dns_name_equal(name, DNS_RESOURCE_KEY_NAME(rr->key));
+        r = dns_name_equal(name, dns_resource_key_name(rr->key));
         if (r <= 0)
                 return r;
 
@@ -1707,7 +1686,7 @@ static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) {
 
         /* Checks whether the "Next Closer" is witin the space covered by the specified RR. */
 
-        r = dns_name_common_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rr->nsec.next_domain_name, &common_suffix);
+        r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix);
         if (r < 0)
                 return r;
 
@@ -1728,11 +1707,12 @@ static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) {
 
         /* p is now the "Next Closer". */
 
-        return dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), p, rr->nsec.next_domain_name);
+        return dns_name_between(dns_resource_key_name(rr->key), p, rr->nsec.next_domain_name);
 }
 
 static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) {
-        const char *common_suffix, *wc;
+        _cleanup_free_ char *wc = NULL;
+        const char *common_suffix;
         int r;
 
         assert(rr);
@@ -1747,7 +1727,7 @@ static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name)
          *     NSEC yyy.zzz.xoo.bar →             bar: indicates that a number of wildcards don#t exist either...
          */
 
-        r = dns_name_common_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rr->nsec.next_domain_name, &common_suffix);
+        r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix);
         if (r < 0)
                 return r;
 
@@ -1756,8 +1736,11 @@ static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name)
         if (r <= 0)
                 return r;
 
-        wc = strjoina("*.", common_suffix, NULL);
-        return dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wc, rr->nsec.next_domain_name);
+        r = dns_name_concat("*", common_suffix, &wc);
+        if (r < 0)
+                return r;
+
+        return dns_name_between(dns_resource_key_name(rr->key), wc, rr->nsec.next_domain_name);
 }
 
 int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
@@ -1772,7 +1755,7 @@ int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r
 
         /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
 
-        name = DNS_RESOURCE_KEY_NAME(key);
+        name = dns_resource_key_name(key);
 
         DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
 
@@ -1792,7 +1775,7 @@ int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r
                         continue;
 
                 /* Check if this is a direct match. If so, we have encountered a NODATA case */
-                r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), name);
+                r = dns_name_equal(dns_resource_key_name(rr->key), name);
                 if (r < 0)
                         return r;
                 if (r == 0) {
@@ -1894,7 +1877,7 @@ int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r
         return 0;
 }
 
-int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated) {
+static int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated) {
         DnsResourceRecord *rr;
         DnsAnswerFlags flags;
         int r;
@@ -1922,7 +1905,7 @@ int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name
                         if (r == 0)
                                 continue;
 
-                        r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), name, rr->nsec.next_domain_name);
+                        r = dns_name_between(dns_resource_key_name(rr->key), name, rr->nsec.next_domain_name);
                         if (r < 0)
                                 return r;
 
@@ -1965,7 +1948,7 @@ int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name
                         if (r < 0)
                                 return r;
 
-                        r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain, next_hashed_domain);
+                        r = dns_name_between(dns_resource_key_name(rr->key), hashed_domain, next_hashed_domain);
                         if (r < 0)
                                 return r;
 
@@ -1999,7 +1982,7 @@ static int dnssec_test_positive_wildcard_nsec3(
 
         /* Run a positive NSEC3 wildcard proof. Specifically:
          *
-         * A proof that the the "next closer" of the generating wildcard does not exist.
+         * A proof that the "next closer" of the generating wildcard does not exist.
          *
          * Note a key difference between the NSEC3 and NSEC versions of the proof. NSEC RRs don't have to exist for
          * empty non-transients. NSEC3 RRs however have to. This means it's sufficient to check if the next closer name
@@ -2126,6 +2109,77 @@ int dnssec_test_positive_wildcard(
                 return dnssec_test_positive_wildcard_nsec(answer, name, source, zone, authenticated);
 }
 
+#else
+
+int dnssec_verify_rrset(
+                DnsAnswer *a,
+                const DnsResourceKey *key,
+                DnsResourceRecord *rrsig,
+                DnsResourceRecord *dnskey,
+                usec_t realtime,
+                DnssecResult *result) {
+
+        return -EOPNOTSUPP;
+}
+
+int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) {
+
+        return -EOPNOTSUPP;
+}
+
+int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
+
+        return -EOPNOTSUPP;
+}
+
+int dnssec_verify_rrset_search(
+                DnsAnswer *a,
+                const DnsResourceKey *key,
+                DnsAnswer *validated_dnskeys,
+                usec_t realtime,
+                DnssecResult *result,
+                DnsResourceRecord **ret_rrsig) {
+
+        return -EOPNOTSUPP;
+}
+
+int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
+
+        return -EOPNOTSUPP;
+}
+
+int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
+
+        return -EOPNOTSUPP;
+}
+
+int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
+
+        return -EOPNOTSUPP;
+}
+
+int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
+
+        return -EOPNOTSUPP;
+}
+
+int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
+
+        return -EOPNOTSUPP;
+}
+
+int dnssec_test_positive_wildcard(
+                DnsAnswer *answer,
+                const char *name,
+                const char *source,
+                const char *zone,
+                bool *authenticated) {
+
+        return -EOPNOTSUPP;
+}
+
+#endif
+
 static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
         [DNSSEC_VALIDATED] = "validated",
         [DNSSEC_VALIDATED_WILDCARD] = "validated-wildcard",