]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
sematic-checks: use verification routines from dnssec-verify
authorLibor Peltan <libor.peltan@nic.cz>
Fri, 7 Jan 2022 16:06:45 +0000 (17:06 +0100)
committerDaniel Salzman <daniel.salzman@nic.cz>
Fri, 27 May 2022 12:35:19 +0000 (14:35 +0200)
src/knot/dnssec/context.c
src/knot/zone/semantic-check.c
tests/knot/semantic_check_data/delegation.signed [moved from tests/knot/semantic_check_data/no_error_nsec3_delegation.signed with 98% similarity]
tests/knot/test_semantic_check.in

index 1e045b3760b81ee9ea22a76b8991f9b73c422de0..e1b668e6a3ad3fab00d66d7f8422a5a003893c23 100644 (file)
@@ -309,10 +309,14 @@ int kdnssec_validation_ctx(conf_t *conf, kdnssec_ctx_t *ctx, const zone_contents
        }
 
        policy_from_zone(ctx->policy, zone);
-       conf_val_t policy_id = conf_zone_get(conf, C_DNSSEC_POLICY, zone->apex->owner);
-       conf_id_fix_default(&policy_id);
-       conf_val_t num_threads = conf_id_get(conf, C_POLICY, C_SIGNING_THREADS, &policy_id);
-       ctx->policy->signing_threads = conf_int(&num_threads);
+       if (conf != NULL) {
+               conf_val_t policy_id = conf_zone_get(conf, C_DNSSEC_POLICY, zone->apex->owner);
+               conf_id_fix_default(&policy_id);
+               conf_val_t num_threads = conf_id_get(conf, C_POLICY, C_SIGNING_THREADS, &policy_id);
+               ctx->policy->signing_threads = conf_int(&num_threads);
+       } else {
+               ctx->policy->signing_threads = 1;
+       }
 
        int ret = kasp_zone_from_contents(ctx->zone, zone, ctx->policy->single_type_signing,
                                          ctx->policy->nsec3_enabled, &ctx->policy->nsec3_iterations,
index a4cee7b2fc79142f11969d138df7e35cf7f4d927..8f611566ddf020ab02711c4c187bd8fa2320e675 100644 (file)
  */
 
 #include <stdio.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
+
+#include "knot/zone/semantic-check.h"
 
 #include "libdnssec/error.h"
-#include "contrib/base32hex.h"
+#include "libdnssec/key.h"
 #include "contrib/string.h"
 #include "libknot/libknot.h"
-#include "knot/zone/semantic-check.h"
-#include "knot/dnssec/rrset-sign.h"
+#include "knot/dnssec/key-events.h"
 #include "knot/dnssec/zone-keys.h"
-#include "knot/dnssec/zone-nsec.h"
+#include "knot/updates/zone-update.h"
 
 static const char *error_messages[SEM_ERR_UNKNOWN + 1] = {
        [SEM_ERR_SOA_NONE] =
@@ -153,7 +150,6 @@ typedef enum {
 typedef struct {
        zone_contents_t *zone;
        sem_handler_t *handler;
-       const zone_node_t *next_nsec;
        check_level_t level;
        time_t time;
 } semchecks_data_t;
@@ -164,13 +160,6 @@ static int check_dname(const zone_node_t *node, semchecks_data_t *data);
 static int check_delegation(const zone_node_t *node, semchecks_data_t *data);
 static int check_submission(const zone_node_t *node, semchecks_data_t *data);
 static int check_ds(const zone_node_t *node, semchecks_data_t *data);
-static int check_nsec(const zone_node_t *node, semchecks_data_t *data);
-static int check_nsec3(const zone_node_t *node, semchecks_data_t *data);
-static int check_nsec3_opt_out(const zone_node_t *node, semchecks_data_t *data);
-static int check_rrsig(const zone_node_t *node, semchecks_data_t *data);
-static int check_rrsig_signed(const zone_node_t *node, semchecks_data_t *data);
-static int check_nsec_bitmap(const zone_node_t *node, semchecks_data_t *data);
-static int check_nsec3_presence(const zone_node_t *node, semchecks_data_t *data);
 
 struct check_function {
        int (*function)(const zone_node_t *, semchecks_data_t *);
@@ -185,247 +174,11 @@ static const struct check_function CHECK_FUNCTIONS[] = {
        { check_delegation,     MANDATORY | SOFT }, // mandatory for apex, optional for others
        { check_ds,             OPTIONAL },
        { check_submission,     NSEC | NSEC3 },
-       { check_rrsig,          NSEC | NSEC3 },
-       { check_rrsig_signed,   NSEC | NSEC3 },
-       { check_nsec_bitmap,    NSEC | NSEC3 },
-       { check_nsec,           NSEC },
-       { check_nsec3,          NSEC3 },
-       { check_nsec3_presence, NSEC3 },
-       { check_nsec3_opt_out,  NSEC3 },
 };
 
 static const int CHECK_FUNCTIONS_LEN = sizeof(CHECK_FUNCTIONS)
                                      / sizeof(struct check_function);
 
-static int check_signature(const knot_rdata_t *rrsig, const dnssec_key_t *key,
-                           const knot_rrset_t *covered)
-{
-       if (!rrsig || !key || !dnssec_key_can_verify(key)) {
-               return KNOT_EINVAL;
-       }
-
-       int ret = KNOT_EOK;
-       dnssec_sign_ctx_t *sign_ctx = NULL;
-
-       dnssec_binary_t signature = {
-               .size = knot_rrsig_signature_len(rrsig),
-               .data = (uint8_t *)knot_rrsig_signature(rrsig)
-       };
-       if (!signature.data || !signature.size) {
-               ret = KNOT_EINVAL;
-               goto fail;
-       }
-
-       if (dnssec_sign_new(&sign_ctx, key) != KNOT_EOK) {
-               ret = KNOT_ENOMEM;
-               goto fail;
-       }
-
-       if (knot_sign_ctx_add_data(sign_ctx, rrsig->data, covered) != KNOT_EOK) {
-               ret = KNOT_ENOMEM;
-               goto fail;
-       }
-
-       if (dnssec_sign_verify(sign_ctx, false, &signature) != KNOT_EOK) {
-               ret = KNOT_EINVAL;
-               goto fail;
-       }
-
-fail:
-       dnssec_sign_free(sign_ctx);
-       return ret;
-}
-
-/*!
- * \brief Semantic check - RRSIG rdata.
- *
- * \param handler    Pointer on function to be called in case of negative check.
- * \param zone       The zone the rrset is in.
- * \param node       The node in the zone contents.
- * \param rrsig      RRSIG rdata.
- * \param rrset      RRSet signed by the RRSIG.
- * \param context    The time stamp we check the rrsig validity according to.
- * \param level      Level of the check.
- * \param verified   Out: the RRSIG has been verified to be signed by existing DNSKEY.
- *
- * \retval KNOT_EOK on success.
- * \return Appropriate error code if error was found.
- */
-static int check_rrsig_rdata(sem_handler_t *handler,
-                             const zone_contents_t *zone,
-                             const zone_node_t *node,
-                             const knot_rdata_t *rrsig,
-                             const knot_rrset_t *rrset,
-                             time_t context,
-                             check_level_t level,
-                             bool *verified)
-{
-       /* Prepare additional info string. */
-       char info_str[50] = "";
-       char type_str[16] = "";
-       knot_rrtype_to_string(rrset->type, type_str, sizeof(type_str));
-       int ret = snprintf(info_str, sizeof(info_str), "(record type %s)", type_str);
-       if (ret < 0 || ret >= sizeof(info_str)) {
-               return KNOT_ENOMEM;
-       }
-
-       if (knot_rrsig_type_covered(rrsig) != rrset->type) {
-               handler->cb(handler, zone, node->owner, SEM_ERR_RRSIG_RDATA_TYPE_COVERED,
-                           info_str);
-       }
-
-       /* label number at the 2nd index should be same as owner's */
-       uint8_t labels_rdata = knot_rrsig_labels(rrsig);
-
-       size_t tmp = knot_dname_labels(rrset->owner, NULL) - labels_rdata;
-       if (tmp != 0) {
-               /* if name has wildcard, label must not be included */
-               if (!knot_dname_is_wildcard(rrset->owner)) {
-                       handler->cb(handler, zone, node->owner, SEM_ERR_RRSIG_RDATA_LABELS,
-                                   info_str);
-               } else if (tmp != 1) {
-                       handler->cb(handler, zone, node->owner, SEM_ERR_RRSIG_RDATA_LABELS,
-                                   info_str);
-               }
-       }
-
-       /* Check original TTL. */
-       uint32_t original_ttl = knot_rrsig_original_ttl(rrsig);
-       if (original_ttl != rrset->ttl) {
-               handler->cb(handler, zone, node->owner, SEM_ERR_RRSIG_RDATA_TTL,
-                           info_str);
-       }
-
-       /* Check for expired signature. */
-       if (knot_rrsig_sig_expiration(rrsig) < context) {
-               handler->cb(handler, zone, node->owner, SEM_ERR_RRSIG_RDATA_EXPIRATION,
-                           info_str);
-       }
-
-       /* Check inception */
-       if (knot_rrsig_sig_inception(rrsig) > context) {
-               handler->cb(handler, zone, node->owner, SEM_ERR_RRSIG_RDATA_INCEPTION,
-                           info_str);
-       }
-
-       /* Check signer name. */
-       const knot_dname_t *signer = knot_rrsig_signer_name(rrsig);
-       if (!knot_dname_is_equal(signer, zone->apex->owner)) {
-               handler->cb(handler, zone, node->owner, SEM_ERR_RRSIG_RDATA_OWNER,
-                           info_str);
-       }
-
-       /* Verify with public key - only one RRSIG of covered record needed */
-       if (level & OPTIONAL && !*verified) {
-               const knot_rdataset_t *dnskeys = node_rdataset(zone->apex, KNOT_RRTYPE_DNSKEY);
-               if (dnskeys == NULL) {
-                       return KNOT_EOK;
-               }
-
-               for (int i = 0; i < dnskeys->count; i++) {
-                       knot_rdata_t *dnskey = knot_rdataset_at(dnskeys, i);
-                       uint16_t flags = knot_dnskey_flags(dnskey);
-                       uint8_t proto = knot_dnskey_proto(dnskey);
-                       /* RFC 4034 2.1.1 & 2.1.2 */
-                       if (flags & DNSKEY_FLAGS_ZSK && proto == 3) {
-                               dnssec_key_t *key;
-
-                               ret = dnssec_key_from_rdata(&key, zone->apex->owner,
-                                                           dnskey->data, dnskey->len);
-                               if (ret != KNOT_EOK) {
-                                       continue;
-                               }
-
-                               if (dnssec_key_get_keytag(key) != knot_rrsig_key_tag(rrsig)) {
-                                       dnssec_key_free(key);
-                                       continue;
-                               }
-
-                               ret = check_signature(rrsig, key, rrset);
-                               dnssec_key_free(key);
-                               if (ret == KNOT_EOK) {
-                                       *verified = true;
-                                       break;
-                               }
-                       }
-               }
-       }
-
-       return KNOT_EOK;
-}
-
-static int check_rrsig_signed(const zone_node_t *node, semchecks_data_t *data)
-{
-       /* signed rrsig - nonsense */
-       if (node_rrtype_is_signed(node, KNOT_RRTYPE_RRSIG)) {
-               data->handler->cb(data->handler, data->zone, node->owner,
-                                 SEM_ERR_RRSIG_SIGNED, NULL);
-       }
-
-       return KNOT_EOK;
-}
-/*!
- * \brief Semantic check - RRSet's RRSIG.
- *
- * \param handler    Pointer on function to be called in case of negative check.
- * \param zone       The zone the rrset is in.
- * \param node       The node in the zone contents.
- * \param rrset      RRSet signed by the RRSIG.
- * \param context    The time stamp we check the rrsig validity according to.
- * \param level      Level of the check.
- *
- * \retval KNOT_EOK on success.
- * \return Appropriate error code if error was found.
- */
-static int check_rrsig_in_rrset(sem_handler_t *handler,
-                                const zone_contents_t *zone,
-                                const zone_node_t *node,
-                                const knot_rrset_t *rrset,
-                                time_t context,
-                                check_level_t level)
-{
-       if (handler == NULL || node == NULL || rrset == NULL) {
-               return KNOT_EINVAL;
-       }
-       /* Prepare additional info string. */
-       char info_str[50] = "";
-       char type_str[16] = "";
-       knot_rrtype_to_string(rrset->type, type_str, sizeof(type_str));
-       int ret = snprintf(info_str, sizeof(info_str), "(record type %s)", type_str);
-       if (ret < 0 || ret >= sizeof(info_str)) {
-               return KNOT_ENOMEM;
-       }
-
-       knot_rrset_t node_rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG);
-
-       knot_rdataset_t rrsigs;
-       knot_rdataset_init(&rrsigs);
-       ret = knot_synth_rrsig(rrset->type, &node_rrsigs.rrs, &rrsigs, NULL);
-       if (ret != KNOT_EOK && ret != KNOT_ENOENT) {
-               return ret;
-       }
-       if (ret == KNOT_ENOENT) {
-               handler->cb(handler, zone, node->owner, SEM_ERR_RRSIG_NO_RRSIG, info_str);
-               return KNOT_EOK;
-       }
-
-       bool verified = false;
-       knot_rdata_t *rrsig = rrsigs.rdata;
-       for (uint16_t i = 0; ret == KNOT_EOK && i < rrsigs.count; ++i) {
-               ret = check_rrsig_rdata(handler, zone, node, rrsig, rrset,
-                                       context, level, &verified);
-               rrsig = knot_rdataset_next(rrsig);
-       }
-       /* Only one rrsig of covered record needs to be verified by DNSKEY. */
-       if (!verified) {
-               handler->cb(handler, zone, node->owner, SEM_ERR_RRSIG_UNVERIFIABLE,
-                           info_str);
-       }
-
-       knot_rdataset_clear(&rrsigs, NULL);
-       return KNOT_EOK;;
-}
-
 /*!
  * \brief Check if glue record for delegation is present.
  *
@@ -647,397 +400,6 @@ static int check_ds(const zone_node_t *node, semchecks_data_t *data)
        return KNOT_EOK;
 }
 
-/*!
- * \brief Run all semantic check related to RRSIG record
- *
- * \param node Node to check
- * \param data Semantic checks context data
- */
-static int check_rrsig(const zone_node_t *node, semchecks_data_t *data)
-{
-       assert(node);
-       if (node->flags & NODE_FLAGS_NONAUTH) {
-               return KNOT_EOK;
-       }
-
-       bool deleg = node->flags & NODE_FLAGS_DELEG;
-
-       int ret = KNOT_EOK;
-
-       int rrset_count = node->rrset_count;
-       for (int i = 0; ret == KNOT_EOK && i < rrset_count; i++) {
-               knot_rrset_t rrset = node_rrset_at(node, i);
-               if (rrset.type == KNOT_RRTYPE_RRSIG) {
-                       continue;
-               }
-               if (deleg && rrset.type != KNOT_RRTYPE_NSEC &&
-                   rrset.type != KNOT_RRTYPE_DS ) {
-                       continue;
-               }
-
-               ret = check_rrsig_in_rrset(data->handler, data->zone, node, &rrset,
-                                          data->time, data->level);
-       }
-       return ret;
-}
-
-/*!
- * \brief Add all RR types from a node into the bitmap.
- */
-static void bitmap_add_all_node_rrsets(dnssec_nsec_bitmap_t *bitmap,
-                                       const zone_node_t *node)
-{
-       bool deleg = node->flags & NODE_FLAGS_DELEG;
-       for (int i = 0; i < node->rrset_count; i++) {
-               knot_rrset_t rr = node_rrset_at(node, i);
-               if (deleg && (rr.type != KNOT_RRTYPE_NS &&
-                             rr.type != KNOT_RRTYPE_DS &&
-                             rr.type != KNOT_RRTYPE_NSEC &&
-                             rr.type != KNOT_RRTYPE_RRSIG)) {
-                       continue;
-               }
-               dnssec_nsec_bitmap_add(bitmap, rr.type);
-       }
-}
-
-static char *nsec3_info(const knot_dname_t *owner, char *out, size_t out_len)
-{
-       knot_dname_txt_storage_t buff;
-       char *str = knot_dname_to_str(buff, owner, sizeof(buff));
-       if (str == NULL) {
-               return NULL;
-       }
-
-       int ret = snprintf(out, out_len, "(NSEC3 owner=%s)", str);
-       if (ret <= 0 || ret >= out_len) {
-               return NULL;
-       }
-
-       return out;
-}
-
-/*!
- * \brief Check NSEC and NSEC3 type bitmap
- *
- * \param node Node to check
- * \param data Semantic checks context data
- */
-static int check_nsec_bitmap(const zone_node_t *node, semchecks_data_t *data)
-{
-       assert(node);
-       if (node->flags & NODE_FLAGS_NONAUTH) {
-               return KNOT_EOK;
-       }
-
-       bool nsec = data->level & NSEC;
-       knot_rdataset_t *nsec_rrs = NULL;
-
-       zone_node_t *nsec3_node = node_nsec3_get(node);
-
-       if (nsec) {
-               nsec_rrs = node_rdataset(node, KNOT_RRTYPE_NSEC);
-       } else if (nsec3_node != NULL) {
-               nsec_rrs = node_rdataset(nsec3_node, KNOT_RRTYPE_NSEC3);
-       }
-       if (nsec_rrs == NULL) {
-               return KNOT_EOK;
-       }
-
-       // create NSEC bitmap from node
-       dnssec_nsec_bitmap_t *node_bitmap = dnssec_nsec_bitmap_new();
-       if (node_bitmap == NULL) {
-               return KNOT_ENOMEM;
-       }
-       bitmap_add_all_node_rrsets(node_bitmap, node);
-
-       uint16_t node_wire_size = dnssec_nsec_bitmap_size(node_bitmap);
-       uint8_t *node_wire = malloc(node_wire_size);
-       if (node_wire == NULL) {
-               dnssec_nsec_bitmap_free(node_bitmap);
-               return KNOT_ENOMEM;
-       }
-       dnssec_nsec_bitmap_write(node_bitmap, node_wire);
-       dnssec_nsec_bitmap_free(node_bitmap);
-
-       // get NSEC bitmap from NSEC node
-       const uint8_t *nsec_wire = NULL;
-       uint16_t nsec_wire_size = 0;
-       if (nsec) {
-               nsec_wire = knot_nsec_bitmap(nsec_rrs->rdata);
-               nsec_wire_size = knot_nsec_bitmap_len(nsec_rrs->rdata);
-       } else {
-               nsec_wire = knot_nsec3_bitmap(nsec_rrs->rdata);
-               nsec_wire_size = knot_nsec3_bitmap_len(nsec_rrs->rdata);
-       }
-
-       if (node_wire_size != nsec_wire_size ||
-           memcmp(node_wire, nsec_wire, node_wire_size) != 0) {
-               char buff[50 + KNOT_DNAME_TXT_MAXLEN];
-               char *info = nsec ? NULL : nsec3_info(nsec3_node->owner,
-                                                     buff, sizeof(buff));
-               data->handler->cb(data->handler, data->zone, node->owner,
-                                 (nsec ? SEM_ERR_NSEC_RDATA_BITMAP : SEM_ERR_NSEC3_RDATA_BITMAP),
-                                 info);
-       }
-
-       free(node_wire);
-       return KNOT_EOK;
-}
-
-/*!
- * \brief Run NSEC related semantic checks
- *
- * \param node Node to check
- * \param data Semantic checks context data
- */
-static int check_nsec(const zone_node_t *node, semchecks_data_t *data)
-{
-       assert(node);
-       if (node->flags & NODE_FLAGS_NONAUTH) {
-               return KNOT_EOK;
-       }
-
-       if (node->rrset_count == 0) { // empty nonterminal
-               return KNOT_EOK;
-       }
-
-       /* check for NSEC record */
-       const knot_rdataset_t *nsec_rrs = node_rdataset(node, KNOT_RRTYPE_NSEC);
-       if (nsec_rrs == NULL) {
-               data->handler->cb(data->handler, data->zone, node->owner,
-                                 SEM_ERR_NSEC_NONE, NULL);
-               return KNOT_EOK;
-       }
-
-       /* Test that only one record is in the NSEC RRSet */
-       if (nsec_rrs->count != 1) {
-               data->handler->cb(data->handler, data->zone, node->owner,
-                                 SEM_ERR_NSEC_RDATA_MULTIPLE, NULL);
-       }
-
-       if (data->next_nsec != node) {
-               data->handler->cb(data->handler, data->zone, node->owner,
-                                 SEM_ERR_NSEC_RDATA_CHAIN, NULL);
-       }
-
-       /*
-        * Test that NSEC chain is coherent.
-        * We have already checked that every
-        * authoritative node contains NSEC record
-        * so checking should only be matter of testing
-        * the next link in each node.
-        */
-       knot_dname_storage_t next_domain;
-       if (knot_dname_store(next_domain, knot_nsec_next(nsec_rrs->rdata)) == 0) {
-               return KNOT_EINVAL;
-       }
-       knot_dname_to_lower(next_domain);
-
-       data->next_nsec = zone_contents_find_node(data->zone, next_domain);
-       if (data->next_nsec == NULL) {
-               data->handler->cb(data->handler, data->zone, node->owner,
-                                 SEM_ERR_NSEC_RDATA_CHAIN, NULL);
-       }
-
-       return KNOT_EOK;
-}
-
-static bool nsec3_optout_allow(const zone_node_t *node)
-{
-       return (node->flags & NODE_FLAGS_DELEG) && !node_rrtype_exists(node, KNOT_RRTYPE_DS);
-}
-
-/*!
- * \brief Check if node has NSEC3 node.
- *
- * \param node Node to check
- * \param data Semantic checks context data
- */
-static int check_nsec3_presence(const zone_node_t *node, semchecks_data_t *data)
-{
-       bool auth = (node->flags & NODE_FLAGS_NONAUTH) == 0;
-       bool empty = (node->flags & NODE_FLAGS_EMPTY) != 0;
-
-       if (!nsec3_optout_allow(node) && auth && !empty) {
-               if (node_nsec3_get(node) == NULL) {
-                       data->handler->cb(data->handler, data->zone, node->owner,
-                                         SEM_ERR_NSEC3_NONE, NULL);
-               }
-       }
-
-       return KNOT_EOK;
-}
-
-/*!
- * \brief Check NSEC3 opt-out.
- *
- * \param node Node to check
- * \param data Semantic checks context data
- */
-static int check_nsec3_opt_out(const zone_node_t *node, semchecks_data_t *data)
-{
-       if (!(node_nsec3_get(node) == NULL && node->flags & (NODE_FLAGS_DELEG | NODE_FLAGS_EMPTY))) {
-               return KNOT_EOK;
-       }
-       /* Insecure delegation, check whether it is part of opt-out span. */
-
-       const zone_node_t *nsec3_previous = NULL;
-       const zone_node_t *nsec3_node;
-       zone_contents_find_nsec3_for_name(data->zone, node->owner, &nsec3_node,
-                                         &nsec3_previous);
-
-       if (nsec3_previous == NULL) {
-               data->handler->cb(data->handler, data->zone, node->owner,
-                                 SEM_ERR_NSEC3_NONE, NULL);
-               return KNOT_EOK;
-       }
-
-       const knot_rdataset_t *previous_rrs;
-       previous_rrs = node_rdataset(nsec3_previous, KNOT_RRTYPE_NSEC3);
-       assert(previous_rrs);
-
-       /* Check for opt-out flag. */
-       uint8_t flags = knot_nsec3_flags(previous_rrs->rdata);
-       if (!(flags & 1)) {
-               data->handler->cb(data->handler, data->zone, node->owner,
-                                 SEM_ERR_NSEC3_INSECURE_DELEGATION_OPT, NULL);
-       }
-
-       return KNOT_EOK;
-}
-
-/*!
- * \brief Run checks related to NSEC3.
- *
- * Check NSEC3 node for given node.
- * Check if NSEC3 chain is coherent and cyclic.
- * \param node Node to check
- * \param data Semantic checks context data
- */
-static int check_nsec3(const zone_node_t *node, semchecks_data_t *data)
-{
-       assert(node);
-       bool auth = (node->flags & NODE_FLAGS_NONAUTH) == 0;
-       bool deleg = (node->flags & NODE_FLAGS_DELEG) != 0;
-
-       if (!auth && !deleg) {
-               return KNOT_EOK;
-       }
-
-       zone_node_t *nsec3_node = node_nsec3_get(node);
-       if (nsec3_node == NULL) {
-               return KNOT_EOK;
-       }
-
-       dnssec_nsec3_params_t params_apex = { 0 };
-       int ret = KNOT_EOK;
-
-       char buff[50 + KNOT_DNAME_TXT_MAXLEN];
-       char *info = nsec3_info(nsec3_node->owner, buff, sizeof(buff));
-
-       knot_rrset_t nsec3_rrs = node_rrset(nsec3_node, KNOT_RRTYPE_NSEC3);
-       if (knot_rrset_empty(&nsec3_rrs)) {
-               data->handler->cb(data->handler, data->zone, node->owner,
-                                 SEM_ERR_NSEC3_NONE, info);
-               goto nsec3_cleanup;
-       }
-
-       knot_rrset_t soa_rrset = node_rrset(data->zone->apex, KNOT_RRTYPE_SOA);
-       assert(!knot_rrset_empty(&soa_rrset));
-       uint32_t minimum_ttl = knot_soa_minimum(soa_rrset.rrs.rdata);
-       if (nsec3_rrs.ttl != MIN(minimum_ttl, soa_rrset.ttl)) {
-               data->handler->cb(data->handler, data->zone, node->owner,
-                                 SEM_ERR_NSEC3_RDATA_TTL, info);
-       }
-
-       // Check parameters.
-       const knot_rdataset_t *nsec3param = node_rdataset(data->zone->apex,
-                                                         KNOT_RRTYPE_NSEC3PARAM);
-       dnssec_binary_t rdata = {
-               .size = nsec3param->rdata->len,
-               .data = nsec3param->rdata->data
-       };
-       ret = dnssec_nsec3_params_from_rdata(&params_apex, &rdata);
-       if (ret != DNSSEC_EOK) {
-               ret = knot_error_from_libdnssec(ret);
-               goto nsec3_cleanup;
-       }
-
-       if (knot_nsec3_flags(nsec3_rrs.rrs.rdata) > 1) {
-               data->handler->cb(data->handler, data->zone, node->owner,
-                                 SEM_ERR_NSEC3_RDATA_FLAGS, info);
-       }
-
-       dnssec_binary_t salt = {
-               .size = knot_nsec3_salt_len(nsec3_rrs.rrs.rdata),
-               .data = (uint8_t *)knot_nsec3_salt(nsec3_rrs.rrs.rdata),
-       };
-
-       if (dnssec_binary_cmp(&salt, &params_apex.salt)) {
-               data->handler->cb(data->handler, data->zone, node->owner,
-                                 SEM_ERR_NSEC3_RDATA_SALT, info);
-       }
-
-       if (knot_nsec3_alg(nsec3_rrs.rrs.rdata) != params_apex.algorithm) {
-               data->handler->cb(data->handler, data->zone, node->owner,
-                                 SEM_ERR_NSEC3_RDATA_ALG, info);
-       }
-
-       if (knot_nsec3_iters(nsec3_rrs.rrs.rdata) != params_apex.iterations) {
-               data->handler->cb(data->handler, data->zone, node->owner,
-                                 SEM_ERR_NSEC3_RDATA_ITERS, info);
-       }
-
-       // Get next nsec3 node.
-       const zone_node_t *apex = data->zone->apex;
-       const uint8_t *next_dname_str = knot_nsec3_next(nsec3_rrs.rrs.rdata);
-       uint8_t next_dname_str_size = knot_nsec3_next_len(nsec3_rrs.rrs.rdata);
-       knot_dname_storage_t next_dname;
-       ret = knot_nsec3_hash_to_dname(next_dname, sizeof(next_dname),
-                                      next_dname_str, next_dname_str_size,
-                                      apex->owner);
-       if (ret != KNOT_EOK) {
-               goto nsec3_cleanup;
-       }
-
-       const zone_node_t *next_nsec3 = zone_contents_find_nsec3_node(data->zone,
-                                                                     next_dname);
-       if (next_nsec3 == NULL || node_prev(next_nsec3) != nsec3_node) {
-               uint8_t *next = NULL;
-               int32_t next_len = knot_base32hex_encode_alloc(next_dname_str,
-                                                              next_dname_str_size,
-                                                              &next);
-               char *hash_info = NULL;
-               if (next != NULL) {
-                       hash_info = sprintf_alloc("(next hash %.*s)", next_len, next);
-                       free(next);
-               }
-               data->handler->cb(data->handler, data->zone, node->owner,
-                                 SEM_ERR_NSEC3_RDATA_CHAIN, hash_info);
-               free(hash_info);
-       }
-
-       ret = check_rrsig(nsec3_node, data);
-       if (ret != KNOT_EOK) {
-               goto nsec3_cleanup;
-       }
-
-       // Check that the node only contains NSEC3 and RRSIG.
-       for (int i = 0; ret == KNOT_EOK && i < nsec3_node->rrset_count; i++) {
-               knot_rrset_t rrset = node_rrset_at(nsec3_node, i);
-               uint16_t type = rrset.type;
-               if (type != KNOT_RRTYPE_NSEC3 && type != KNOT_RRTYPE_RRSIG) {
-                       data->handler->cb(data->handler, data->zone, nsec3_node->owner,
-                                         SEM_ERR_NSEC3_EXTRA_RECORD, NULL);
-               }
-       }
-
-nsec3_cleanup:
-       dnssec_nsec3_params_free(&params_apex);
-
-       return ret;
-}
-
 /*!
  * \brief Check if apex node contains SOA record
  *
@@ -1134,27 +496,6 @@ static int check_dname(const zone_node_t *node, semchecks_data_t *data)
        return KNOT_EOK;
 }
 
-/*!
- * \brief Check that NSEC chain is cyclic.
- *
- * Run only once per zone. Check that last NSEC node points to first one.
- * \param data Semantic checks context data
- */
-static int check_nsec_cyclic(semchecks_data_t *data)
-{
-       if (data->next_nsec == NULL) {
-               data->handler->cb(data->handler, data->zone, data->zone->apex->owner,
-                                 SEM_ERR_NSEC_RDATA_CHAIN, NULL);
-               return KNOT_EOK;
-       }
-       if (!knot_dname_is_equal(data->next_nsec->owner, data->zone->apex->owner)) {
-               data->handler->cb(data->handler, data->zone, data->next_nsec->owner,
-                                 SEM_ERR_NSEC_RDATA_CHAIN, NULL);
-       }
-
-       return KNOT_EOK;
-}
-
 /*!
  * \brief Call all semantic checks for each node.
  *
@@ -1203,59 +544,37 @@ static void check_nsec3param(knot_rdataset_t *nsec3param, zone_contents_t *zone,
        }
 }
 
-static void check_dnskey(zone_contents_t *zone, sem_handler_t *handler)
-{
-       const knot_rdataset_t *dnskeys = node_rdataset(zone->apex, KNOT_RRTYPE_DNSKEY);
-       if (dnskeys == NULL) {
-               handler->cb(handler, zone, zone->apex->owner, SEM_ERR_DNSKEY_NONE, NULL);
-               return;
-       }
-
-       for (int i = 0; i < dnskeys->count; i++) {
-               knot_rdata_t *dnskey = knot_rdataset_at(dnskeys, i);
-               dnssec_key_t *key;
-               int ret = dnssec_key_from_rdata(&key, zone->apex->owner,
-                                               dnskey->data, dnskey->len);
-               if (ret == KNOT_EOK) {
-                       dnssec_key_free(key);
-               } else {
-                       handler->cb(handler, zone, zone->apex->owner, SEM_ERR_DNSKEY_INVALID, NULL);
-               }
-
-               if (knot_dnskey_proto(dnskey) != 3) {
-                       handler->cb(handler, zone, zone->apex->owner, SEM_ERR_DNSKEY_RDATA_PROTOCOL,
-                                   NULL);
-               }
-
-               dnssec_key_algorithm_t alg = knot_dnskey_alg(dnskey);
-               if (!dnssec_algorithm_key_support(alg)) {
-                       char *info = sprintf_alloc("(unsupported algorithm %d)", alg);
-                       handler->cb(handler, zone, zone->apex->owner, SEM_ERR_DNSKEY_INVALID, info);
-                       free(info);
-               }
-       }
-}
-
-static int mark_nsec3_optout(zone_node_t *node, _unused_ void *ctx)
+static sem_error_t err_dnssec2sem(int err)
 {
-       if (nsec3_optout_allow(node) && node_nsec3_get(node) == NULL) {
-               do {
-                       assert(!(node->flags & NODE_FLAGS_APEX));
-                       node->flags |= NODE_FLAGS_EMPTY;
-                       node = node_parent(node);
-                       node->children--;
-               } while (node->rrset_count == 0 && node->children == 0 && node_nsec3_get(node) == NULL);
+       switch (err) {
+       case KNOT_DNSSEC_ENOSIG:
+               return SEM_ERR_RRSIG_UNVERIFIABLE;
+       case KNOT_DNSSEC_ENSEC_BITMAP:
+               return SEM_ERR_NSEC_RDATA_BITMAP;
+       case KNOT_DNSSEC_ENSEC_CHAIN:
+               return SEM_ERR_NSEC_RDATA_CHAIN;
+       case KNOT_DNSSEC_ENSEC3_OPTOUT:
+               return SEM_ERR_NSEC3_INSECURE_DELEGATION_OPT;
+       default:
+               return SEM_ERR_UNKNOWN;
        }
-       return KNOT_EOK;
 }
 
-static int unmark_nsec3_optout(zone_node_t *node, _unused_ void *ctx)
+static int verify_dnssec(zone_contents_t *zone, sem_handler_t *handler, time_t time)
 {
-       if (node->flags & NODE_FLAGS_EMPTY) {
-               node->flags &= ~NODE_FLAGS_EMPTY;
-               node_parent(node)->children++;
+       zone_update_t fake_up = { .new_cont = zone, };
+       int ret = knot_dnssec_validate_zone(&fake_up, NULL, time, false);
+       if (fake_up.validation_hint.node != NULL) { // validation found an issue
+               char type_str[16] = { 0 };
+               knot_rrtype_to_string(fake_up.validation_hint.rrtype, type_str, sizeof(type_str));
+               handler->cb(handler, zone, fake_up.validation_hint.node, err_dnssec2sem(ret), type_str);
+               return KNOT_EOK;
+       } else if (ret == KNOT_INVALID_PUBLIC_KEY) { // validation failed due to invalid DNSKEY
+               handler->cb(handler, zone, zone->apex->owner, SEM_ERR_DNSKEY_INVALID, NULL);
+               return KNOT_EOK;
+       } else { // validation failed by itself
+               return ret;
        }
-       return KNOT_EOK;
 }
 
 int sem_checks_process(zone_contents_t *zone, semcheck_optional_t optional, sem_handler_t *handler,
@@ -1272,7 +591,6 @@ int sem_checks_process(zone_contents_t *zone, semcheck_optional_t optional, sem_
        semchecks_data_t data = {
                .handler = handler,
                .zone = zone,
-               .next_nsec = zone->apex,
                .level = MANDATORY,
                .time = time,
        };
@@ -1293,20 +611,10 @@ int sem_checks_process(zone_contents_t *zone, semcheck_optional_t optional, sem_
                        } else {
                                data.level |= NSEC;
                        }
-                       check_dnskey(zone, handler);
                }
        }
 
-       if (data.level & NSEC3) {
-               int ret = zone_tree_apply(zone->nodes, mark_nsec3_optout, NULL);
-               if (ret != KNOT_EOK) {
-                       return ret;
-               }
-       }
        int ret = zone_contents_apply(zone, do_checks_in_tree, &data);
-       if (data.level & NSEC3) {
-               (void)zone_tree_apply(zone->nodes, unmark_nsec3_optout, NULL);
-       }
        if (ret != KNOT_EOK) {
                return ret;
        }
@@ -1314,13 +622,10 @@ int sem_checks_process(zone_contents_t *zone, semcheck_optional_t optional, sem_
                return KNOT_ESEMCHECK;
        }
 
-       // check cyclic chain after every node was checked
-       if (data.level & NSEC) {
-               check_nsec_cyclic(&data);
-       }
-       if (data.handler->fatal_error) {
-               return KNOT_ESEMCHECK;
+       if (optional == SEMCHECK_DNSSEC_ON ||
+           (optional == SEMCHECK_DNSSEC_AUTO && zone->dnssec)) {
+               ret = verify_dnssec(zone, handler, time);
        }
 
-       return KNOT_EOK;
+       return ret;
 }
similarity index 98%
rename from tests/knot/semantic_check_data/no_error_nsec3_delegation.signed
rename to tests/knot/semantic_check_data/delegation.signed
index 4c85a4b3ac72d4277225143f6bb58921928a5f90..200721679a5d4364d6c99eaf10da64c140fdf173 100644 (file)
@@ -1,4 +1,4 @@
-; Zone without any semantic error
+; Delegation NS and glue signed despite mustn't.
 
 example.com.           3600    IN SOA  dns1.example.com. hostmaster.example.com. (
                                        2010111220 ; serial
index 3cc054238dbe185dfd34f5b4dfe736e951794c47..614446ab9b79c2163aee1ae2e2a3b0ca7827ac3f 100644 (file)
@@ -95,34 +95,31 @@ expect_error "glue_apex_one.missing" 0 1 "$NS_GLUE"
 expect_error "glue_besides.missing" 0 1 "$NS_GLUE"
 expect_error "glue_deleg.missing" 0 1 "$NS_GLUE"
 expect_error "glue_in_apex.missing" 0 1 "$NS_GLUE"
-expect_error "different_signer_name.signed" 0 1 "$RRSIG_RDATA_DNSKEY_OWNER \(record type NSEC\)"
-expect_error "different_signer_name.signed" 0 1 "$RRSIG_UNVERIFIABLE \(record type NSEC\)"
-expect_error "no_rrsig.signed" 0 1 "$RRSIG_NO_RRSIG \(record type A\)"
-expect_error "no_rrsig.signed" 0 1 "$RRSIG_NO_RRSIG \(record type NSEC\)"
-expect_error "no_rrsig_with_delegation.signed" 0 1 "$RRSIG_NO_RRSIG \(record type NSEC\)"
+expect_error "different_signer_name.signed" 0 1 "$RRSIG_UNVERIFIABLE"
+expect_error "no_rrsig.signed" 0 1 "$RRSIG_UNVERIFIABLE"
+expect_error "no_rrsig_with_delegation.signed" 0 1 "$RRSIG_UNVERIFIABLE"
 expect_error "nsec_broken_chain_01.signed" 0 1 "$NSEC_RDATA_CHAIN"
 expect_error "nsec_broken_chain_02.signed" 0 1 "$NSEC_RDATA_CHAIN"
-expect_error "nsec_missing.signed" 0 1 "$NSEC_NONE"
-expect_error "nsec_multiple.signed" 0 1 "$NSEC_RDATA_MULTIPLE"
+expect_error "nsec_missing.signed" 0 1 "$NSEC_RDATA_BITMAP"
+expect_error "nsec_multiple.signed" 0 1 "$NSEC_RDATA_BITMAP"
 expect_error "nsec_wrong_bitmap_01.signed" 0 1 "$NSEC_RDATA_BITMAP"
 expect_error "nsec_wrong_bitmap_02.signed" 0 1 "$NSEC_RDATA_BITMAP"
-expect_error "nsec3_missing.signed" 0 1 "$NSEC3_NONE"
-expect_error "nsec3_optout_ent.invalid" 0 1 "$NSEC3_NONE"
-expect_error "nsec3_wrong_bitmap_01.signed" 0 1 "$NSEC3_RDATA_BITMAP"
-expect_error "nsec3_wrong_bitmap_02.signed" 0 1 "$NSEC3_RDATA_BITMAP"
-expect_error "nsec3_ds.signed" 0 1 "$NSEC3_NONE"
+expect_error "nsec3_missing.signed" 0 1 "$NSEC_RDATA_BITMAP"
+expect_error "nsec3_optout_ent.invalid" 0 1 "$NSEC_RDATA_BITMAP"
+expect_error "nsec3_wrong_bitmap_01.signed" 0 1 "$NSEC_RDATA_BITMAP"
+expect_error "nsec3_wrong_bitmap_02.signed" 0 1 "$NSEC_RDATA_BITMAP"
+expect_error "nsec3_ds.signed" 0 1 "$NSEC_RDATA_BITMAP"
 expect_error "nsec3_optout.signed" 0 1 "$NSEC3_INSECURE_DELEGATION_OPT"
-expect_error "nsec3_chain_01.signed" 0 1 "$NSEC3_RDATA_CHAIN"
-expect_error "nsec3_chain_02.signed" 0 2 "$NSEC3_RDATA_CHAIN"
-expect_error "nsec3_chain_03.signed" 0 2 "$NSEC3_RDATA_CHAIN"
-expect_error "nsec3_param_invalid.signed" 0 1 "$NSEC3_ALG"
-expect_error "nsec3_param_invalid.signed" 0 1 "$NSEC3_ITERS"
+expect_error "nsec3_chain_01.signed" 0 1 "$NSEC_RDATA_CHAIN"
+expect_error "nsec3_chain_02.signed" 0 1 "$NSEC_RDATA_CHAIN"
+expect_error "nsec3_chain_03.signed" 0 1 "$NSEC_RDATA_CHAIN"
+expect_error "nsec3_param_invalid.signed" 0 1 "$NSEC_RDATA_BITMAP"
 expect_error "nsec3_param_invalid.signed" 0 1 "$NSEC3PARAM_FLAGS"
-expect_error "rrsig_signed.signed" 0 1 "$RRSIG_SIGNED"
-expect_error "rrsig_rdata_ttl.signed" 0 1 "$RRSIG_RDATA_TTL \(record type A\)"
-expect_error "duplicate.signature" 0 7 "$RRSIG_EXPIRED"
-expect_error "missing.signed" 0 1 "$NSEC_NONE"
-expect_error "dnskey_param_error.signed" 0 1 "$DNSKEY_PROTO"
+expect_error "rrsig_signed.signed" 0 1 "$RRSIG_UNVERIFIABLE"
+expect_error "rrsig_rdata_ttl.signed" 0 1 "$RRSIG_UNVERIFIABLE"
+expect_error "duplicate.signature" 0 1 "$RRSIG_UNVERIFIABLE"
+expect_error "missing.signed" 0 1 "$NSEC_RDATA_BITMAP"
+expect_error "dnskey_param_error.signed" 0 1 "$RRSIG_UNVERIFIABLE" # FIXME this shall rather fail on invalid pubkey
 expect_error "invalid_ds.signed" 0 2 "$DS_ALG \(keytag 60485\)"
 expect_error "cdnskey.invalid" 0 1 "$CDS_NOT_MATCH"
 expect_error "cdnskey.invalid.param" 0 1 "$CDS_NOT_MATCH"
@@ -133,10 +130,10 @@ expect_error "cdnskey.orphan.cds" 0 1 "$CDS_NOT_MATCH"
 expect_error "cdnskey.orphan.cdnskey" 0 1 "$CDNSKEY_NO_CDS"
 expect_error "cdnskey.delete.invalid.cds" 0 1 "$CDNSKEY_DELETE"
 expect_error "cdnskey.delete.invalid.cdnskey" 0 1 "$CDNSKEY_DELETE"
+expect_error "delegation.signed" 0 1 "$NSEC_RDATA_BITMAP"
 
 test_correct "rrsig_ttl.signed"
 test_correct "no_error_delegation_bitmap.signed"
-test_correct "no_error_nsec3_delegation.signed"
 test_correct "no_error_nsec3_optout.signed"
 test_correct "glue_wildcard.valid"
 test_correct "glue_no_foreign.valid"
@@ -178,5 +175,6 @@ test_correct_no_dnssec "cdnskey.orphan.cds"
 test_correct_no_dnssec "cdnskey.orphan.cdnskey"
 test_correct_no_dnssec "cdnskey.delete.invalid.cds"
 test_correct_no_dnssec "cdnskey.delete.invalid.cdnskey"
+test_correct_no_dnssec "delegation.signed"
 
 rm $LOG