Commit be44184f authored by Libor Peltan's avatar Libor Peltan Committed by Daniel Salzman

ksk rollover: implemented

parent commit confirmed for now only by knotc
parent f4599e58
......@@ -190,6 +190,8 @@ static const yp_item_t desc_policy[] = {
CONF_IO_FRLD_ZONES },
{ C_ZSK_LIFETIME, YP_TINT, YP_VINT = { 1, UINT32_MAX, DAYS(30), YP_STIME },
CONF_IO_FRLD_ZONES },
{ C_KSK_LIFETIME, YP_TINT, YP_VINT = { 1, UINT32_MAX, DAYS(3650), YP_STIME },
CONF_IO_FRLD_ZONES },
{ C_PROPAG_DELAY, YP_TINT, YP_VINT = { 0, UINT32_MAX, HOURS(1), YP_STIME },
CONF_IO_FRLD_ZONES },
{ C_RRSIG_LIFETIME, YP_TINT, YP_VINT = { 1, UINT32_MAX, DAYS(14), YP_STIME },
......
......@@ -117,6 +117,7 @@
#define C_ZONE "\x04""zone"
#define C_ZONEFILE_SYNC "\x0D""zonefile-sync"
#define C_ZSK_LIFETIME "\x0C""zsk-lifetime"
#define C_KSK_LIFETIME "\x0C""ksk-lifetime"
#define C_ZSK_SIZE "\x08""zsk-size"
enum {
......
......@@ -22,6 +22,7 @@
#include "knot/common/stats.h"
#include "knot/conf/confio.h"
#include "knot/ctl/commands.h"
#include "knot/dnssec/key-events.h"
#include "knot/events/handlers.h"
#include "knot/nameserver/query_module.h"
#include "knot/updates/zone-update.h"
......@@ -343,6 +344,33 @@ static int zone_sign(zone_t *zone, ctl_args_t *args)
return KNOT_EOK;
}
static int zone_ksk_submittion_confirm(zone_t *zone, ctl_args_t *args)
{
const char *data = args->data[KNOT_CTL_IDX_OWNER];
uint16_t keytag;
if (data == NULL || sscanf(data, "%hu", &keytag) != 1) {
return KNOT_EINVAL;
}
conf_val_t policy = conf_zone_get(conf(), C_DNSSEC_POLICY, zone->name);
kdnssec_ctx_t ctx = { 0 };
int ret = kdnssec_ctx_init(&ctx, zone->name, &policy);
if (ret != KNOT_EOK) {
return ret;
}
ret = knot_dnssec_ksk_submittion_confirm(&ctx, keytag);
kdnssec_ctx_deinit(&ctx);
zone_events_schedule_now(zone, ZONE_EVENT_DNSSEC);
// NOT zone_events_schedule_user(), intentionally
return ret;
}
static int zone_freeze(zone_t *zone, ctl_args_t *args)
{
UNUSED(args);
......@@ -1157,6 +1185,8 @@ static int ctl_zone(ctl_args_t *args, ctl_cmd_t cmd)
return zones_apply(args, zone_flush);
case CTL_ZONE_SIGN:
return zones_apply(args, zone_sign);
case CTL_ZONE_SUBMITTION_CONFIRM:
return zones_apply(args, zone_ksk_submittion_confirm);
case CTL_ZONE_FREEZE:
return zones_apply(args, zone_freeze);
case CTL_ZONE_THAW:
......@@ -1585,6 +1615,7 @@ static const desc_t cmd_table[] = {
[CTL_ZONE_RETRANSFER] = { "zone-retransfer", ctl_zone },
[CTL_ZONE_FLUSH] = { "zone-flush", ctl_zone },
[CTL_ZONE_SIGN] = { "zone-sign", ctl_zone },
[CTL_ZONE_SUBMITTION_CONFIRM] = { "zone-submittion-confirm", ctl_zone },
[CTL_ZONE_FREEZE] = { "zone-freeze", ctl_zone },
[CTL_ZONE_THAW] = { "zone-thaw", ctl_zone },
......
......@@ -60,6 +60,7 @@ typedef enum {
CTL_ZONE_RETRANSFER,
CTL_ZONE_FLUSH,
CTL_ZONE_SIGN,
CTL_ZONE_SUBMITTION_CONFIRM,
CTL_ZONE_FREEZE,
CTL_ZONE_THAW,
......
......@@ -49,6 +49,9 @@ static void policy_load(knot_kasp_policy_t *policy, conf_val_t *id)
val = conf_id_get(conf(), C_POLICY, C_ZSK_LIFETIME, id);
policy->zsk_lifetime = conf_int(&val);
val = conf_id_get(conf(), C_POLICY, C_KSK_LIFETIME, id);
policy->ksk_lifetime = conf_int(&val);
val = conf_id_get(conf(), C_POLICY, C_PROPAG_DELAY, id);
policy->propagation_delay = conf_int(&val);
......
......@@ -65,6 +65,7 @@ typedef struct {
uint16_t zsk_size;
uint32_t dnskey_ttl;
uint32_t zsk_lifetime;
uint32_t ksk_lifetime;
bool singe_type_signing;
// RRSIG
uint32_t rrsig_lifetime;
......
......@@ -75,6 +75,7 @@ typedef struct {
knot_kasp_key_t *key;
} roll_action;
__attribute__((unused))
inline static time_t time_max(time_t a, time_t b)
{
return ((a > b) ? a : b);
......@@ -85,6 +86,7 @@ inline static time_t time_min(time_t a, time_t b)
return ((a < b) ? a : b);
}
__attribute__((unused))
static time_t time_min_multi(time_t first, ...)
{
va_list args;
......@@ -121,12 +123,37 @@ static time_t zsk_remove_time(time_t retire_time, const kdnssec_ctx_t *ctx)
return retire_time + ctx->policy->propagation_delay + ctx->policy->zone_maximal_ttl;
}
static time_t ksk_publish_time(time_t active_time, const kdnssec_ctx_t *ctx)
{
if (active_time <= 0 || active_time >= TIME_INFINITY) {
return TIME_INFINITY;
}
return active_time + ctx->policy->ksk_lifetime; // TODO better minus something ?
}
static time_t ksk_ready_time(time_t publish_time, const kdnssec_ctx_t *ctx)
{
if (publish_time <= 0 || publish_time >= TIME_INFINITY) {
return TIME_INFINITY;
}
return publish_time + ctx->policy->propagation_delay + ctx->policy->dnskey_ttl;
}
static time_t ksk_remove_time(time_t retire_time, const kdnssec_ctx_t *ctx)
{
if (retire_time <= 0 || retire_time >= TIME_INFINITY) {
return TIME_INFINITY;
}
return retire_time + ctx->policy->propagation_delay + ctx->policy->dnskey_ttl; // DS TTL == DNSKEY TTL (?) TODO
}
static roll_action next_action(kdnssec_ctx_t *ctx)
{
roll_action res = { 0 };
res.time = TIME_INFINITY;
bool is_zsk_published = false;
bool is_ksk_published = false;
for (size_t i = 0; i < ctx->zone->num_keys; i++) {
knot_kasp_key_t *key = &ctx->zone->keys[i];
key_state_t keystate = get_key_state(key, ctx->now);
......@@ -134,13 +161,40 @@ static roll_action next_action(kdnssec_ctx_t *ctx)
keystate == DNSSEC_KEY_STATE_PUBLISHED)) {
is_zsk_published = true;
}
if (dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_KSK && (
keystate == DNSSEC_KEY_STATE_PUBLISHED || keystate == DNSSEC_KEY_STATE_READY)) {
is_ksk_published = true;
}
}
for (size_t i = 0; i < ctx->zone->num_keys; i++) {
knot_kasp_key_t *key = &ctx->zone->keys[i];
time_t keytime = TIME_INFINITY;
roll_action_type restype = INVALID;
if (dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_ZSK) {
bool isksk = (dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_KSK);
if (isksk) {
switch (get_key_state(key, ctx->now)) {
case DNSSEC_KEY_STATE_PUBLISHED:
keytime = ksk_ready_time(key->timing.publish, ctx);
restype = SUBMIT;
break;
case DNSSEC_KEY_STATE_READY:
break; // TODO !!!
case DNSSEC_KEY_STATE_ACTIVE:
if (!is_ksk_published) {
keytime = ksk_publish_time(key->timing.active, ctx);
restype = PUBLISH;
}
break;
case DNSSEC_KEY_STATE_RETIRED:
keytime = ksk_remove_time(key->timing.retire, ctx);
restype = REMOVE;
break;
default:
assert(0);
}
} else {
switch (get_key_state(key, ctx->now)) {
case DNSSEC_KEY_STATE_PUBLISHED:
keytime = zsk_active_time(key->timing.publish, ctx);
......@@ -163,22 +217,19 @@ static roll_action next_action(kdnssec_ctx_t *ctx)
}
if (keytime < res.time) {
res.key = key;
res.ksk = false;
res.ksk = isksk;
res.time = keytime;
res.type = restype;
}
}
printf("result %d @ %ld (now: %ld )\n",
(int)res.type, res.time, ctx->now);
return res;
}
static int exec_new_key(kdnssec_ctx_t *ctx)
static int exec_new_key(kdnssec_ctx_t *ctx, bool ksk)
{
knot_kasp_key_t *new_key = NULL;
int r = kdnssec_generate_key(ctx, false, &new_key);
int r = kdnssec_generate_key(ctx, ksk, &new_key);
if (r != KNOT_EOK) {
return r;
}
......@@ -191,18 +242,30 @@ static int exec_new_key(kdnssec_ctx_t *ctx)
return KNOT_EOK;
}
static int submit_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey) {
assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_PUBLISHED);
newkey->timing.ready = ctx->now;
return KNOT_EOK;
}
static int exec_new_signatures(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey)
{
uint16_t kskflag = dnssec_key_get_flags(newkey->key);
for (size_t i = 0; i < ctx->zone->num_keys; i++) {
knot_kasp_key_t *key = &ctx->zone->keys[i];
if (dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_ZSK &&
if (dnssec_key_get_flags(key->key) == kskflag &&
get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_ACTIVE) {
key->timing.retire = ctx->now;
}
}
assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_PUBLISHED);
newkey->timing.ready = ctx->now;
if (kskflag == DNSKEY_FLAGS_KSK) {
assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_READY);
} else {
assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_PUBLISHED);
newkey->timing.ready = ctx->now;
}
newkey->timing.active = ctx->now;
return KNOT_EOK;
......@@ -243,10 +306,12 @@ int knot_dnssec_zsk_rollover(kdnssec_ctx_t *ctx, bool *keys_changed, time_t *nex
*next_rollover = next.time;
if (!ctx->policy->singe_type_signing && *next_rollover <= ctx->now) {
assert(next.ksk == false);
switch (next.type) {
case PUBLISH:
ret = exec_new_key(ctx);
ret = exec_new_key(ctx, next.ksk);
break;
case SUBMIT:
ret = submit_key(ctx, next.key);
break;
case REPLACE:
ret = exec_new_signatures(ctx, next.key);
......@@ -272,3 +337,20 @@ int knot_dnssec_zsk_rollover(kdnssec_ctx_t *ctx, bool *keys_changed, time_t *nex
}
return (ret == KNOT_ESEMCHECK ? KNOT_EOK : ret);
}
int knot_dnssec_ksk_submittion_confirm(kdnssec_ctx_t *ctx, uint16_t for_key)
{
for (size_t i = 0; i < ctx->zone->num_keys; i++) {
knot_kasp_key_t *key = &ctx->zone->keys[i];
if (dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_KSK &&
dnssec_key_get_keytag(key->key) == for_key &&
get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY) {
int ret = exec_new_signatures(ctx, key);
if (ret == KNOT_EOK) {
ret = kdnssec_ctx_commit(ctx);
}
return ret;
}
}
return KNOT_ENOENT;
}
......@@ -38,3 +38,5 @@
* \return KNOT_E*
*/
int knot_dnssec_zsk_rollover(kdnssec_ctx_t *ctx, bool *keys_changed, time_t *next_rollover);
int knot_dnssec_ksk_submittion_confirm(kdnssec_ctx_t *ctx, uint16_t for_key);
......@@ -652,6 +652,7 @@ static bool cds_rdata_match(zone_key_t *key,
static bool is_from_keyset(zone_keyset_t *keyset,
const knot_rdata_t *record,
bool is_ds, // otherwise, it's DNSKEY
bool is_cds_cdnskey, // in this case we match only ready keys
zone_key_t **matching_key) // out, optional
{
assert(keyset);
......@@ -678,7 +679,8 @@ static bool is_from_keyset(zone_keyset_t *keyset,
keyptr_dynarray_fix(&keys);
for (size_t i = 0; i < keys.size; i++) {
if (keys.arr[i]->is_public && match_fce(keys.arr[i], &rdata)) {
bool usekey = (is_cds_cdnskey ? (keys.arr[i]->is_ready && !keys.arr[i]->is_active) : keys.arr[i]->is_public);
if (usekey && match_fce(keys.arr[i], &rdata)) {
found = true;
if (matching_key != NULL) {
*matching_key = keys.arr[i];
......@@ -791,13 +793,17 @@ static int remove_invalid_records(const knot_rrset_t *soa,
assert(changeset);
bool is_ds = false;
bool is_c = false;
switch (records->type) {
case KNOT_RRTYPE_DNSKEY:
break;
case KNOT_RRTYPE_CDNSKEY:
is_c = true;
break;
case KNOT_RRTYPE_CDS:
is_ds = true;
is_c = true;
break;
default:
return KNOT_EINVAL;
......@@ -813,7 +819,7 @@ static int remove_invalid_records(const knot_rrset_t *soa,
for (uint16_t i = 0; i < records->rrs.rr_count; i++) {
const knot_rdata_t *r = knot_rdataset_at(&records->rrs, i);
uint32_t r_ttl = knot_rdata_ttl(r);
if (r_ttl == soa_ttl && is_from_keyset(keyset, r, is_ds, NULL)) {
if (r_ttl == soa_ttl && is_from_keyset(keyset, r, is_ds, is_c, NULL)) {
continue;
}
......
......@@ -46,6 +46,7 @@
#define CMD_ZONE_RETRANSFER "zone-retransfer"
#define CMD_ZONE_FLUSH "zone-flush"
#define CMD_ZONE_SIGN "zone-sign"
#define CMD_ZONE_SUBMITTION_CONFIRM "zone-submittion-confirm"
#define CMD_ZONE_FREEZE "zone-freeze"
#define CMD_ZONE_THAW "zone-thaw"
......@@ -239,6 +240,7 @@ static void format_data(ctl_cmd_t cmd, knot_ctl_type_t data_type,
case CTL_ZONE_RETRANSFER:
case CTL_ZONE_FLUSH:
case CTL_ZONE_SIGN:
case CTL_ZONE_SUBMITTION_CONFIRM:
case CTL_ZONE_BEGIN:
case CTL_ZONE_COMMIT:
case CTL_ZONE_ABORT:
......@@ -358,6 +360,7 @@ static void format_block(ctl_cmd_t cmd, bool failed, bool empty)
case CTL_ZONE_RETRANSFER:
case CTL_ZONE_FLUSH:
case CTL_ZONE_SIGN:
case CTL_ZONE_SUBMITTION_CONFIRM:
case CTL_ZONE_FREEZE:
case CTL_ZONE_THAW:
case CTL_ZONE_BEGIN:
......@@ -823,6 +826,7 @@ static int set_node_items(cmd_args_t *args, knot_ctl_data_t *data, char *rdata,
case CTL_ZONE_READ:
case CTL_ZONE_GET: min_args = 1; max_args = 3; break;
case CTL_ZONE_STATUS: min_args = 1; max_args = 2; break;
case CTL_ZONE_SUBMITTION_CONFIRM: min_args = 2; max_args = 2; break;
case CTL_ZONE_DIFF: min_args = 1; max_args = 1; break;
case CTL_ZONE_SET: min_args = 3; max_args = -1; break;
case CTL_ZONE_UNSET: min_args = 2; max_args = -1; break;
......@@ -1058,6 +1062,7 @@ const cmd_desc_t cmd_table[] = {
{ CMD_ZONE_RETRANSFER, cmd_zone_ctl, CTL_ZONE_RETRANSFER, CMD_FOPT_ZONE },
{ CMD_ZONE_FLUSH, cmd_zone_filter_ctl, CTL_ZONE_FLUSH, CMD_FOPT_ZONE },
{ CMD_ZONE_SIGN, cmd_zone_ctl, CTL_ZONE_SIGN, CMD_FOPT_ZONE },
{ CMD_ZONE_SUBMITTION_CONFIRM, cmd_zone_node_ctl, CTL_ZONE_SUBMITTION_CONFIRM, CMD_FREQ_ZONE },
{ CMD_ZONE_FREEZE, cmd_zone_ctl, CTL_ZONE_FREEZE, CMD_FOPT_ZONE },
{ CMD_ZONE_THAW, cmd_zone_ctl, CTL_ZONE_THAW, CMD_FOPT_ZONE },
......
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