/* Don't update ZONEMD if no change and ZONEMD is up-to-date.
* If ZONEFILE_LOAD_DIFSE, the change is non-empty and ZONEMD
* is directly updated without its verification. */
- if (!zone_update_no_change(&up) || !zone_contents_digest_exists(up.new_cont, digest_alg, false)) {
+ if (!zone_update_no_change(&up) || !zone_contents_digest_exists(up.new_cont, digest_alg, false, false)) {
if (zone_update_to(&up) == NULL || middle_serial == zone->zonefile.serial) {
ret = zone_update_increment_soa(&up, conf);
}
}
// If the original ZONEMD is outdated, use the reverted changeset again.
- if (update_zonemd && !zone_contents_digest_exists(up.new_cont, digest_alg, false)) {
+ if (update_zonemd && !zone_contents_digest_exists(up.new_cont, digest_alg, false, false)) {
ret = zone_update_apply_changeset(&up, cpy);
changeset_free(cpy);
if (ret != KNOT_EOK) {
if (!conf_bool(&val)) {
return KNOT_EOK;
}
+ val = conf_zone_get(conf, C_DNSSEC_SIGNING, update->zone->name);
- int ret = zone_contents_digest_verify(update->new_cont);
+ int ret = zone_contents_digest_verify(update->new_cont, conf_bool(&val));
if (ret != KNOT_EOK) {
log_zone_error(update->zone->name, "ZONEMD, verification failed (%s)",
knot_strerror(ret));
uint8_t *buf;
struct dnssec_digest_ctx *digest_ctx;
const zone_node_t *apex;
+ bool ignore_dnssec;
} contents_digest_ctx_t;
static int digest_rrset(knot_rrset_t *rrset, const zone_node_t *node, void *vctx)
return KNOT_EOK;
}
+ // ignore DNSSEC if verifying on signer
+ if (ctx->ignore_dnssec && knot_rrtype_is_dnssec(rrset->type)) {
+ return KNOT_EOK;
+ }
+
// ignore RRSIGs of apex ZONEMD
if (node == ctx->apex && rrset->type == KNOT_RRTYPE_RRSIG) {
knot_rdataset_t cpy = rrset->rrs, zonemd_rrsig = { 0 };
}
int zone_contents_digest(const zone_contents_t *contents, int algorithm,
+ bool ignore_dnssec,
uint8_t **out_digest, size_t *out_size)
{
if (out_digest == NULL || out_size == NULL) {
.buf_size = DIGEST_BUF_MIN,
.buf = malloc(DIGEST_BUF_MIN),
.apex = contents->apex,
+ .ignore_dnssec = ignore_dnssec,
};
if (ctx.buf == NULL) {
return KNOT_ENOMEM;
return ret;
}
-static int verify_zonemd(const knot_rdata_t *zonemd, const zone_contents_t *contents)
+static int verify_zonemd(const knot_rdata_t *zonemd, const zone_contents_t *contents,
+ bool ignore_dnssec)
{
uint8_t *computed = NULL;
size_t comp_size = 0;
int ret = zone_contents_digest(contents, knot_zonemd_algorithm(zonemd),
- &computed, &comp_size);
+ ignore_dnssec, &computed, &comp_size);
if (ret != KNOT_EOK) {
return ret;
}
return ret;
}
-bool zone_contents_digest_exists(const zone_contents_t *contents, int alg, bool no_verify)
+bool zone_contents_digest_exists(const zone_contents_t *contents, int alg, bool no_verify,
+ bool ignore_dnssec)
{
if (alg == 0) {
return true;
return true;
}
- return verify_zonemd(zonemd->rdata, contents) == KNOT_EOK;
+ return verify_zonemd(zonemd->rdata, contents, ignore_dnssec) == KNOT_EOK;
}
static bool check_duplicate_schalg(const knot_rdataset_t *zonemd, int check_upto,
return true;
}
-int zone_contents_digest_verify(const zone_contents_t *contents)
+int zone_contents_digest_verify(const zone_contents_t *contents, bool ignore_dnssec)
{
if (contents == NULL) {
return KNOT_EEMPTYZONE;
rr = knot_rdataset_next(rr);
}
- return supported == NULL ? KNOT_ENOTSUP : verify_zonemd(supported, contents);
+ return supported == NULL ? KNOT_ENOTSUP : verify_zonemd(supported, contents, ignore_dnssec);
}
static ptrdiff_t zonemd_hash_offs(void)
return KNOT_EOK;
}
} else {
- int ret = zone_contents_digest(update->new_cont, algorithm, &digest, &dsize);
+ int ret = zone_contents_digest(update->new_cont, algorithm, false, &digest, &dsize);
if (ret != KNOT_EOK) {
return ret;
}
/*!
* \brief Compute hash over whole zone by concatenating RRSets in wire format.
*
- * \param contents Zone contents to digest.
- * \param algorithm Algorithm to use.
- * \param out_digest Output: buffer with computed hash (to be freed).
- * \param out_size Output: size of the resulting hash.
+ * \param contents Zone contents to digest.
+ * \param algorithm Algorithm to use.
+ * \param ignore_dnssec Skip DNSSEC-related records while computing the digest.
+ * \param out_digest Output: buffer with computed hash (to be freed).
+ * \param out_size Output: size of the resulting hash.
*
* \return KNOT_E*
*/
int zone_contents_digest(const zone_contents_t *contents, int algorithm,
+ bool ignore_dnssec,
uint8_t **out_digest, size_t *out_size);
/*!
*
* \note Special value 255 of algorithm means that ZONEMD shall not exist.
*
- * \param contents Zone contents to be verified.
- * \param alg Required algorithm of the ZONEMD.
- * \param no_verify Don't verify the validness of the digest in ZONEMD.
+ * \param contents Zone contents to be verified.
+ * \param alg Required algorithm of the ZONEMD.
+ * \param no_verify Don't verify the validness of the digest in ZONEMD.
+ * \param ignore_dnssec Skip DNSSEC-related records when eventually verifying the digest.
*/
-bool zone_contents_digest_exists(const zone_contents_t *contents, int alg, bool no_verify);
+bool zone_contents_digest_exists(const zone_contents_t *contents, int alg, bool no_verify,
+ bool ignore_dnssec);
/*!
* \brief Verify zone dgest in ZONEMD record.
*
- * \param contents Zone contents ot be verified.
+ * \param contents Zone contents ot be verified.
+ * \param ignore_dnssec Skip DNSSEC-related records while computing the digest.
*
* \retval KNOT_EEMPTYZONE The zone is empty.
* \retval KNOT_ENOENT There is no ZONEMD in contents' apex.
* \retval KNOT_EMALF The computed hash differs from ZONEMD.
* \return KNOT_E*
*/
-int zone_contents_digest_verify(const zone_contents_t *contents);
+int zone_contents_digest_verify(const zone_contents_t *contents, bool ignore_dnssec);
struct zone_update;
/*!
{
if (!conf_updated) {
conf_val_t digest = conf_zone_get(conf, C_ZONEMD_GENERATE, zone->name);
- if (zone->contents != NULL && !zone_contents_digest_exists(zone->contents, conf_opt(&digest), true)) {
+ if (zone->contents != NULL && !zone_contents_digest_exists(zone->contents, conf_opt(&digest), true, false)) {
conf_updated = true;
}
}
}
if (zonemd) {
- ret = zone_contents_digest_verify(contents);
+ ret = zone_contents_digest_verify(contents, false);
if (ret != KNOT_EOK) {
if (stats.error_count > 0 && !stats.handler.error) {
(void)fprintf(stderr, "\n");
signer.conf_zone(zones).zonemd_verify = True
signer.dnssec(zones).enable = True
-signer.conf_zone(zones).zonemd_generate = "zonemd-sha384"
+signer.conf_zone(zones).zonemd_generate = random.choice(["zonemd-sha384", "zonemd-sha512"])
slave.conf_zone(zones).dnssec_validation = True
slave.conf_zone(zones).zonemd_verify = True
serials = slave.zones_wait(zones)
master.random_ddns(zones, allow_empty=False)
-t.sleep(4)
-slave.zones_wait(zones, serials, equal=True, greater=False)
-signer.ctl("zone-retransfer")
serials = slave.zones_wait(zones, serials)
-signer.conf_zone(zones).zonemd_verify = False
-signer.gen_confile()
-signer.reload()
-
+signer.ctl("zone-freeze", wait=True)
+master.random_ddns(zones, allow_empty=False)
master.random_ddns(zones, allow_empty=False)
+signer.ctl("zone-thaw")
serials = slave.zones_wait(zones, serials)
-if signer.log_search_count("fallback to AXFR ") > 0: # NOTE without the trailing space the message can appear for outgoing IXFR as well, which it actually should
- set_err("AXFR fallback")
t.end()
static int check_contents(const char *zone_str)
{
zone_contents_t *cont = str2contents(zone_str);
- int ret = zone_contents_digest_verify(cont);
+ int ret = zone_contents_digest_verify(cont, false);
zone_contents_deep_free(cont);
return ret;
}