Commit 48a0046d authored by Libor Peltan's avatar Libor Peltan Committed by Daniel Salzman

dnssec: after DS check, KSK remains active for DS TTL

parent 04a0dad5
......@@ -384,7 +384,7 @@ static int zone_ksk_sbm_confirm(zone_t *zone, ctl_args_t *args)
return ret;
}
ret = knot_dnssec_ksk_sbm_confirm(&ctx);
ret = knot_dnssec_ksk_sbm_confirm(&ctx, 0);
kdnssec_ctx_deinit(&ctx);
conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name);
......
......@@ -16,6 +16,7 @@
#include <assert.h>
#include "contrib/macros.h"
#include "knot/common/log.h"
#include "knot/conf/conf.h"
#include "knot/dnssec/ds_query.h"
......@@ -50,8 +51,12 @@ struct ds_query_data {
zone_key_t *key;
uint16_t edns_max_payload;
bool ds_ok;
bool result_logged;
uint32_t ttl;
};
static int ds_query_begin(knot_layer_t *layer, void *params)
......@@ -64,6 +69,7 @@ static int ds_query_begin(knot_layer_t *layer, void *params)
static int ds_query_produce(knot_layer_t *layer, knot_pkt_t *pkt)
{
struct ds_query_data *data = layer->data;
struct query_edns_data edns = { .max_payload = data->edns_max_payload, .do_flag = true, };
query_init_pkt(pkt);
......@@ -72,6 +78,11 @@ static int ds_query_produce(knot_layer_t *layer, knot_pkt_t *pkt)
return KNOT_STATE_FAIL;
}
r = query_put_edns(pkt, &edns);
if (r != KNOT_EOK) {
return KNOT_STATE_FAIL;
}
knot_wire_set_rd(pkt->wire);
return KNOT_STATE_CONSUME;
......@@ -94,14 +105,19 @@ static int ds_query_consume(knot_layer_t *layer, knot_pkt_t *pkt)
for (size_t j = 0; j < answer->count; j++) {
const knot_rrset_t *rr = knot_pkt_rr(answer, j);
if (!rr || rr->type != KNOT_RRTYPE_DS || rr->rrs.count != 1) {
ns_log(LOG_WARNING, data->zone_name, LOG_OPERATION_PARENT,
LOG_DIRECTION_OUT, data->remote, "malformed message");
return KNOT_STATE_FAIL;
}
if (match_key_ds(data->key, rr->rrs.rdata)) {
match = true;
switch ((rr && rr->rrs.count > 0) ? rr->type : 0) {
case KNOT_RRTYPE_DS:
if (match_key_ds(data->key, rr->rrs.rdata)) {
match = true;
if (data->ttl == 0) { // fallback: if there is no RRSIG
data->ttl = rr->ttl;
}
}
break;
case KNOT_RRTYPE_RRSIG:
data->ttl = knot_rrsig_original_ttl(rr->rrs.rdata);
break;
default:
break;
}
}
......@@ -110,7 +126,9 @@ static int ds_query_consume(knot_layer_t *layer, knot_pkt_t *pkt)
LOG_DIRECTION_OUT, data->remote, "KSK submission attempt: %s",
(match ? "positive" : "negative"));
if (match) data->ds_ok = true;
if (match) {
data->ds_ok = true;
}
return KNOT_STATE_DONE;
}
......@@ -122,7 +140,8 @@ static const knot_layer_api_t ds_query_api = {
.finish = NULL,
};
static int try_ds(const knot_dname_t *zone_name, const conf_remote_t *parent, zone_key_t *key, size_t timeout)
static int try_ds(const knot_dname_t *zone_name, const conf_remote_t *parent, zone_key_t *key,
size_t timeout, uint32_t *ds_ttl)
{
// TODO: Abstract interface to issue DNS queries. This is almost copy-pasted.
......@@ -135,6 +154,7 @@ static int try_ds(const knot_dname_t *zone_name, const conf_remote_t *parent, zo
.key = key,
.ds_ok = false,
.result_logged = false,
.ttl = 0,
};
knot_requestor_t requestor;
......@@ -155,6 +175,10 @@ static int try_ds(const knot_dname_t *zone_name, const conf_remote_t *parent, zo
return KNOT_ENOMEM;
}
data.edns_max_payload = dst->sa_family == AF_INET6 ?
conf()->cache.srv_max_ipv6_udp_payload :
conf()->cache.srv_max_ipv4_udp_payload;
int ret = knot_requestor_exec(&requestor, req, timeout);
knot_request_free(req, NULL);
knot_requestor_clear(&requestor);
......@@ -169,17 +193,22 @@ static int try_ds(const knot_dname_t *zone_name, const conf_remote_t *parent, zo
LOG_DIRECTION_OUT, data.remote, "failed (%s)", knot_strerror(ret));
}
*ds_ttl = data.ttl;
return ret;
}
static bool parents_have_ds(kdnssec_ctx_t *kctx, zone_key_t *key, size_t timeout)
static bool parents_have_ds(kdnssec_ctx_t *kctx, zone_key_t *key, size_t timeout,
uint32_t *max_ds_ttl)
{
bool success = false;
dynarray_foreach(parent, knot_kasp_parent_t, i, kctx->policy->parents) {
success = false;
for (size_t j = 0; j < i->addrs; j++) {
int ret = try_ds(kctx->zone->dname, &i->addr[j], key, timeout);
uint32_t ds_ttl = 0;
int ret = try_ds(kctx->zone->dname, &i->addr[j], key, timeout, &ds_ttl);
if (ret == KNOT_EOK) {
*max_ds_ttl = MAX(*max_ds_ttl, ds_ttl);
success = true;
break;
}
......@@ -194,12 +223,13 @@ static bool parents_have_ds(kdnssec_ctx_t *kctx, zone_key_t *key, size_t timeout
int knot_parent_ds_query(kdnssec_ctx_t *kctx, zone_keyset_t *keyset, size_t timeout)
{
uint32_t max_ds_ttl = 0;
for (size_t i = 0; i < keyset->count; i++) {
zone_key_t *key = &keyset->keys[i];
if (key->is_ksk &&
key->cds_priority > 1) {
if (parents_have_ds(kctx, key, timeout)) {
return knot_dnssec_ksk_sbm_confirm(kctx);
if (key->is_ksk && key->cds_priority > 1) {
if (parents_have_ds(kctx, key, timeout, &max_ds_ttl)) {
return knot_dnssec_ksk_sbm_confirm(kctx, max_ds_ttl);
} else {
return KNOT_ENOENT;
}
......
......@@ -322,6 +322,7 @@ static knot_time_t ksk_retire_time(knot_time_t retire_active_time, const kdnssec
if (retire_active_time <= 0) {
return 0;
}
// this is not correct! It should be parent DS TTL.
return knot_time_add(retire_active_time, ctx->policy->propagation_delay + ctx->policy->dnskey_ttl);
}
......@@ -388,8 +389,11 @@ static roll_action_t next_action(kdnssec_ctx_t *ctx, zone_sign_roll_flags_t flag
}
break;
case DNSSEC_KEY_STATE_RETIRE_ACTIVE:
keytime = ksk_retire_time(key->timing.retire_active, ctx);
restype = RETIRE;
if (key->timing.retire == 0) { // this shouldn't normally happen
// when a KSK is retire_active, it has already retire or post_active timer set
keytime = ksk_retire_time(key->timing.retire_active, ctx);
restype = RETIRE;
}
break;
case DNSSEC_KEY_STATE_POST_ACTIVE:
keytime = alg_remove_time(key->timing.post_active, ctx);
......@@ -473,7 +477,7 @@ static int submit_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey)
return KNOT_EOK;
}
static int exec_new_signatures(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey)
static int exec_new_signatures(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey, uint32_t active_retire_delay)
{
if (newkey->is_ksk) {
log_zone_notice(ctx->zone->dname, "DNSSEC, KSK submission, confirmed");
......@@ -491,6 +495,14 @@ static int exec_new_signatures(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey)
key->timing.retire = ctx->now;
}
}
if (newkey->is_ksk && (keystate == DNSSEC_KEY_STATE_ACTIVE ||
keystate == DNSSEC_KEY_STATE_RETIRE_ACTIVE)) {
if (keyalg != dnssec_key_get_algorithm(newkey->key)) {
key->timing.post_active = ctx->now + active_retire_delay;
} else if (key->is_ksk) {
key->timing.retire = ctx->now + active_retire_delay;
}
}
}
if (newkey->is_ksk) {
......@@ -656,7 +668,7 @@ int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_roll_flags_t flags,
reschedule->plan_ds_query = true;
break;
case REPLACE:
ret = exec_new_signatures(ctx, next.key);
ret = exec_new_signatures(ctx, next.key, 0);
break;
case RETIRE:
ret = exec_ksk_retire(ctx, next.key);
......@@ -690,12 +702,12 @@ int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_roll_flags_t flags,
return ret;
}
int knot_dnssec_ksk_sbm_confirm(kdnssec_ctx_t *ctx)
int knot_dnssec_ksk_sbm_confirm(kdnssec_ctx_t *ctx, uint32_t retire_delay)
{
for (size_t i = 0; i < ctx->zone->num_keys; i++) {
knot_kasp_key_t *key = &ctx->zone->keys[i];
if (key->is_ksk && get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY) {
int ret = exec_new_signatures(ctx, key);
int ret = exec_new_signatures(ctx, key, retire_delay);
if (ret == KNOT_EOK) {
ret = kdnssec_ctx_commit(ctx);
}
......
......@@ -42,11 +42,12 @@ int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_roll_flags_t flags,
/*!
* \brief Set the submitted KSK to active state and the active one to retired
*
* \param ctx zone signing context
* \param ctx Zone signing context.
* \param retire_delay Retire event delay.
*
* \return KNOT_E*
*/
int knot_dnssec_ksk_sbm_confirm(kdnssec_ctx_t *ctx);
int knot_dnssec_ksk_sbm_confirm(kdnssec_ctx_t *ctx, uint32_t retire_delay);
/*!
* \brief Is there a key in sumbmission phase?
......
......@@ -90,6 +90,10 @@ int query_put_edns(knot_pkt_t *pkt, const struct query_edns_data *edns)
return ret;
}
if (edns->do_flag) {
knot_edns_set_do(&opt_rr);
}
if (edns->custom_code != 0) {
ret = knot_edns_add_option(&opt_rr, edns->custom_code,
edns->custom_len, edns->custom_data,
......
......@@ -24,6 +24,7 @@
*/
struct query_edns_data {
uint16_t max_payload;
bool do_flag;
// Custom EDNS option:
uint16_t custom_code;
......
......@@ -163,6 +163,8 @@ parent = t.server("knot")
parent_zone = t.zone("com.", storage=".")
t.link(parent_zone, parent)
parent.dnssec(parent_zone).enable = True
child = t.server("knot")
child_zone = t.zone("example.com.")
t.link(child_zone, child)
......@@ -171,7 +173,7 @@ def cds_submission():
cds = child.dig(ZONE, "CDS")
cds_rdata = cds.resp.answer[0].to_rdataset()[0].to_text()
up = parent.update(parent_zone)
up.add(ZONE, 3600, "DS", cds_rdata)
up.add(ZONE, 7, "DS", cds_rdata)
up.send("NOERROR")
child.zonefile_sync = 24 * 60 * 60
......@@ -193,6 +195,9 @@ ZONE = "example.com."
t.start()
child.zone_wait(child_zone)
cds_submission()
t.sleep(5)
pregenerate_key(child, child_zone, "ECDSAP256SHA256")
watch_alg_rollover(t, child, child_zone, 2, 1, "KZSK to CSK alg", "ECDSAP256SHA256", True, cds_submission)
......
......@@ -161,6 +161,7 @@ ModOnlineSign.check()
parent = t.server("knot")
parent_zone = t.zone("com.", storage=".")
t.link(parent_zone, parent)
parent.dnssec(parent_zone).enable = True
child = t.server("knot")
child_zone = t.zone("example.com.")
......@@ -170,7 +171,7 @@ def cds_submission():
cds = child.dig(ZONE, "CDS")
cds_rdata = cds.resp.answer[0].to_rdataset()[0].to_text()
up = parent.update(parent_zone)
up.add(ZONE, 3600, "DS", cds_rdata)
up.add(ZONE, 7, "DS", cds_rdata)
up.send("NOERROR")
child.zonefile_sync = 24 * 60 * 60
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment