key-events.c 18.3 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 26 27 28 29 30
#include "knot/dnssec/kasp/keystate.h"
#include "knot/dnssec/key-events.h"
#include "knot/dnssec/policy.h"
#include "knot/dnssec/zone-keys.h"

static bool key_present(kdnssec_ctx_t *ctx, uint16_t flag)
{
	assert(ctx);
	assert(ctx->zone);
	for (size_t i = 0; i < ctx->zone->num_keys; i++) {
31
		knot_kasp_key_t *key = &ctx->zone->keys[i];
32 33 34 35 36 37 38
		if (dnssec_key_get_flags(key->key) == flag) {
			return true;
		}
	}
	return false;
}

Libor Peltan's avatar
Libor Peltan committed
39 40 41 42 43 44 45 46 47 48 49 50 51 52
static bool key_id_present(kdnssec_ctx_t *ctx, const char *keyid, uint16_t flag)
{
	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 &&
		    dnssec_key_get_flags(key->key) == flag) {
			return true;
		}
	}
	return false;
}

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
static bool algorithm_present(const kdnssec_ctx_t *ctx, uint8_t alg)
{
	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];
		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) {
			return true;
		}
	}
	return false;
}

68 69 70 71 72 73 74 75 76 77 78 79 80
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;
}

81
static int generate_key(kdnssec_ctx_t *ctx, bool ksk, knot_time_t when_active, bool pre_active)
82
{
83 84
	assert(!pre_active || when_active == 0);

85
	knot_kasp_key_t *key = NULL;
Daniel Salzman's avatar
Daniel Salzman committed
86 87 88
	int ret = kdnssec_generate_key(ctx, ksk, &key);
	if (ret != KNOT_EOK) {
		return ret;
89 90
	}

91 92
	key->timing.remove = 0;
	key->timing.retire = 0;
93 94 95 96
	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);
97 98 99 100

	return KNOT_EOK;
}

101
static int share_or_generate_key(kdnssec_ctx_t *ctx, bool ksk, knot_time_t when_active, bool pre_active)
Libor Peltan's avatar
Libor Peltan committed
102
{
103 104
	assert(!pre_active || when_active == 0);

Libor Peltan's avatar
Libor Peltan committed
105 106 107 108 109 110 111
	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
112 113
	int ret = kasp_db_get_policy_last(*ctx->kasp_db, ctx->policy->string,
	                                  &borrow_zone, &borrow_key);
Libor Peltan's avatar
Libor Peltan committed
114 115 116 117 118
	if (ret != KNOT_EOK && ret != KNOT_ENOENT) {
		return ret;
	}

	// if we already have the policy-last key, we have to generate new one
Daniel Salzman's avatar
Daniel Salzman committed
119
	if (ret == KNOT_ENOENT || key_id_present(ctx, borrow_key, DNSKEY_FLAGS_KSK)) {
Libor Peltan's avatar
Libor Peltan committed
120 121 122 123 124
		knot_kasp_key_t *key = NULL;
		ret = kdnssec_generate_key(ctx, ksk, &key);
		if (ret != KNOT_EOK) {
			return ret;
		}
125 126
		key->timing.remove = 0;
		key->timing.retire = 0;
127 128 129 130
		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);
Libor Peltan's avatar
Libor Peltan committed
131 132 133 134 135 136

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

Daniel Salzman's avatar
Daniel Salzman committed
137 138
		ret = kasp_db_set_policy_last(*ctx->kasp_db, ctx->policy->string,
		                              borrow_key, ctx->zone->dname, key->id);
Libor Peltan's avatar
Libor Peltan committed
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
		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
156 157
			ret = kasp_db_get_policy_last(*ctx->kasp_db, ctx->policy->string,
			                              &borrow_zone, &borrow_key);
Libor Peltan's avatar
Libor Peltan committed
158 159 160 161 162
		}
	}

	if (ret == KNOT_EOK) {
		ret = kdnssec_share_key(ctx, borrow_zone, borrow_key);
163 164 165 166 167
		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;
168
			newkey->timing.active = (ksk ? 0 : when_active);
169
		}
Libor Peltan's avatar
Libor Peltan committed
170 171 172 173 174 175
	}
	free(borrow_zone);
	free(borrow_key);
	return ret;
}

176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
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];
		bool isksk = (dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_KSK);
		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:
			ready_ksk = (ready_ksk || isksk);
			break;
		case DNSSEC_KEY_STATE_ACTIVE:
			active_ksk = (active_ksk || isksk);
			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;
}

213 214
typedef enum {
	INVALID = 0,
215
	GENERATE = 1,
216
	PUBLISH,
217
	SUBMIT,
218
	REPLACE,
219
	RETIRE,
220
	REMOVE,
221
} roll_action_type_t;
222 223

typedef struct {
224
	roll_action_type_t type;
225
	bool ksk;
226
	knot_time_t time;
227
	knot_kasp_key_t *key;
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
} 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";
	}
}
244

245
static knot_time_t zsk_rollover_time(knot_time_t active_time, const kdnssec_ctx_t *ctx)
246
{
247 248
	if (active_time <= 0) {
		return 0;
249
	}
250
	return knot_time_add(active_time, ctx->policy->zsk_lifetime);
251
}
252

253
static knot_time_t zsk_active_time(knot_time_t publish_time, const kdnssec_ctx_t *ctx)
254
{
255 256
	if (publish_time <= 0) {
		return 0;
257
	}
258
	return knot_time_add(publish_time, ctx->policy->propagation_delay + ctx->policy->dnskey_ttl);
259 260
}

261
static knot_time_t zsk_remove_time(knot_time_t retire_time, const kdnssec_ctx_t *ctx)
262
{
263 264
	if (retire_time <= 0) {
		return 0;
265
	}
266
	return knot_time_add(retire_time, ctx->policy->propagation_delay + ctx->policy->zone_maximal_ttl);
267 268
}

269
static knot_time_t ksk_rollover_time(knot_time_t created_time, const kdnssec_ctx_t *ctx)
Libor Peltan's avatar
Libor Peltan committed
270
{
271 272
	if (created_time <= 0 || ctx->policy->ksk_lifetime == 0) {
		return 0;
Libor Peltan's avatar
Libor Peltan committed
273
	}
274
	return knot_time_add(created_time, ctx->policy->ksk_lifetime);
Libor Peltan's avatar
Libor Peltan committed
275 276
}

277
static knot_time_t ksk_ready_time(knot_time_t publish_time, const kdnssec_ctx_t *ctx)
Libor Peltan's avatar
Libor Peltan committed
278
{
279 280
	if (publish_time <= 0) {
		return 0;
Libor Peltan's avatar
Libor Peltan committed
281
	}
282
	return knot_time_add(publish_time, ctx->policy->propagation_delay + ctx->policy->dnskey_ttl);
Libor Peltan's avatar
Libor Peltan committed
283 284
}

285
static knot_time_t ksk_sbm_max_time(knot_time_t ready_time, const kdnssec_ctx_t *ctx)
286
{
287 288
	if (ready_time <= 0 || ctx->policy->ksk_sbm_timeout == 0) {
		return 0;
289
	}
290
	return knot_time_add(ready_time, ctx->policy->ksk_sbm_timeout);
291 292
}

293 294 295 296 297 298 299 300
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);
}

301
static knot_time_t ksk_remove_time(knot_time_t retire_time, const kdnssec_ctx_t *ctx)
Libor Peltan's avatar
Libor Peltan committed
302
{
303 304
	if (retire_time <= 0) {
		return 0;
Libor Peltan's avatar
Libor Peltan committed
305
	}
306 307 308 309 310
	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
311 312
}

313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
// 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));
}

328
static roll_action_t next_action(kdnssec_ctx_t *ctx)
329
{
330
	roll_action_t res = { 0 };
331
	res.time = 0;
332 333 334

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

428
	return res;
429 430
}

431 432
static int submit_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey)
{
Libor Peltan's avatar
Libor Peltan committed
433
	assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_PUBLISHED);
434 435 436 437 438 439 440 441 442 443 444
	assert(dnssec_key_get_flags(newkey->key) == DNSKEY_FLAGS_KSK);

	// 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];
		if (dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_KSK &&
		    get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY) {
			key->timing.active = ctx->now;
		}
	}

Libor Peltan's avatar
Libor Peltan committed
445 446 447 448
	newkey->timing.ready = ctx->now;
	return KNOT_EOK;
}

449
static int exec_new_signatures(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey)
450
{
Libor Peltan's avatar
Libor Peltan committed
451
	uint16_t kskflag = dnssec_key_get_flags(newkey->key);
Daniel Salzman's avatar
Daniel Salzman committed
452

453 454 455 456
	if (kskflag == DNSKEY_FLAGS_KSK) {
		log_zone_notice(ctx->zone->dname, "DNSSEC, KSK submission, confirmed");
	}

457 458
	for (size_t i = 0; i < ctx->zone->num_keys; i++) {
		knot_kasp_key_t *key = &ctx->zone->keys[i];
459
		uint16_t keyflags = dnssec_key_get_flags(key->key);
460 461 462 463 464 465
		key_state_t keystate = get_key_state(key, ctx->now);
		uint8_t keyalg = dnssec_key_get_algorithm(key->key);
		if (keyflags == kskflag && keystate == DNSSEC_KEY_STATE_ACTIVE) {
			if (keyflags == DNSKEY_FLAGS_KSK ||
			    keyalg != dnssec_key_get_algorithm(newkey->key)) {
				key->timing.retire_active = ctx->now;
466
			} else {
467
				key->timing.retire = ctx->now;
468
			}
469
		}
470 471
	}

Libor Peltan's avatar
Libor Peltan committed
472 473 474 475 476
	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);
	}
477 478 479 480 481
	newkey->timing.active = knot_time_min(ctx->now, newkey->timing.active);

	return KNOT_EOK;
}

482 483 484 485 486 487 488 489
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;
}

490 491
static int exec_ksk_retire(kdnssec_ctx_t *ctx, knot_kasp_key_t *key)
{
492 493 494 495 496 497 498 499 500 501 502 503
	bool alg_rollover = false;
	knot_kasp_key_t *alg_rollover_friend = NULL;

	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 &&
		    get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_RETIRE_ACTIVE) {
			alg_rollover = true;
			alg_rollover_friend = key;
		}
	}

504
	assert(get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_RETIRE_ACTIVE);
505 506 507 508 509 510 511

	if (alg_rollover) {
		key->timing.post_active = ctx->now;
		alg_rollover_friend->timing.post_active = ctx->now;
	} else {
		key->timing.retire = ctx->now;
	}
512 513 514 515

	return KNOT_EOK;
}

516
static int exec_remove_old_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *key)
517
{
518
	assert(get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_RETIRED ||
519
	       get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_POST_ACTIVE ||
520
	       get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_REMOVED);
521
	key->timing.remove = ctx->now;
522

523
	return kdnssec_delete_key(ctx, key);
524 525
}

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

574
	roll_action_t next = next_action(ctx);
575

576
	reschedule->next_rollover = next.time;
577

578
	if (knot_time_cmp(reschedule->next_rollover, ctx->now) <= 0) {
579
		switch (next.type) {
580
		case GENERATE:
Libor Peltan's avatar
Libor Peltan committed
581
			if (next.ksk && ctx->policy->ksk_shared) {
582
				ret = share_or_generate_key(ctx, next.ksk, 0, false);
Libor Peltan's avatar
Libor Peltan committed
583
			} else {
584
				ret = generate_key(ctx, next.ksk, 0, false);
Libor Peltan's avatar
Libor Peltan committed
585
			}
586
			if (ret == KNOT_EOK) {
587 588
				log_zone_info(ctx->zone->dname, "DNSSEC, %cSK rollover started",
				              (next.ksk ? 'K' : 'Z'));
589
			}
Libor Peltan's avatar
Libor Peltan committed
590
			break;
591 592 593
		case PUBLISH:
			ret = exec_publish(ctx, next.key);
			break;
Libor Peltan's avatar
Libor Peltan committed
594 595
		case SUBMIT:
			ret = submit_key(ctx, next.key);
596
			reschedule->plan_ds_query = true;
597 598
			break;
		case REPLACE:
599
			ret = exec_new_signatures(ctx, next.key);
600
			break;
601 602 603
		case RETIRE:
			ret = exec_ksk_retire(ctx, next.key);
			break;
604
		case REMOVE:
605
			ret = exec_remove_old_key(ctx, next.key);
606 607 608 609 610 611
			break;
		default:
			ret = KNOT_EINVAL;
		}

		if (ret == KNOT_EOK) {
612
			reschedule->keys_changed = true;
613
			next = next_action(ctx);
614
			reschedule->next_rollover = next.time;
615
		} else {
616 617 618 619
			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);
620 621 622
		}
	}

623 624 625 626
	if (ret == KNOT_EOK && knot_time_cmp(reschedule->next_rollover, ctx->now) <= 0) {
		return knot_dnssec_key_rollover(ctx, reschedule);
	}

627
	if (reschedule->keys_changed) {
628 629
		ret = kdnssec_ctx_commit(ctx);
	}
Libor Peltan's avatar
Libor Peltan committed
630
	return ret;
631
}
Libor Peltan's avatar
Libor Peltan committed
632

633
int knot_dnssec_ksk_sbm_confirm(kdnssec_ctx_t *ctx)
Libor Peltan's avatar
Libor Peltan committed
634 635 636 637 638 639 640 641 642 643 644 645 646 647
{
	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 &&
		    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;
}
648

649
bool zone_has_key_sbm(const kdnssec_ctx_t *ctx)
650 651 652 653 654 655
{
	assert(ctx->zone);

	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 &&
656 657
		    (get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY ||
		     get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_ACTIVE)) {
658 659 660 661 662
			return true;
		}
	}
	return false;
}