key-events.c 19 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*  Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <assert.h>

19
#include "contrib/macros.h"
20
#include "knot/common/log.h"
21 22 23 24 25
#include "knot/dnssec/kasp/keystate.h"
#include "knot/dnssec/key-events.h"
#include "knot/dnssec/policy.h"
#include "knot/dnssec/zone-keys.h"

26
static bool key_present(const kdnssec_ctx_t *ctx, bool ksk, bool zsk)
27 28 29 30
{
	assert(ctx);
	assert(ctx->zone);
	for (size_t i = 0; i < ctx->zone->num_keys; i++) {
31 32
		const knot_kasp_key_t *key = &ctx->zone->keys[i];
		if (key->is_ksk == ksk && key->is_zsk == zsk) {
33 34 35 36 37 38
			return true;
		}
	}
	return false;
}

39
static bool key_id_present(const kdnssec_ctx_t *ctx, const char *keyid, uint16_t flag)
40 41 42 43
{
	assert(ctx);
	assert(ctx->zone);
	for (size_t i = 0; i < ctx->zone->num_keys; i++) {
44
		const knot_kasp_key_t *key = &ctx->zone->keys[i];
45 46 47 48 49 50 51 52
		if (strcmp(keyid, key->id) == 0 &&
		    dnssec_key_get_flags(key->key) == flag) {
			return true;
		}
	}
	return false;
}

53
static unsigned algorithm_present(const kdnssec_ctx_t *ctx, uint8_t alg)
54 55 56
{
	assert(ctx);
	assert(ctx->zone);
57
	unsigned ret = 0;
58
	for (size_t i = 0; i < ctx->zone->num_keys; i++) {
59
		const knot_kasp_key_t *key = &ctx->zone->keys[i];
60 61 62
		knot_time_t activated = knot_time_min(key->timing.pre_active, key->timing.ready);
		if (knot_time_cmp(knot_time_min(activated, key->timing.active), ctx->now) <= 0 &&
		    dnssec_key_get_algorithm(key->key) == alg) {
63
			ret++;
64 65
		}
	}
66 67 68 69 70 71 72 73 74 75
	return ret;
}

static bool signing_scheme_present(const kdnssec_ctx_t *ctx)
{
	if (ctx->policy->singe_type_signing) {
		return key_present(ctx, true, true);
	} else {
		return (key_present(ctx, true, false) && key_present(ctx, false, true));
	}
76 77
}

78 79 80 81 82 83 84 85 86 87 88 89 90
static knot_kasp_key_t *key_get_by_id(kdnssec_ctx_t *ctx, const char *keyid)
{
	assert(ctx);
	assert(ctx->zone);
	for (size_t i = 0; i < ctx->zone->num_keys; i++) {
		knot_kasp_key_t *key = &ctx->zone->keys[i];
		if (strcmp(keyid, key->id) == 0) {
			return key;
		}
	}
	return NULL;
}

91
static int generate_key(kdnssec_ctx_t *ctx, bool ksk, bool zsk, knot_time_t when_active, bool pre_active)
92
{
93 94
	assert(!pre_active || when_active == 0);

95
	knot_kasp_key_t *key = NULL;
96
	int ret = kdnssec_generate_key(ctx, ksk, zsk, &key);
97 98
	if (ret != KNOT_EOK) {
		return ret;
99 100
	}

101 102
	key->timing.remove = 0;
	key->timing.retire = 0;
103 104 105 106
	key->timing.active = (ksk ? 0 : when_active);
	key->timing.ready  = (ksk ? when_active : 0);
	key->timing.publish    = (pre_active ? 0 : ctx->now);
	key->timing.pre_active = (pre_active ? ctx->now : 0);
107 108 109 110

	return KNOT_EOK;
}

