mirror of
https://gitlab.nic.cz/knot/knot-dns.git
synced 2026-05-28 04:02:31 -04:00
sematic-checks: use verification routines from dnssec-verify
This commit is contained in:
parent
e80a0576ca
commit
fa828c5da7
4 changed files with 64 additions and 757 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -15,19 +15,16 @@
|
|||
*/
|
||||
|
||||
#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(¶ms_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, ¶ms_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(¶ms_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)
|
||||
static sem_error_t err_dnssec2sem(int err)
|
||||
{
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static int mark_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 (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);
|
||||
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;
|
||||
}
|
||||
|
||||
static int unmark_nsec3_optout(zone_node_t *node, _unused_ void *ctx)
|
||||
{
|
||||
if (node->flags & NODE_FLAGS_EMPTY) {
|
||||
node->flags &= ~NODE_FLAGS_EMPTY;
|
||||
node_parent(node)->children++;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue