nsec3\-salt\-lifetime: TIME
ksk\-submission: submission_id
cds\-cdnskey\-publish: none | delete\-dnssec | rollover | always
+ offline\-ksk: BOOL
.ft P
.fi
.UNINDENT
.UNINDENT
.sp
\fIDefault:\fP always
+.SS offline\-ksk
+.sp
+Specifies if Offline KSK feature is enabled.
+.sp
+\fIDefault:\fP off
.SH REMOTE SECTION
.sp
Definitions of remote servers for outgoing connections (source of a zone
For the "ZSK side" (i.e. the operator of the DNS server), the pre-requirements are
similar to pervious case: proper DNSSEC configuration with :ref:`zsk-lifetime <policy_zsk-lifetime>`
-but :ref:`manual <policy_manual>`, a complete KASP db with the public part of the KSK
-(or multiple KSKs if in the middle of a rollover), but without its private part.
+but :ref:`manual <policy_manual>`, a complete KASP db with just ZSKs. Also
+:ref:`offline-ksk <policy_offline-ksk>` must be enabled.
For the "KSK side" (i.e. the operator of the KSK signer), the pre-requirements are
equal Knot configuration (at least the :ref:`Policy section` must be identical) and a KASP db
-with the KSK(s). The timers for the KSKs must be identical on both sides! The ZSKs are not
-necessary here, but their public parts might be in the KASP db too.
+with the KSK(s).
The first step for the "ZSK side" is the same as previously: ``keymgr pregenerate``. Then,
the operator shall export the public parts of the future ZSKs (in facts in the form of
-complete future DNSKEY records) by calling ``keymgr generate-ksr``. The output
+future DNSKEY records) by calling ``keymgr generate-ksr``. The output
(called Key Signing Request; perhaps redirected to a file) shall be then sent to the
"KSK side" e.g. via e-mail.
The third step is done by "KSK side" by using the command ``keymgr sign-ksr`` with
-the KSR file as the parameter. This signs all the future forms of DNSKEY record,
-creating the future RRSIGs, which are again printed on output (and called Signed Key
-Response). This shall be sent back to "ZSK side".
+the KSR file as the parameter. This completes and signs all the future forms of DNSKEY record,
+creating the future RRSIGs (adding also CDNSKEYs and CDSs), which are again printed on output
+(and called Signed Key Response). This shall be sent back to "ZSK side".
The last step is importing the signatures from SKR to the KASP db for later use,
this is done by ``keymgr import-skr``, followed by (as previously) ``knotc zone-resign``
nsec3-salt-lifetime: TIME
ksk-submission: submission_id
cds-cdnskey-publish: none | delete-dnssec | rollover | always
+ offline-ksk: BOOL
.. _policy_id:
*Default:* always
+.. _policy_offline-ksk:
+
+offline-ksk
+-----------
+
+Specifies if Offline KSK feature is enabled.
+
+*Default:* off
+
.. _Remote section:
Remote section
{ C_KSK_SBM, YP_TREF, YP_VREF = { C_SBM }, CONF_IO_FRLD_ZONES,
{ check_ref } },
{ C_CHILD_RECORDS, YP_TOPT, YP_VOPT = { child_record, CHILD_RECORDS_ALWAYS } },
+ { C_OFFLINE_KSK, YP_TBOOL, YP_VNONE, CONF_IO_FRLD_ZONES },
{ C_COMMENT, YP_TSTR, YP_VNONE },
{ NULL }
};
#define C_NSEC3_SALT_LEN "\x11""nsec3-salt-length"
#define C_NSEC3_SALT_LIFETIME "\x13""nsec3-salt-lifetime"
#define C_NSID "\x04""nsid"
+#define C_OFFLINE_KSK "\x0B""offline-ksk"
#define C_PARENT "\x06""parent"
#define C_PIDFILE "\x07""pidfile"
#define C_POLICY "\x06""policy"
conf_val_next(&val);
}
}
+
+ val = conf_id_get(conf(), C_POLICY, C_OFFLINE_KSK, id);
+ policy->offline_ksk = conf_bool(&val);
}
int kdnssec_ctx_init(conf_t *conf, kdnssec_ctx_t *ctx, const knot_dname_t *zone_name,
}
free(ctx->policy);
}
+ knot_rrset_free(ctx->offline_rrsig, NULL);
dnssec_keystore_deinit(ctx->keystore);
kasp_zone_free(&ctx->zone);
free(ctx->kasp_zone_path);
bool rrsig_drop_existing;
bool keep_deleted_keys;
bool rollover_only_zsk;
+
+ knot_rrset_t *offline_rrsig;
} kdnssec_ctx_t;
/*!
snprintf(str, 21, "%020lu", t);
}
-int kasp_db_store_offline_rrsig(kasp_db_t *db, knot_time_t for_time, const knot_rrset_t *rrsig)
+int kasp_db_store_offline_rrsig(kasp_db_t *db, knot_time_t for_time, const knot_rrset_t *rrsig, const knot_rrset_t *dnskey,
+ const knot_rrset_t *cdnskey, const knot_rrset_t *cds)
{
- if (db == NULL || rrsig == NULL || rrsig->type != KNOT_RRTYPE_RRSIG) {
+ if (db == NULL || rrsig == NULL || rrsig->type != KNOT_RRTYPE_RRSIG || dnskey == NULL || dnskey->type != KNOT_RRTYPE_DNSKEY ||
+ cdnskey == NULL || cdnskey->type != KNOT_RRTYPE_CDNSKEY || cds == NULL || cds->type != KNOT_RRTYPE_CDS) {
return KNOT_EINVAL;
}
char for_time_str[21];
for_time2string(for_time_str, for_time);
knot_db_val_t key = make_key(KASPDBKEY_OFFLINE_RRSIG, rrsig->owner, for_time_str), val;
- val.len = rrset_serialized_size(rrsig);
+ val.len = rrset_serialized_size(rrsig) + rrset_serialized_size(dnskey) +
+ rrset_serialized_size(cdnskey) + rrset_serialized_size(cds);
val.data = malloc(val.len);
if (val.data == NULL) {
free_key(&key);
with_txn(KEYS_RW, NULL);
wire_ctx_t wire = wire_ctx_init(val.data, val.len);
ret = serialize_rrset(&wire, rrsig);
+ if (ret == KNOT_EOK) {
+ ret = serialize_rrset(&wire, dnskey);
+ }
+ if (ret == KNOT_EOK) {
+ ret = serialize_rrset(&wire, cdnskey);
+ }
+ if (ret == KNOT_EOK) {
+ ret = serialize_rrset(&wire, cds);
+ }
if (ret == KNOT_EOK) {
ret = db_api->insert(txn, &key, &val, 0);
}
return ret;
}
-int kasp_db_load_offline_rrsig(kasp_db_t *db, const knot_dname_t *for_dname, knot_time_t for_time, knot_rrset_t *rrsig)
+int kasp_db_load_offline_rrsig(kasp_db_t *db, const knot_dname_t *for_dname, knot_time_t for_time, knot_time_t *next_time,
+ knot_rrset_t *rrsig, knot_rrset_t *dnskey, knot_rrset_t *cdnskey, knot_rrset_t *cds)
{
- if (db == NULL || rrsig == NULL) {
+ if (db == NULL || rrsig == NULL || dnskey == NULL || cdnskey == NULL || cds == NULL) {
return KNOT_EINVAL;
}
char for_time_str[21];
for_time2string(for_time_str, for_time);
with_txn(KEYS_RO, NULL);
- knot_db_val_t key = make_key(KASPDBKEY_OFFLINE_RRSIG, for_dname, for_time_str), val;
- ret = db_api->find(txn, &key, &val, KNOT_DB_LEQ);
- if (ret == KNOT_EOK) {
- wire_ctx_t wire = wire_ctx_init(val.data, val.len);
- ret = deserialize_rrset(&wire, rrsig);
- if (ret == KNOT_EOK && knot_dname_cmp(rrsig->owner, for_dname) != 0) {
- ret = KNOT_ENOENT;
+ knot_db_val_t search = make_key(KASPDBKEY_OFFLINE_RRSIG, for_dname, for_time_str), key, val;
+ knot_db_iter_t *it = db_api->iter_begin(txn, KNOT_DB_NOOP);
+ if (it == NULL) {
+ ret = KNOT_ERROR;
+ goto cleanup;
+ }
+ it = db_api->iter_seek(it, &search, KNOT_DB_LEQ);
+ if (it == NULL) {
+ ret = KNOT_ENOENT;
+ goto cleanup;
+ }
+ if (db_api->iter_key(it, &key) != KNOT_EOK || db_api->iter_val(it, &val) != KNOT_EOK) {
+ ret = KNOT_ERROR;
+ goto cleanup;
+ }
+ if (knot_dname_cmp((const knot_dname_t *)key.data + 1, rrsig->owner) != 0) {
+ ret = KNOT_ENOENT;
+ goto cleanup;
+ }
+ wire_ctx_t wire = wire_ctx_init(val.data, val.len);
+#define CHK_RET if (ret != KNOT_EOK) goto cleanup;
+ ret = deserialize_rrset(&wire, rrsig);
+ CHK_RET
+ ret = deserialize_rrset(&wire, dnskey);
+ CHK_RET
+ ret = deserialize_rrset(&wire, cdnskey);
+ CHK_RET
+ ret = deserialize_rrset(&wire, cds);
+ CHK_RET
+#undef CHK_RET
+ *next_time = 0;
+ if ((it = db_api->iter_next(it)) != NULL && db_api->iter_key(it, &key) == KNOT_EOK) {
+ const knot_dname_t *next_name = (const knot_dname_t *)key.data + 1;
+ if (knot_dname_cmp(next_name, rrsig->owner) == 0) {
+ *next_time = atol((char *)key.data + 1 + knot_dname_size(next_name));
}
}
- free_key(&key);
+cleanup:
+ db_api->iter_finish(it);
+ free_key(&search);
with_txn_end(NULL);
return ret;
}
*
* \return KNOT_E*
*/
-int kasp_db_store_offline_rrsig(kasp_db_t *db, knot_time_t for_time, const knot_rrset_t *rrsig);
+int kasp_db_store_offline_rrsig(kasp_db_t *db, knot_time_t for_time, const knot_rrset_t *rrsig,
+ const knot_rrset_t *dnskey, const knot_rrset_t *cdnskey, const knot_rrset_t *cds);
/*!
* \brief Load pregenerated RRSIG.
*
* \return KNOT_E*
*/
-int kasp_db_load_offline_rrsig(kasp_db_t *db, const knot_dname_t *for_dname, knot_time_t for_time, knot_rrset_t *rrsig);
+int kasp_db_load_offline_rrsig(kasp_db_t *db, const knot_dname_t *for_dname, knot_time_t for_time, knot_time_t *next_time,
+ knot_rrset_t *rrsig, knot_rrset_t *dnskey, knot_rrset_t *cdnskey, knot_rrset_t *cds);
/*!
* \brief Delete pregenerated RRSIGs from beginning to the specified timestamp.
uint32_t ksk_sbm_check_interval;
unsigned child_records_publish;
parent_dynarray_t parents;
+ bool offline_ksk;
} knot_kasp_policy_t;
// TODO make the time parameters knot_timediff_t ??
bool allowed_general_roll = ((flags & KEY_ROLL_ALLOW_KSK_ROLL) && (flags & KEY_ROLL_ALLOW_ZSK_ROLL));
// generate initial keys if missing
if (!key_present(ctx, true, false) && !key_present(ctx, true, true)) {
- if (ctx->policy->ksk_shared) {
- ret = share_or_generate_key(ctx, GEN_KSK_FLAGS, ctx->now, false);
- } else {
- ret = generate_key(ctx, GEN_KSK_FLAGS, ctx->now, false);
+ if (!ctx->rollover_only_zsk) {
+ if (ctx->policy->ksk_shared) {
+ ret = share_or_generate_key(ctx, GEN_KSK_FLAGS, ctx->now, false);
+ } else {
+ ret = generate_key(ctx, GEN_KSK_FLAGS, ctx->now, false);
+ }
+ reschedule->plan_ds_query = true;
}
- reschedule->plan_ds_query = true;
if (ret == KNOT_EOK) {
reschedule->keys_changed = true;
if (!ctx->policy->singe_type_signing &&
log_zone_info(zone_name, "DNSSEC, signing started");
- result = knot_zone_sign_update_dnskeys(update, &keyset, &ctx);
+ knot_time_t next_resign = 0;
+ result = knot_zone_sign_update_dnskeys(update, &keyset, &ctx, &next_resign);
if (result != KNOT_EOK) {
log_zone_error(zone_name, "DNSSEC, failed to update DNSKEY records (%s)",
knot_strerror(result));
done:
if (result == KNOT_EOK) {
- reschedule->next_sign = schedule_next(&ctx, &keyset, zone_expire);
+ reschedule->next_sign = schedule_next(&ctx, &keyset, knot_time_min(zone_expire, next_resign));
}
free_zone_keys(&keyset);
case 15: // all keys ready for signing
have_active_alg = true;
break;
+ case 10:
+ if (ctx->policy->offline_ksk) {
+ have_active_alg = true;
+ break;
+ }
+ // else FALLTHROUGH
default:
return KNOT_DNSSEC_EMISSINGKEYTYPE;
}
case DNSSEC_EOK:
case DNSSEC_KEY_ALREADY_PRESENT:
break;
- case DNSSEC_ENOENT: // we hope that this is just offline KSK
- break;
default:
return r;
}
return result;
}
-static bool can_have_offline_rrsig(const knot_rrset_t *rr, const knot_dname_t *zone_apex)
-{
- return (rr->type == KNOT_RRTYPE_DNSKEY && knot_dname_cmp(rr->owner, zone_apex) == 0);
-}
-
-static int load_offline_rrsig(const knot_rrset_t *covered,
- knot_rrset_t *rrsig,
- const kdnssec_ctx_t *ctx)
-{
- knot_rrset_init_empty(rrsig);
-
- if (!can_have_offline_rrsig(covered, ctx->zone->dname)) {
- return KNOT_EOK;
- }
-
- int ret = kasp_db_load_offline_rrsig(*ctx->kasp_db, covered->owner, ctx->now, rrsig);
- if (ret == KNOT_ENOENT) {
- ret = KNOT_EOK;
- }
- return ret;
-}
-
/*!
* \brief Add missing RRSIGs into the changeset for adding.
*
assert(zone_keys);
assert(changeset);
- knot_rrset_t to_add, offline_rrsigs;
+ int result = KNOT_EOK;
+ knot_rrset_t to_add;
knot_rrset_init_empty(&to_add);
- int result = load_offline_rrsig(covered, &offline_rrsigs, dnssec_ctx);
- if (result != KNOT_EOK) {
- log_zone_warning(dnssec_ctx->zone->dname, "DNSSEC, failed to load offline DNSKEY RRSIG (%s)",
- knot_strerror(result));
+
+ if (covered->type == KNOT_RRTYPE_DNSKEY &&
+ knot_dname_cmp(covered->owner, dnssec_ctx->zone->dname) == 0 &&
+ dnssec_ctx->offline_rrsig != NULL) {
+ return changeset_add_addition(changeset, dnssec_ctx->offline_rrsig, CHANGESET_CHECK);
}
for (int i = 0; i < zone_keys->count; i++) {
to_add = create_empty_rrsigs_for(covered);
}
- uint16_t at_offline;
- if (valid_signature_exists(covered, &offline_rrsigs, key->key, key->ctx, dnssec_ctx, &at_offline)) {
- log_zone_info(dnssec_ctx->zone->dname, "DNSSEC, using offline DNSKEY RRSIG");
- knot_rdata_t *offline_rd = knot_rdataset_at(&offline_rrsigs.rrs, at_offline);
- result = knot_rrset_add_rdata(&to_add, offline_rd->data, offline_rd->len, NULL);
- if (result != KNOT_EOK) {
- break;
- }
- continue;
- }
-
result = knot_sign_rrset(&to_add, covered, key->key, key->ctx,
dnssec_ctx, NULL, expires_at);
if (result != KNOT_EOK) {
}
knot_rdataset_clear(&to_add.rrs, NULL);
- knot_rrset_clear(&offline_rrsigs, NULL);
return result;
}
int knot_zone_sign_update_dnskeys(zone_update_t *update,
zone_keyset_t *zone_keys,
- const kdnssec_ctx_t *dnssec_ctx)
+ kdnssec_ctx_t *dnssec_ctx,
+ knot_time_t *next_resign)
{
if (update == NULL || zone_keys == NULL || dnssec_ctx == NULL) {
return KNOT_EINVAL;
knot_rrset_t *add_dnskeys = NULL;
knot_rrset_t *add_cdnskeys = NULL;
knot_rrset_t *add_cdss = NULL;
+ knot_rrset_t *add_rrsigs = NULL;
uint32_t dnskey_ttl = dnssec_ctx->policy->dnskey_ttl;
knot_rrset_t soa = node_rrset(apex, KNOT_RRTYPE_SOA);
if (knot_rrset_empty(&soa)) {
0, NULL);
add_cdss = knot_rrset_new(apex->owner, KNOT_RRTYPE_CDS, soa.rclass,
0, NULL);
- if (add_dnskeys == NULL || add_cdnskeys == NULL || add_cdss == NULL) {
+ add_rrsigs = knot_rrset_new(apex->owner, KNOT_RRTYPE_RRSIG, soa.rclass,
+ dnskey_ttl, NULL);
+ if (add_dnskeys == NULL || add_cdnskeys == NULL || add_cdss == NULL || add_rrsigs == NULL) {
ret = KNOT_ENOMEM;
CHECK_RET;
}
- ret = knot_zone_sign_add_dnskeys(zone_keys, dnssec_ctx, add_dnskeys, add_cdnskeys, add_cdss);
+ if (dnssec_ctx->policy->offline_ksk) {
+ ret = kasp_db_load_offline_rrsig(*dnssec_ctx->kasp_db, apex->owner, dnssec_ctx->now, next_resign,
+ add_rrsigs, add_dnskeys, add_cdnskeys, add_cdss);
+ if (ret == KNOT_EOK) {
+ log_zone_info(dnssec_ctx->zone->dname, "DNSSEC, using offline DNSKEY RRSIG");
+ } else {
+ log_zone_warning(dnssec_ctx->zone->dname, "DNSSEC, failed to load offline DNSKEY RRSIG (%s)",
+ knot_strerror(ret));
+ }
+ } else {
+ ret = knot_zone_sign_add_dnskeys(zone_keys, dnssec_ctx, add_dnskeys, add_cdnskeys, add_cdss);
+ }
CHECK_RET;
if (!knot_rrset_empty(add_cdnskeys)) {
CHECK_RET;
}
+ if (!knot_rrset_empty(add_rrsigs)) {
+ dnssec_ctx->offline_rrsig = add_rrsigs;
+ add_rrsigs = NULL; // prevent free
+ }
+
ret = zone_update_apply_changeset(update, &ch);
#undef CHECK_RET
knot_rrset_free(add_dnskeys, NULL);
knot_rrset_free(add_cdnskeys, NULL);
knot_rrset_free(add_cdss, NULL);
+ knot_rrset_free(add_rrsigs, NULL);
changeset_clear(&ch);
return ret;
}
*/
int knot_zone_sign_update_dnskeys(zone_update_t *update,
zone_keyset_t *zone_keys,
- const kdnssec_ctx_t *dnssec_ctx);
+ kdnssec_ctx_t *dnssec_ctx,
+ knot_time_t *next_resign);
/*!
* \brief Check if key can be used to sign given RR.
if (wire->error != KNOT_EOK) {
return wire->error;
}
+ if (rrset->owner != NULL) {
+ if (knot_dname_cmp(owner, rrset->owner) != 0) {
+ knot_dname_free(owner, NULL);
+ return KNOT_ESEMCHECK;
+ }
+ knot_rrset_clear(rrset, NULL);
+ }
knot_rrset_init(rrset, owner, type, rclass, 0);
for (size_t phase = 0; phase < rrcount && wire_ctx_available(wire) > 0; phase++) {
size_t rrset_serialized_size(const knot_rrset_t *rrset)
{
- if (rrset == NULL || rrset->rrs.count == 0) {
+ if (rrset == NULL) {
return 0;
}
// store it to KASP db
assert(!knot_rrset_empty(rrsig));
- ret = kasp_db_store_offline_rrsig(*ctx->kasp_db, ctx->now, rrsig);
+ ret = kasp_db_store_offline_rrsig(*ctx->kasp_db, ctx->now, rrsig, NULL, NULL, NULL); // TODO !
if (ret != KNOT_EOK) {
printf("store rrsig failed\n");
goto done;
int keymgr_print_rrsig(kdnssec_ctx_t *ctx, knot_time_t when)
{
- knot_rrset_t rrsig = { 0 };
- knot_rrset_init_empty(&rrsig);
- int ret = kasp_db_load_offline_rrsig(*ctx->kasp_db, ctx->zone->dname, when, &rrsig);
+ knot_time_t next = 0;
+ knot_rrset_t rrsig = { 0 }, dnskey = { 0 }, cdnskey = { 0 }, cds = { 0 };
+ knot_rrset_init(&rrsig, knot_dname_copy(ctx->zone->dname, NULL),
+ KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN, ctx->policy->dnskey_ttl);
+ knot_rrset_init_empty(&dnskey);
+ knot_rrset_init_empty(&cdnskey);
+ knot_rrset_init_empty(&cds);
+ int ret = kasp_db_load_offline_rrsig(*ctx->kasp_db, ctx->zone->dname, when, &next, &rrsig, &dnskey, &cdnskey, &cds);
if (ret == KNOT_EOK) {
char *buf = NULL;
size_t buf_size = 512;
ret = dump_rrset_to_buf(&rrsig, &buf, &buf_size);
+ if (ret >= 0) {
+ printf("%s", buf);
+ ret = dump_rrset_to_buf(&dnskey, &buf, &buf_size);
+ }
+ if (ret >= 0) {
+ printf("%s", buf);
+ ret = dump_rrset_to_buf(&cdnskey, &buf, &buf_size);
+ }
+ if (ret >= 0) {
+ printf("%s", buf);
+ ret = dump_rrset_to_buf(&cds, &buf, &buf_size);
+ }
if (ret >= 0) {
printf("%s", buf);
ret = KNOT_EOK;
}
free(buf);
+ printf("; next %lu\n", next);
}
knot_rrset_clear(&rrsig, NULL);
+ knot_rrset_clear(&dnskey, NULL);
+ knot_rrset_clear(&cdnskey, NULL);
+ knot_rrset_clear(&cds, NULL);
return ret;
}
printf("generated on %s by KnotDNS %s\n", buf, VERSION);
}
-static int ksr_once(kdnssec_ctx_t *ctx, char **buf, size_t *buf_size)
+static int ksr_once(kdnssec_ctx_t *ctx, char **buf, size_t *buf_size, knot_time_t *next_ksr)
{
knot_rrset_t *dnskey = NULL;
zone_keyset_t keyset = { 0 };
}
done:
+ if (ret == KNOT_EOK && next_ksr != NULL) {
+ *next_ksr = knot_get_next_zone_key_event(&keyset);
+ }
knot_rrset_free(dnskey, NULL);
free_zone_keys(&keyset);
return ret;
char *buf = NULL;
size_t buf_size = 4096;
- while (ret == KNOT_EOK && knot_time_cmp(next, upto) <= 0) {
+ while (ret == KNOT_EOK && knot_time_cmp(next, upto) < 0) {
ctx->now = next;
- ret = ksr_once(ctx, &buf, &buf_size);
- next_resign(&next, ctx);
+ ret = ksr_once(ctx, &buf, &buf_size, &next);
}
+ if (ret != KNOT_EOK) {
+ free(buf);
+ return ret;
+ }
+ ctx->now = upto;
+ // force end of period as a KSR timestamp
+ ret = ksr_once(ctx, &buf, &buf_size, NULL);
+
printf(";; KeySigningRequest ");
print_generated_message();
}
typedef struct {
- knot_rrset_t *rr;
+ knot_rrset_t *dnskey;
+ knot_rrset_t *dnskey_prev;
+ knot_rrset_t *cdnskey;
+ knot_rrset_t *cds;
+ knot_rrset_t *rrsig;
kdnssec_ctx_t *kctx;
} ksr_sign_ctx_t;
-static int ksr_sign_dnskey(kdnssec_ctx_t *ctx, knot_rrset_t *dnskey)
+static int ksr_sign_dnskey(kdnssec_ctx_t *ctx, knot_rrset_t *zsk, knot_time_t *next_sign)
{
zone_keyset_t keyset = { 0 };
char *buf = NULL;
if (ret != KNOT_EOK) {
return ret;
}
- knot_rrset_t *rrsig = knot_rrset_new(ctx->zone->dname, KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN, ctx->policy->dnskey_ttl, NULL);
- if (rrsig == NULL) {
+ knot_rrset_t *dnskey = knot_rrset_copy(zsk, NULL);
+ knot_rrset_t *cdnskey = knot_rrset_new(zsk->owner, KNOT_RRTYPE_CDNSKEY, zsk->rclass, 0, NULL);
+ knot_rrset_t *cds = knot_rrset_new(zsk->owner, KNOT_RRTYPE_CDS, zsk->rclass, 0, NULL);
+ knot_rrset_t *rrsig = knot_rrset_new(zsk->owner, KNOT_RRTYPE_RRSIG, zsk->rclass, zsk->ttl, NULL);
+ if (dnskey == NULL || cdnskey == NULL || cds == NULL || rrsig == NULL) {
ret = KNOT_ENOMEM;
goto done;
}
+
+ ret = knot_zone_sign_add_dnskeys(&keyset, ctx, dnskey, cdnskey, cds);
+ if (ret != KNOT_EOK) {
+ goto done;
+ }
+
// no check if the KSK used for signing (in keyset) is contained in DNSKEY record being signed (in KSR) !
for (int i = 0; i < keyset.count; i++) {
zone_key_t *key = &keyset.keys[i];
}
}
}
- ret = dump_rrset_to_buf(rrsig, &buf, &buf_size);
+ printf(";;SKR ===========\n");
+ ret = dump_rrset_to_buf(dnskey, &buf, &buf_size);
+ if (ret >= 0) {
+ printf("%s", buf);
+ ret = dump_rrset_to_buf(cdnskey, &buf, &buf_size);
+ }
+ if (ret >= 0) {
+ printf("%s", buf);
+ ret = dump_rrset_to_buf(cds, &buf, &buf_size);
+ }
+ if (ret >= 0) {
+ printf("%s", buf);
+ ret = dump_rrset_to_buf(rrsig, &buf, &buf_size);
+ }
if (ret >= 0) {
buf[strlen(buf) - 1] = '\0'; // remove trailing newline
- printf(";;SKR %lu %hu %d\n%s ; end SKR %lu\n", ctx->now, rrsig->rrs.count, ret, buf, ctx->now);
+ printf("%s ; end SKR %lu\n", buf, ctx->now);
ret = KNOT_EOK;
}
done:
free(buf);
+ knot_rrset_free(dnskey, NULL);
+ knot_rrset_free(cdnskey, NULL);
+ knot_rrset_free(cds, NULL);
knot_rrset_free(rrsig, NULL);
+ if (ret == KNOT_EOK) {
+ *next_sign = knot_get_next_zone_key_event(&keyset);
+ }
free_zone_keys(&keyset);
return ret;
}
+static int process_skr_between_ksrs(ksr_sign_ctx_t *ctx, knot_time_t next_ksr)
+{
+ static knot_time_t prev_ksr = 0;
+
+ assert((prev_ksr == 0 ? 1 : 0) == (knot_rrset_empty(ctx->dnskey_prev) ? 1 : 0));
+
+ if (!knot_rrset_empty(ctx->dnskey_prev)) {
+ for (knot_time_t inbetween_skr = prev_ksr; inbetween_skr < next_ksr; ) {
+ ctx->kctx->now = inbetween_skr;
+ int ret = ksr_sign_dnskey(ctx->kctx, ctx->dnskey_prev, &inbetween_skr);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ }
+
+ // finally, do the "requested" SKR and copy ctx->dnskey into ct->rrsig
+ ctx->kctx->now = next_ksr;
+ int ret = ksr_sign_dnskey(ctx->kctx, ctx->dnskey, &prev_ksr);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ knot_rdataset_clear(&ctx->dnskey_prev->rrs, NULL);
+ ret = knot_rdataset_copy(&ctx->dnskey_prev->rrs, &ctx->dnskey->rrs, NULL);
+ return ret;
+}
+
static void ksr_sign_once(zs_scanner_t *sc)
{
ksr_sign_ctx_t *ctx = sc->process.data;
- sc->error.code = knot_rrset_add_rdata(ctx->rr, sc->r_data, sc->r_data_length, NULL);
- ctx->rr->ttl = sc->r_ttl;
+ sc->error.code = knot_rrset_add_rdata(ctx->dnskey, sc->r_data, sc->r_data_length, NULL);
+ ctx->dnskey->ttl = sc->r_ttl;
if (sc->error.code == KNOT_EOK && sc->buffer_length > 9 && strncmp((const char *)sc->buffer, " end KSR ", 9) == 0) {
- ctx->kctx->now = atol((const char *)sc->buffer + 9);
- sc->error.code = ksr_sign_dnskey(ctx->kctx, ctx->rr);
- knot_rdataset_clear(&ctx->rr->rrs, NULL);
+ knot_time_t next_ksr = atol((const char *)sc->buffer + 9);
+ sc->error.code = process_skr_between_ksrs(ctx, next_ksr);
+ knot_rdataset_clear(&ctx->dnskey->rrs, NULL);
}
}
static void skr_import_once(zs_scanner_t *sc)
{
ksr_sign_ctx_t *ctx = sc->process.data;
+ knot_rrset_t *to_add = NULL;
+ switch (sc->r_type) {
+ case KNOT_RRTYPE_DNSKEY:
+ to_add = ctx->dnskey;
+ break;
+ case KNOT_RRTYPE_CDNSKEY:
+ to_add = ctx->cdnskey;
+ break;
+ case KNOT_RRTYPE_CDS:
+ to_add = ctx->cds;
+ break;
+ case KNOT_RRTYPE_RRSIG:
+ to_add = ctx->rrsig;
+ break;
+ default:
+ sc->error.code = KNOT_EINVAL;
+ return;
+ }
- sc->error.code = knot_rrset_add_rdata(ctx->rr, sc->r_data, sc->r_data_length, NULL);
- ctx->rr->ttl = sc->r_ttl;
+ sc->error.code = knot_rrset_add_rdata(to_add, sc->r_data, sc->r_data_length, NULL);
+ to_add->ttl = sc->r_ttl;
if (sc->error.code == KNOT_EOK && sc->buffer_length > 9 && strncmp((const char *)sc->buffer, " end SKR ", 9) == 0) {
knot_time_t for_time = atol((const char *)sc->buffer + 9);
- sc->error.code = kasp_db_store_offline_rrsig(*ctx->kctx->kasp_db, for_time, ctx->rr);
- knot_rdataset_clear(&ctx->rr->rrs, NULL);
+ sc->error.code = kasp_db_store_offline_rrsig(*ctx->kctx->kasp_db, for_time, ctx->rrsig, ctx->dnskey, ctx->cdnskey, ctx->cds);
+ knot_rdataset_clear(&ctx->dnskey->rrs, NULL);
+ knot_rdataset_clear(&ctx->cdnskey->rrs, NULL);
+ knot_rdataset_clear(&ctx->cds->rrs, NULL);
+ knot_rdataset_clear(&ctx->rrsig->rrs, NULL);
}
}
-static int read_ksr_skr(kdnssec_ctx_t *ctx, const char *infile, void (*cb)(zs_scanner_t *), uint16_t rrtype)
+static int read_ksr_skr(kdnssec_ctx_t *ctx, const char *infile, void (*cb)(zs_scanner_t *))
{
zs_scanner_t sc = { 0 };
int ret = zs_init(&sc, "", KNOT_CLASS_IN, 0);
return KNOT_EFILE;
}
- knot_rrset_t rr = { 0 };
- knot_rrset_init(&rr, ctx->zone->dname, rrtype, KNOT_CLASS_IN, ctx->policy->dnskey_ttl);
+ knot_rrset_t dnskey = { 0 }, dnskey_prev = { 0 }, cdnskey = { 0 }, cds = { 0 }, rrsig = { 0 };
+ knot_rrset_init(&dnskey, ctx->zone->dname, KNOT_RRTYPE_DNSKEY, KNOT_CLASS_IN, ctx->policy->dnskey_ttl);
+ knot_rrset_init(&dnskey_prev, ctx->zone->dname, KNOT_RRTYPE_DNSKEY, KNOT_CLASS_IN, ctx->policy->dnskey_ttl);
+ knot_rrset_init(&cdnskey, ctx->zone->dname, KNOT_RRTYPE_CDNSKEY, KNOT_CLASS_IN, 0);
+ knot_rrset_init(&cds, ctx->zone->dname, KNOT_RRTYPE_CDS, KNOT_CLASS_IN, 0);
+ knot_rrset_init(&rrsig, ctx->zone->dname, KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN, ctx->policy->dnskey_ttl);
- ksr_sign_ctx_t pctx = { &rr, ctx };
+ ksr_sign_ctx_t pctx = { &dnskey, &dnskey_prev, &cdnskey, &cds, &rrsig, ctx };
ret = zs_set_processing(&sc, cb, NULL, &pctx);
if (ret < 0) {
zs_deinit(&sc);
if (sc.error.code != KNOT_EOK) {
ret = sc.error.code;
- } else if (ret < 0 || rr.rrs.count > 0) {
+ } else if (ret < 0 || dnskey.rrs.count > 0 || cdnskey.rrs.count > 0 ||
+ cds.rrs.count > 0 || rrsig.rrs.count > 0) {
ret = KNOT_EMALF;
}
- knot_rdataset_clear(&rr.rrs, NULL);
+ knot_rdataset_clear(&dnskey.rrs, NULL);
+ knot_rdataset_clear(&dnskey_prev.rrs, NULL);
+ knot_rdataset_clear(&cdnskey.rrs, NULL);
+ knot_rdataset_clear(&cds.rrs, NULL);
+ knot_rdataset_clear(&rrsig.rrs, NULL);
zs_deinit(&sc);
return ret;
}
int keymgr_sign_ksr(kdnssec_ctx_t *ctx, const char *ksr_file)
{
- int ret = read_ksr_skr(ctx, ksr_file, ksr_sign_once, KNOT_RRTYPE_DNSKEY);
+ int ret = read_ksr_skr(ctx, ksr_file, ksr_sign_once);
printf(";; SignedKeyResponse ");
print_generated_message();
return ret;
int keymgr_import_skr(kdnssec_ctx_t *ctx, const char *skr_file)
{
- return read_ksr_skr(ctx, skr_file, skr_import_once, KNOT_RRTYPE_RRSIG);
+ return read_ksr_skr(ctx, skr_file, skr_import_once);
}
+++ /dev/null
-#!/usr/bin/env python3
-
-"""
-Test of offline KSK by trying pre-generated ZSK rollover.
-"""
-
-import collections
-import os
-import shutil
-import datetime
-import subprocess
-import time
-from subprocess import check_call
-
-from dnstest.utils import *
-from dnstest.keys import Keymgr
-from dnstest.test import Test
-
-# check zone if keys are present and used for signing
-def check_zone(server, zone, dnskeys, dnskey_rrsigs, soa_rrsigs, msg):
- qdnskeys = server.dig("example.com", "DNSKEY", bufsize=4096)
- found_dnskeys = qdnskeys.count("DNSKEY")
-
- qdnskeyrrsig = server.dig("example.com", "DNSKEY", dnssec=True, bufsize=4096)
- found_rrsigs = qdnskeyrrsig.count("RRSIG")
-
- qsoa = server.dig("example.com", "SOA", dnssec=True, bufsize=4096)
- found_soa_rrsigs = qsoa.count("RRSIG")
-
- check_log("DNSKEYs: %d (expected %d)" % (found_dnskeys, dnskeys));
- check_log("RRSIGs: %d (expected %d)" % (found_soa_rrsigs, soa_rrsigs));
- check_log("DNSKEY-RRSIGs: %d (expected %d)" % (found_rrsigs, dnskey_rrsigs));
-
- if found_dnskeys != dnskeys:
- set_err("BAD DNSKEY COUNT: " + msg)
- detail_log("!DNSKEYs not published and activated as expected: " + msg)
-
- if found_soa_rrsigs != soa_rrsigs:
- set_err("BAD RRSIG COUNT: " + msg)
- detail_log("!RRSIGs not published and activated as expected: " + msg)
-
- if found_rrsigs != dnskey_rrsigs:
- set_err("BAD DNSKEY RRSIG COUNT: " + msg)
- detail_log("!RRSIGs not published and activated as expected: " + msg)
-
- detail_log(SEP)
-
- # Valgrind delay breaks the timing!
- if not server.valgrind:
- server.zone_backup(zone, flush=True)
- server.zone_verify(zone)
-
-def wait_for_rrsig_count(t, server, rrtype, rrsig_count, timeout):
- rtime = 0
- while True:
- qdnskeyrrsig = server.dig("example.com", rrtype, dnssec=True, bufsize=4096)
- found_rrsigs = qdnskeyrrsig.count("RRSIG")
- if found_rrsigs == rrsig_count:
- break
- rtime = rtime + 1
- t.sleep(1)
- if rtime > timeout:
- break
-
-def wait_for_dnskey_count(t, server, dnskey_count, timeout):
- for rtime in range(1, timeout):
- qdnskeyrrsig = server.dig("example.com", "DNSKEY", dnssec=True, bufsize=4096)
- found_dnskeys = qdnskeyrrsig.count("DNSKEY")
- if found_dnskeys == dnskey_count:
- break
- t.sleep(1)
-
-t = Test()
-
-knot = t.server("knot")
-ZONE = "example.com."
-FUTURE = 100
-
-zone = t.zone(ZONE)
-t.link(zone, knot)
-
-knot.zonefile_sync = 24 * 60 * 60
-
-knot.dnssec(zone).enable = True
-knot.dnssec(zone).manual = True
-knot.dnssec(zone).alg = "ECDSAP384SHA384"
-knot.dnssec(zone).dnskey_ttl = 2
-knot.dnssec(zone).zsk_lifetime = 12
-knot.dnssec(zone).ksk_lifetime = 300 # this can be possibly left also infinity
-knot.dnssec(zone).propagation_delay = 3
-knot.port = 1234 # dummy, will be overwritten
-knot.gen_confile()
-
-key_ksk = knot.gen_key(zone, ksk=True, alg="ECDSAP384SHA384", key_len=384)
-key_zsk = knot.gen_key(zone, ksk=False, alg="ECDSAP384SHA384", key_len=384)
-
-Keymgr.run_check(knot.confile, ZONE, "pregenerate", str(FUTURE))
-Keymgr.run_check(knot.confile, ZONE, "presign", str(FUTURE))
-
-os.remove(knot.keydir + "/keys/" + key_ksk.keyid + ".pem")
-
-# parameters
-
-t.start()
-knot.zone_wait(zone)
-check_zone(knot, zone, 2, 1, 1, "init")
-
-wait_for_dnskey_count(t, knot, 3, knot.dnssec(zone).zsk_lifetime)
-check_zone(knot, zone, 3, 1, 1, "ZSK rollover")
-
-t.sleep(2)
-
-wait_for_dnskey_count(t, knot, 2, 2 * (knot.dnssec(zone).propagation_delay + knot.dnssec(zone).dnskey_ttl))
-check_zone(knot, zone, 2, 1, 1, "end")
-
-t.end()
knot.dnssec(zone).zsk_lifetime = STARTUP + 6*TICK # see ksk1 lifetime
knot.dnssec(zone).ksk_lifetime = 300 # this can be possibly left also infinity
knot.dnssec(zone).propagation_delay = TICK-2
-knot.dnssec(zone).cds_publish = "none"
+knot.dnssec(zone).offline_ksk = "on"
knot.port = 1234 # dummy, will be overwritten
knot.gen_confile()
-def tickf(when):
- return "+%d" % (STARTUP + when * TICK)
-
-# generate keys, including manual KSK rollover on the beginning
-key_ksk1 = knot.key_gen(ZONE, ksk="true", created="+0", publish="+0", ready="+0", active="+0", retire=tickf(4), remove=tickf(5))
-key_ksk2 = knot.key_gen(ZONE, ksk="true", created="+0", publish=tickf(2), ready=tickf(3), active=tickf(4), retire="+2h", remove="+3h")
-key_zsk1 = knot.key_gen(ZONE, ksk="false", created="+0", publish="+0", active="+0")
-
-# signer knot, copy everything from "knot"
signer = t.server("knot")
t.link(zone, signer)
signer.zones[ZONE].dnssec = knot.zones[ZONE].dnssec
signer.port = 1235
signer.gen_confile()
-_, keys_list, _ = Keymgr.run_check(knot.confile, ZONE, "list")
-for keyparm in keys_list.splitlines():
- pem = knot.keydir + "/keys/" + keyparm.split()[0] + ".pem"
- parm1 = keyparm.split()[1]
- parm2 = keyparm.replace("-", "_").split()[7:]
- Keymgr.run_check(signer.confile, ZONE, "import-pem", pem, parm1, *parm2)
-
-# delete KSKs in "knot" and ZSKs in "signer"
-os.remove(knot.keydir + "/keys/" + key_ksk1 + ".pem")
-os.remove(knot.keydir + "/keys/" + key_ksk2 + ".pem")
-os.remove(signer.keydir + "/keys/" + key_zsk1 + ".pem")
+
+def tickf(when):
+ return "+%d" % (STARTUP + when * TICK)
+
+# generate keys, including manual KSK rollover on the beginning
+key_ksk1 = signer.key_gen(ZONE, ksk="true", created="+0", publish="+0", ready="+0", active="+0", retire=tickf(4), remove=tickf(5))
+key_ksk2 = signer.key_gen(ZONE, ksk="true", created="+0", publish=tickf(2), ready=tickf(3), active=tickf(4), retire="+2h", remove="+3h")
+key_zsk1 = knot.key_gen(ZONE, ksk="false", created="+0", publish="+0", active="+0")
# pregenerate keys, exchange KSR, pre-sign it, exchange SKR
KSR = knot.keydir + "/ksr"
self.ksk_sbm_check_interval = None
self.ksk_shared = None
self.cds_publish = None
+ self.offline_ksk = None
class Zone(object):
'''DNS zone description'''
s.item("ksk-submission", z.name)
self._bool(s, "ksk-shared", z.dnssec.ksk_shared)
self._str(s, "cds-cdnskey-publish", z.dnssec.cds_publish)
+ self._str(s, "offline-ksk", z.dnssec.offline_ksk)
if have_policy:
s.end()