111
static int share_or_generate_key(kdnssec_ctx_t *ctx, bool ksk, bool zsk, knot_time_t when_active, bool pre_active)
112
{
113 114
	assert(!pre_active || when_active == 0);

115 116 117 118 119 120 121
	knot_dname_t *borrow_zone = NULL;
	char *borrow_key = NULL;

	if (!ksk) {
		return KNOT_EINVAL;
	} // for now not designed for rotating shared ZSK

Daniel Salzman's avatar
Daniel Salzman committed
122 123
	int ret = kasp_db_get_policy_last(*ctx->kasp_db, ctx->policy->string,
	                                  &borrow_zone, &borrow_key);
124 125 126 127 128
	if (ret != KNOT_EOK && ret != KNOT_ENOENT) {
		return ret;
	}

	// if we already have the policy-last key, we have to generate new one
129
	if (ret == KNOT_ENOENT || key_id_present(ctx, borrow_key, DNSKEY_FLAGS_KSK)) {
130
		knot_kasp_key_t *key = NULL;
131
		ret = kdnssec_generate_key(ctx, ksk, zsk, &key);
132 133 134
		if (ret != KNOT_EOK) {
			return ret;
		}
135 136
		key->timing.remove = 0;
		key->timing.retire = 0;
137 138 139 140
		key->timing.active = (ksk ? 0 : when_active);
		key->timing.ready  = (ksk ? when_active : 0);
		key->timing.publish    = (pre_active ? 0 : ctx->now);
		key->timing.pre_active = (pre_active ? ctx->now : 0);
141 142 143 144 145 146

		ret = kdnssec_ctx_commit(ctx);
		if (ret != KNOT_EOK) {
			return ret;
		}

Daniel Salzman's avatar
Daniel Salzman committed
147 148
		ret = kasp_db_set_policy_last(*ctx->kasp_db, ctx->policy->string,
		                              borrow_key, ctx->zone->dname, key->id);
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
		free(borrow_zone);
		free(borrow_key);
		borrow_zone = NULL;
		borrow_key = NULL;
		if (ret != KNOT_ESEMCHECK) {
			// all ok, we generated new kay and updated policy-last
			return ret;
		} else {
			// another zone updated policy-last key in the meantime
			ret = kdnssec_delete_key(ctx, key);
			if (ret == KNOT_EOK) {
				ret = kdnssec_ctx_commit(ctx);
			}
			if (ret != KNOT_EOK) {
				return ret;
			}

Daniel Salzman's avatar
Daniel Salzman committed
166 167
			ret = kasp_db_get_policy_last(*ctx->kasp_db, ctx->policy->string,
			                              &borrow_zone, &borrow_key);
168 169 170 171 172
		}
	}

	if (ret == KNOT_EOK) {
		ret = kdnssec_share_key(ctx, borrow_zone, borrow_key);
173 174 175 176 177
		if (ret == KNOT_EOK) {
			knot_kasp_key_t *newkey = key_get_by_id(ctx, borrow_key);
			assert(newkey != NULL);
			newkey->timing.publish = ctx->now;
			newkey->timing.ready = when_active;
178
			newkey->timing.active = (ksk ? 0 : when_active);
179
		}
180 181 182 183 184 185
	}
	free(borrow_zone);
	free(borrow_key);
	return ret;
}

186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
static bool running_rollover(const kdnssec_ctx_t *ctx)
{
	bool res = false;
	bool ready_ksk = false, active_ksk = false;

	for (size_t i = 0; i < ctx->zone->num_keys; i++) {
		knot_kasp_key_t *key = &ctx->zone->keys[i];
		switch (get_key_state(key, ctx->now)) {
		case DNSSEC_KEY_STATE_PRE_ACTIVE:
			res = true;
			break;
		case DNSSEC_KEY_STATE_PUBLISHED:
			res = (res || !key->is_pub_only);
			break;
		case DNSSEC_KEY_STATE_READY:
201
			ready_ksk = (ready_ksk || key->is_ksk);
202 203
			break;
		case DNSSEC_KEY_STATE_ACTIVE:
204
			active_ksk = (active_ksk || key->is_ksk);
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
			break;
		case DNSSEC_KEY_STATE_RETIRE_ACTIVE:
		case DNSSEC_KEY_STATE_POST_ACTIVE:
			res = true;
			break;
		case DNSSEC_KEY_STATE_RETIRED:
		case DNSSEC_KEY_STATE_REMOVED:
		default:
			break;
		}
	}
	if (ready_ksk && active_ksk) {
		res = true;
	}
	return res;
}

222 223
typedef enum {
	INVALID = 0,
224
	GENERATE = 1,
225
	PUBLISH,
226
	SUBMIT,
227
	REPLACE,
228
	RETIRE,
229
	REMOVE,
230
} roll_action_type_t;
231 232

typedef struct {
233
	roll_action_type_t type;
234
	bool ksk;
235
	knot_time_t time;
236
	knot_kasp_key_t *key;
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
} roll_action_t;

static const char *roll_action_name(roll_action_type_t type)
{
	switch (type) {
	case GENERATE: return "generate";
	case PUBLISH:  return "publish";
	case SUBMIT:   return "submit";
	case REPLACE:  return "replace";
	case RETIRE:   return "retire";
	case REMOVE:   return "remove";
	case INVALID:
		// FALLTHROUGH
	default:       return "invalid";
	}
}
253

254
static knot_time_t zsk_rollover_time(knot_time_t active_time, const kdnssec_ctx_t *ctx)
255
{
256 257
	if (active_time <= 0) {
		return 0;
258
	}
259
	return knot_time_add(active_time, ctx->policy->zsk_lifetime);
260
}
261

262
static knot_time_t zsk_active_time(knot_time_t publish_time, const kdnssec_ctx_t *ctx)
263
{
264 265
	if (publish_time <= 0) {
		return 0;
266
	}
267
	return knot_time_add(publish_time, ctx->policy->propagation_delay + ctx->policy->dnskey_ttl);
268 269
}

270
static knot_time_t zsk_remove_time(knot_time_t retire_time, const kdnssec_ctx_t *ctx)
271
{
272 273
	if (retire_time <= 0) {
		return 0;
274
	}
275
	return knot_time_add(retire_time, ctx->policy->propagation_delay + ctx->policy->zone_maximal_ttl);
276 277
}

278
static knot_time_t ksk_rollover_time(knot_time_t created_time, const kdnssec_ctx_t *ctx)
Libor Peltan's avatar
Libor Peltan committed
279
{
280 281
	if (created_time <= 0 || ctx->policy->ksk_lifetime == 0) {
		return 0;
Libor Peltan's avatar
Libor Peltan committed
282
	}
283
	return knot_time_add(created_time, ctx->policy->ksk_lifetime);
Libor Peltan's avatar
Libor Peltan committed
284 285
}

286
static knot_time_t ksk_ready_time(knot_time_t publish_time, const kdnssec_ctx_t *ctx)
Libor Peltan's avatar
Libor Peltan committed
287
{
288 289
	if (publish_time <= 0) {
		return 0;
Libor Peltan's avatar
Libor Peltan committed
290
	}
291
	return knot_time_add(publish_time, ctx->policy->propagation_delay + ctx->policy->dnskey_ttl);
Libor Peltan's avatar
Libor Peltan committed
292 293
}

294
static knot_time_t ksk_sbm_max_time(knot_time_t ready_time, const kdnssec_ctx_t *ctx)
295
{
296 297
	if (ready_time <= 0 || ctx->policy->ksk_sbm_timeout == 0) {
		return 0;
298
	}
299
	return knot_time_add(ready_time, ctx->policy->ksk_sbm_timeout);
300 301
}

302 303 304 305 306 307 308 309
static knot_time_t ksk_retire_time(knot_time_t retire_active_time, const kdnssec_ctx_t *ctx)
{
	if (retire_active_time <= 0) {
		return 0;
	}
	return knot_time_add(retire_active_time, ctx->policy->propagation_delay + ctx->policy->dnskey_ttl);
}

310
static knot_time_t ksk_remove_time(knot_time_t retire_time, const kdnssec_ctx_t *ctx)
Libor Peltan's avatar
Libor Peltan committed
311
{
312 313
	if (retire_time <= 0) {
		return 0;
Libor Peltan's avatar
Libor Peltan committed
314
	}
315 316 317 318 319
	knot_timediff_t use_ttl = ctx->policy->dnskey_ttl;
	if (ctx->policy->singe_type_signing && ctx->policy->zone_maximal_ttl > use_ttl) {
		use_ttl = ctx->policy->zone_maximal_ttl;
	}
	return knot_time_add(retire_time, ctx->policy->propagation_delay + use_ttl);
Libor Peltan's avatar
Libor Peltan committed
320 321
}

322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
// algorithm rollover related timers must be the same for KSK and ZSK

static knot_time_t alg_publish_time(knot_time_t pre_active_time, const kdnssec_ctx_t *ctx)
{
	if (pre_active_time <= 0) {
		return 0;
	}
	return knot_time_add(pre_active_time, ctx->policy->propagation_delay + ctx->policy->zone_maximal_ttl);
}

static knot_time_t alg_remove_time(knot_time_t post_active_time, const kdnssec_ctx_t *ctx)
{
	return MAX(ksk_remove_time(post_active_time, ctx), zsk_remove_time(post_active_time, ctx));
}

337
static roll_action_t next_action(kdnssec_ctx_t *ctx)
338
{
339
	roll_action_t res = { 0 };
340
	res.time = 0;
341 342 343

	for (size_t i = 0; i < ctx->zone->num_keys; i++) {
		knot_kasp_key_t *key = &ctx->zone->keys[i];
344
		knot_time_t keytime = 0;
345
		roll_action_type_t restype = INVALID;
346 347 348
		if (key->is_pub_only) {
			continue;
		}
349
		if (key->is_ksk) {
Libor Peltan's avatar
Libor Peltan committed
350
			switch (get_key_state(key, ctx->now)) {
351 352 353 354
			case DNSSEC_KEY_STATE_PRE_ACTIVE:
				keytime = alg_publish_time(key->timing.pre_active, ctx);
				restype = PUBLISH;
				break;
Libor Peltan's avatar
Libor Peltan committed
355 356 357 358 359
			case DNSSEC_KEY_STATE_PUBLISHED:
				keytime = ksk_ready_time(key->timing.publish, ctx);
				restype = SUBMIT;
				break;
			case DNSSEC_KEY_STATE_READY:
360
				keytime = ksk_sbm_max_time(key->timing.ready, ctx);
361
				restype = REPLACE;
362
				break;
Libor Peltan's avatar
Libor Peltan committed
363
			case DNSSEC_KEY_STATE_ACTIVE:
364 365
				if (!running_rollover(ctx) &&
				    dnssec_key_get_algorithm(key->key) == ctx->policy->algorithm) {
366 367
					keytime = ksk_rollover_time(key->timing.created, ctx);
					restype = GENERATE;
Libor Peltan's avatar
Libor Peltan committed
368 369
				}
				break;
370 371 372 373
			case DNSSEC_KEY_STATE_RETIRE_ACTIVE:
				keytime = ksk_retire_time(key->timing.retire_active, ctx);
				restype = RETIRE;
				break;
374 375 376 377
			case DNSSEC_KEY_STATE_POST_ACTIVE:
				keytime = alg_remove_time(key->timing.post_active, ctx);
				restype = REMOVE;
				break;
Libor Peltan's avatar
Libor Peltan committed
378
			case DNSSEC_KEY_STATE_RETIRED:
379
			case DNSSEC_KEY_STATE_REMOVED:
380 381
				// ad REMOVED state: normally this wouldn't happen
				// (key in removed state is instantly deleted)
382
				// but if imported keys, they can be in this state
383 384
				keytime = knot_time_min(key->timing.retire, key->timing.remove);
				keytime = ksk_remove_time(keytime, ctx);
Libor Peltan's avatar
Libor Peltan committed
385 386 387
				restype = REMOVE;
				break;
			default:
388
				continue;
Libor Peltan's avatar
Libor Peltan committed
389 390
			}
		} else {
391
			switch (get_key_state(key, ctx->now)) {
392 393 394 395
			case DNSSEC_KEY_STATE_PRE_ACTIVE:
				keytime = alg_publish_time(key->timing.pre_active, ctx);
				restype = PUBLISH;
				break;
396 397 398 399 400
			case DNSSEC_KEY_STATE_PUBLISHED:
				keytime = zsk_active_time(key->timing.publish, ctx);
				restype = REPLACE;
				break;
			case DNSSEC_KEY_STATE_ACTIVE:
401 402
				if (!running_rollover(ctx) &&
				    dnssec_key_get_algorithm(key->key) == ctx->policy->algorithm) {
403 404
					keytime = zsk_rollover_time(key->timing.active, ctx);
					restype = GENERATE;
405 406
				}
				break;
407 408 409 410 411 412 413
			case DNSSEC_KEY_STATE_RETIRE_ACTIVE:
				// simply waiting for submitted KSK to retire me.
				break;
			case DNSSEC_KEY_STATE_POST_ACTIVE:
				keytime = alg_remove_time(key->timing.post_active, ctx);
				restype = REMOVE;
				break;
414
			case DNSSEC_KEY_STATE_RETIRED:
415
			case DNSSEC_KEY_STATE_REMOVED:
416 417
				// ad REMOVED state: normally this wouldn't happen
				// (key in removed state is instantly deleted)
418
				// but if imported keys, they can be in this state
419 420
				keytime = knot_time_min(key->timing.retire, key->timing.remove);
				keytime = ksk_remove_time(keytime, ctx);;
421 422 423 424
				restype = REMOVE;
				break;
			case DNSSEC_KEY_STATE_READY:
			default:
425
				continue;
426 427
			}
		}
428
		if (knot_time_cmp(keytime, res.time) < 0) {
429
			res.key = key;
430
			res.ksk = key->is_ksk;
431 432
			res.time = keytime;
			res.type = restype;
433 434 435
		}
	}

436
	return res;
437 438
}

439 440
static int submit_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey)
{
Libor Peltan's avatar
Libor Peltan committed
441
	assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_PUBLISHED);
442
	assert(newkey->is_ksk);
443 444 445 446

	// pushing from READY into ACTIVE decreases the other key's cds_priority
	for (size_t i = 0; i < ctx->zone->num_keys; i++) {
		knot_kasp_key_t *key = &ctx->zone->keys[i];
447
		if (key->is_ksk && get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY) {
448 449 450 451
			key->timing.active = ctx->now;
		}
	}

Libor Peltan's avatar
Libor Peltan committed
452 453 454 455
	newkey->timing.ready = ctx->now;
	return KNOT_EOK;
}

456
static int exec_new_signatures(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey)
457
{
458
	if (newkey->is_ksk) {
459 460 461
		log_zone_notice(ctx->zone->dname, "DNSSEC, KSK submission, confirmed");
	}

462 463
	for (size_t i = 0; i < ctx->zone->num_keys; i++) {
		knot_kasp_key_t *key = &ctx->zone->keys[i];
464 465
		key_state_t keystate = get_key_state(key, ctx->now);
		uint8_t keyalg = dnssec_key_get_algorithm(key->key);
466 467 468
		if (((newkey->is_ksk && key->is_ksk) || (newkey->is_zsk && key->is_zsk))
		    && keystate == DNSSEC_KEY_STATE_ACTIVE) {
			if (key->is_ksk || keyalg != dnssec_key_get_algorithm(newkey->key)) {
469
				key->timing.retire_active = ctx->now;
470
			} else {
471
				key->timing.retire = ctx->now;
472
			}
473
		}
474 475
	}

476
	if (newkey->is_ksk) {
Libor Peltan's avatar
Libor Peltan committed
477 478 479 480
		assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_READY);
	} else {
		assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_PUBLISHED);
	}
481 482 483 484 485
	newkey->timing.active = knot_time_min(ctx->now, newkey->timing.active);

	return KNOT_EOK;
}

486 487 488 489 490 491 492 493
static int exec_publish(kdnssec_ctx_t *ctx, knot_kasp_key_t *key)
{
	assert(get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_PRE_ACTIVE);
	key->timing.publish = ctx->now;

	return KNOT_EOK;
}

494 495
static int exec_ksk_retire(kdnssec_ctx_t *ctx, knot_kasp_key_t *key)
{
496 497 498 499
	bool alg_rollover = false;
	knot_kasp_key_t *alg_rollover_friend = NULL;

	for (size_t i = 0; i < ctx->zone->num_keys; i++) {
500 501 502
		knot_kasp_key_t *k = &ctx->zone->keys[i];
		if (k->is_zsk && get_key_state(k, ctx->now) == DNSSEC_KEY_STATE_RETIRE_ACTIVE &&
		    algorithm_present(ctx, dnssec_key_get_algorithm(k->key)) < 3) {
503
			alg_rollover = true;
504
			alg_rollover_friend = k;
505 506 507
		}
	}

508
	assert(get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_RETIRE_ACTIVE);
509 510 511 512 513 514 515

	if (alg_rollover) {
		key->timing.post_active = ctx->now;
		alg_rollover_friend->timing.post_active = ctx->now;
	} else {
		key->timing.retire = ctx->now;
	}
516 517 518 519

	return KNOT_EOK;
}

520
static int exec_remove_old_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *key)
521
{
522
	assert(get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_RETIRED ||
523
	       get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_POST_ACTIVE ||
524
	       get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_REMOVED);
525
	key->timing.remove = ctx->now;
526

527
	return kdnssec_delete_key(ctx, key);
528 529
}

530
int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_reschedule_t *reschedule)
531
{
532 533 534
	if (ctx == NULL || reschedule == NULL) {
		return KNOT_EINVAL;
	}
535 536 537
	if (ctx->policy->manual) {
		return KNOT_EOK;
	}
Libor Peltan's avatar
Libor Peltan committed
538
	int ret = KNOT_EOK;
539
	// generate initial keys if missing
540
	if (!key_present(ctx, true, false) && !key_present(ctx, true, true)) {
541
		if (ctx->policy->ksk_shared) {
542
			ret = share_or_generate_key(ctx, true, ctx->policy->singe_type_signing, ctx->now, false);
543
		} else {
544
			ret = generate_key(ctx, true, ctx->policy->singe_type_signing, ctx->now, false);
545
		}
546
		reschedule->plan_ds_query = true;
547 548
		if (ret == KNOT_EOK) {
			reschedule->keys_changed = true;
549
			if (!ctx->policy->singe_type_signing &&
550
			    !key_present(ctx, false, true)) {
551 552
				ret = generate_key(ctx, false, true, ctx->now, false);
			}
553 554 555
		}
	}
	// algorithm rollover
556
	if (algorithm_present(ctx, ctx->policy->algorithm) == 0 &&
557
	    !running_rollover(ctx) && ret == KNOT_EOK) {
558
		if (ctx->policy->ksk_shared) {
559
			ret = share_or_generate_key(ctx, true, ctx->policy->singe_type_signing, 0, true);
560
		} else {
561
			ret = generate_key(ctx, true, ctx->policy->singe_type_signing, 0, true);
562 563
		}
		if (!ctx->policy->singe_type_signing && ret == KNOT_EOK) {
564
			ret = generate_key(ctx, false, true, 0, true);
565 566
		}
		log_zone_info(ctx->zone->dname, "DNSSEC, algorithm rollover started");
567 568 569
		if (ret == KNOT_EOK) {
			reschedule->keys_changed = true;
		}
570
	}
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
	// scheme rollover
	if (!signing_scheme_present(ctx) &&
	    !running_rollover(ctx) && ret == KNOT_EOK) {
		if (ctx->policy->ksk_shared) {
			ret = share_or_generate_key(ctx, true, ctx->policy->singe_type_signing, 0, false);
		} else {
			ret = generate_key(ctx, true, ctx->policy->singe_type_signing, 0, false);
		}
		if (!ctx->policy->singe_type_signing && ret == KNOT_EOK) {
			ret = generate_key(ctx, false, true, 0, false);
		}
		log_zone_info(ctx->zone->dname, "DNSSEC, signing scheme rollover started");
		if (ret == KNOT_EOK) {
			reschedule->keys_changed = true;
		}
	}
Libor Peltan's avatar
Libor Peltan committed
587
	if (ret != KNOT_EOK) {
588 589 590
		return ret;
	}

591
	roll_action_t next = next_action(ctx);
592

593
	reschedule->next_rollover = next.time;
594

595
	if (knot_time_cmp(reschedule->next_rollover, ctx->now) <= 0) {
596
		switch (next.type) {
597
		case GENERATE:
598
			if (next.ksk && ctx->policy->ksk_shared) {
599
				ret = share_or_generate_key(ctx, true, ctx->policy->singe_type_signing, 0, false);
600
			} else {
601
				ret = generate_key(ctx, next.ksk, ctx->policy->singe_type_signing || !next.ksk, 0, false);
602
			}
603
			if (ret == KNOT_EOK) {
604 605
				log_zone_info(ctx->zone->dname, "DNSSEC, %cSK rollover started",
				              (next.ksk ? 'K' : 'Z'));
606
			}
Libor Peltan's avatar
Libor Peltan committed
607
			break;
608 609 610
		case PUBLISH:
			ret = exec_publish(ctx, next.key);
			break;
Libor Peltan's avatar
Libor Peltan committed
611 612
		case SUBMIT:
			ret = submit_key(ctx, next.key);
613
			reschedule->plan_ds_query = true;
614 615
			break;
		case REPLACE:
616
			ret = exec_new_signatures(ctx, next.key);
617
			break;
618 619 620
		case RETIRE:
			ret = exec_ksk_retire(ctx, next.key);
			break;
621
		case REMOVE:
622
			ret = exec_remove_old_key(ctx, next.key);
623 624 625 626 627 628
			break;
		default:
			ret = KNOT_EINVAL;
		}

		if (ret == KNOT_EOK) {
629
			reschedule->keys_changed = true;
630
			next = next_action(ctx);
631
			reschedule->next_rollover = next.time;
632
		} else {
633 634 635 636
			log_zone_warning(ctx->zone->dname, "DNSSEC, key rollover, action %s (%s)",
			                 roll_action_name(next.type), knot_strerror(ret));
			// fail => try in 10 seconds #TODO better?
			reschedule->next_rollover = knot_time_add(knot_time(), 10);
637 638 639
		}
	}

640 641 642 643
	if (ret == KNOT_EOK && knot_time_cmp(reschedule->next_rollover, ctx->now) <= 0) {
		return knot_dnssec_key_rollover(ctx, reschedule);
	}

644
	if (reschedule->keys_changed) {
645 646
		ret = kdnssec_ctx_commit(ctx);
	}
Libor Peltan's avatar
Libor Peltan committed
647
	return ret;
648
}
Libor Peltan's avatar
Libor Peltan committed
649

650
int knot_dnssec_ksk_sbm_confirm(kdnssec_ctx_t *ctx)
Libor Peltan's avatar
Libor Peltan committed
651 652 653
{
	for (size_t i = 0; i < ctx->zone->num_keys; i++) {
		knot_kasp_key_t *key = &ctx->zone->keys[i];
654
		if (key->is_ksk && get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY) {
Libor Peltan's avatar
Libor Peltan committed
655 656 657 658 659 660 661 662 663
			int ret = exec_new_signatures(ctx, key);
			if (ret == KNOT_EOK) {
				ret = kdnssec_ctx_commit(ctx);
			}
			return ret;
		}
	}
	return KNOT_ENOENT;
}
664

665
bool zone_has_key_sbm(const kdnssec_ctx_t *ctx)
666 667 668 669 670
{
	assert(ctx->zone);

	for (size_t i = 0; i < ctx->zone->num_keys; i++) {
		knot_kasp_key_t *key = &ctx->zone->keys[i];
671
		if (key->is_ksk &&
672 673
		    (get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY ||
		     get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_ACTIVE)) {
674 675 676 677 678
			return true;
		}
	}
	return false;
}