Commit 81b7bbc7 authored by Jan Kadlec's avatar Jan Kadlec
Browse files

SEC: zone resign planning

- Signing function now store the oldest signature expiration time, this time is
later used to plan zone resigning.
- Added new info strings to the 'zonestatus' command - gives information about w
hen the zone will be resigned

Refs #4
parent 72d3f513
......@@ -222,6 +222,15 @@ static int remote_c_status(server_t *s, remote_cmdargs_t* a)
return KNOT_EOK;
}
static char *dnssec_info(const zonedata_t *zd, char *buf)
{
assert(zd && zd->dnssec_timer);
time_t diff_time = zd->dnssec_timer->tv.tv_sec;
struct tm *t = localtime(&diff_time);
snprintf(buf, 128, "%s", asctime(t));
return buf;
}
/*!
* \brief Remote command 'zonestatus' handler.
*
......@@ -292,15 +301,19 @@ static int remote_c_zonestatus(server_t *s, remote_cmdargs_t* a)
}
/* Workaround, some platforms ignore 'size' with snprintf() */
char buf[256];
char buf[512];
char *dnssec_buf = xmalloc(128);
int n = snprintf(buf, sizeof(buf),
"%s\ttype=%s | serial=%u | %s %s\n",
"%s\ttype=%s | serial=%u | %s %s | %s %s\n",
zd->conf->name,
zd->xfr_in.has_master ? "slave" : "master",
serial,
state ? state : "",
when ? when : "");
when ? when : "",
zd->conf->dnssec_enable ? "automatic DNSSEC, resigning at:" : "",
zd->conf->dnssec_enable ? dnssec_info(zd, dnssec_buf) : "");
free(when);
free(dnssec_buf);
if (n < 0 || (size_t)n > rb) {
*dst = '\0';
ret = KNOT_ESPACE;
......
......@@ -1146,6 +1146,17 @@ static int zones_merge_and_store_changesets(knot_zone_t *zone,
return KNOT_EOK;
}
static int64_t expiration_to_relative(uint32_t exp) {
time_t t = time(NULL);
/*!
* This assert could happen only if signing itself took more
* than the 'refresh' time and some (or one) signatures would be kept.
*/
assert(t < exp);
// We need the time in miliseconds
return (exp - t) * 1000;
}
/*! \brief Creates diff and DNSSEC changesets and stores them to journal. */
static int zones_do_diff_and_sign(const conf_zone_t *z,
knot_zone_t *zone,
......@@ -1229,7 +1240,20 @@ static int zones_do_diff_and_sign(const conf_zone_t *z,
*/
knot_update_serial_t soa_up = KNOT_SOA_SERIAL_INC;
int ret = knot_dnssec_zone_sign(zone, sec_ch, soa_up);
uint32_t expires_at = 0;
int ret = knot_dnssec_zone_sign(zone, sec_ch, soa_up,
&expires_at);
if (ret != KNOT_EOK) {
knot_changesets_free(&diff_chs);
knot_changesets_free(&sec_chs);
rcu_read_unlock();
return ret;
}
// Schedule next zone signing
ret = zones_schedule_dnssec(zone,
expiration_to_relative(expires_at),
false);
if (ret != KNOT_EOK) {
knot_changesets_free(&diff_chs);
knot_changesets_free(&sec_chs);
......@@ -3510,10 +3534,12 @@ static int zones_dnssec_ev(event_t *event, bool force)
}
int ret = 0;
uint32_t expires_at = 0;
if (force) {
ret = knot_dnssec_zone_sign_force(zone, ch);
ret = knot_dnssec_zone_sign_force(zone, ch, &expires_at);
} else {
ret = knot_dnssec_zone_sign(zone, ch, KNOT_SOA_SERIAL_INC);
ret = knot_dnssec_zone_sign(zone, ch, KNOT_SOA_SERIAL_INC,
&expires_at);
}
if (ret != KNOT_EOK) {
knot_changesets_free(&chs);
......@@ -3548,9 +3574,13 @@ static int zones_dnssec_ev(event_t *event, bool force)
log_zone_info("Zone %s forced signed successfully.\n", zname);
free(zname);
// Schedule next signing
ret = zones_schedule_dnssec(zone, expiration_to_relative(expires_at),
force);
rcu_read_unlock();
return KNOT_EOK;
return ret;
}
static int zones_dnssec_regular_ev(event_t *event)
......@@ -3582,20 +3612,23 @@ int zones_schedule_dnssec(knot_zone_t *zone, int64_t time, bool force)
zonedata_t *zd = (zonedata_t *)zone->data;
evsched_t *scheduler = zd->server->sched;
// Cancel previous event, if any
if (zd->dnssec_timer) {
evsched_cancel(scheduler, zd->dnssec_timer);
evsched_event_free(scheduler, zd->dnssec_timer);
zd->dnssec_timer = NULL;
}
// TODO: throw an error if the new signing event is more in the future than the old one
if (force) {
zd->dnssec_timer = evsched_schedule_cb(scheduler,
zones_dnssec_forced_ev,
zone, time);
zones_dnssec_forced_ev,
zone, time);
} else {
zd->dnssec_timer = evsched_schedule_cb(scheduler,
zones_dnssec_regular_ev,
zone, time);
zones_dnssec_regular_ev,
zone, time);
}
return KNOT_EOK;
......
......@@ -83,7 +83,7 @@ static int init_dnssec_structs(const knot_zone_t *zone,
}
static int zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch, bool force,
knot_update_serial_t soa_up)
knot_update_serial_t soa_up, uint32_t *expires_at)
{
assert(zone);
assert(zone->contents);
......@@ -127,7 +127,8 @@ static int zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch, bool force,
knot_changeset_is_empty(out_ch));
// add missing signatures
result = knot_zone_sign(zone->contents, &zone_keys, &policy, out_ch);
result = knot_zone_sign(zone->contents, &zone_keys, &policy, out_ch,
expires_at);
if (result != KNOT_EOK) {
char *zname = knot_dname_to_str(zone->name);
log_zone_error("Error signing zone %s (%s).\n",
......@@ -173,23 +174,23 @@ static int zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch, bool force,
}
int knot_dnssec_zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch,
knot_update_serial_t soa_up)
knot_update_serial_t soa_up, uint32_t *expires_at)
{
if (zone == NULL || zone->contents == NULL || out_ch == NULL) {
return KNOT_EINVAL;
}
return zone_sign(zone, out_ch, false, soa_up);
return zone_sign(zone, out_ch, false, soa_up, expires_at);
}
int knot_dnssec_zone_sign_force(knot_zone_t *zone,
knot_changeset_t *out_ch)
knot_changeset_t *out_ch, uint32_t *expires_at)
{
if (zone == NULL || zone->contents == NULL || out_ch == NULL) {
return KNOT_EINVAL;
}
return zone_sign(zone, out_ch, true, KNOT_SOA_SERIAL_INC);
return zone_sign(zone, out_ch, true, KNOT_SOA_SERIAL_INC, expires_at);
}
int knot_dnssec_sign_changeset(const knot_zone_contents_t *zone,
......
......@@ -35,14 +35,15 @@
* \brief DNSSEC resign zone, store new records into changeset. Valid signatures
* and NSEC(3) records will not be changed.
*
* \param zone Zone to be signed.
* \param out_ch New records will be added to this changeset.
* \param soa_up SOA serial update policy.
* \param zone Zone to be signed.
* \param out_ch New records will be added to this changeset.
* \param soa_up SOA serial update policy.
* \param expires_at Expiration time of the oldest signature in zone
*
* \return Error code, KNOT_EOK if successful.
*/
int knot_dnssec_zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch,
knot_update_serial_t soa_up);
knot_update_serial_t soa_up, uint32_t *expires_at);
/*!
* \brief DNSSEC sign zone, store new records into changeset. Even valid
......@@ -50,10 +51,12 @@ int knot_dnssec_zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch,
*
* \param zone Zone to be signed.
* \param out_ch New records will be added to this changeset.
* \param expires_at Expiration time of the oldest signature in zone
*
* \return Error code, KNOT_EOK if successful.
*/
int knot_dnssec_zone_sign_force(knot_zone_t *zone, knot_changeset_t *out_ch);
int knot_dnssec_zone_sign_force(knot_zone_t *zone, knot_changeset_t *out_ch,
uint32_t *expires_at);
/*!
* \brief Sign changeset created by DDNS or zone-diff.
......
......@@ -161,6 +161,18 @@ static void get_matching_key_and_ctx(const knot_rrset_t *rrsigs, size_t pos,
*key = NULL;
}
static void update_zone_expiration_with(const knot_rrset_t *rrsig, size_t pos,
const knot_dnssec_policy_t *policy,
uint32_t *exp)
{
assert(rrsig && exp);
const uint32_t rrsig_exp = knot_rdata_rrsig_sig_expiration(rrsig, pos) -
policy->sign_refresh;
if (rrsig_exp < *exp) {
*exp = rrsig_exp;
}
}
/*!
* \brief Add expired or invalid RRSIGs into the changeset for removal.
*
......@@ -173,10 +185,11 @@ static void get_matching_key_and_ctx(const knot_rrset_t *rrsigs, size_t pos,
* \return Error code, KNOT_EOK if successful.
*/
static int remove_expired_rrsigs(const knot_rrset_t *covered,
const knot_rrset_t *rrsigs,
const knot_zone_keys_t *zone_keys,
const knot_dnssec_policy_t *policy,
knot_changeset_t *changeset)
const knot_rrset_t *rrsigs,
const knot_zone_keys_t *zone_keys,
const knot_dnssec_policy_t *policy,
knot_changeset_t *changeset,
uint32_t *expires_at)
{
assert(changeset);
......@@ -198,6 +211,8 @@ static int remove_expired_rrsigs(const knot_rrset_t *covered,
result = knot_is_valid_signature(covered, rrsigs, i,
key, ctx, policy);
if (result == KNOT_EOK) {
update_zone_expiration_with(rrsigs, i, policy,
expires_at);
continue; // valid signature
}
......@@ -362,12 +377,15 @@ static int force_resign_rrset(const knot_rrset_t *covered,
static int resign_rrset(const knot_rrset_t *covered,
const knot_zone_keys_t *zone_keys,
const knot_dnssec_policy_t *policy,
knot_changeset_t *changeset)
knot_changeset_t *changeset,
uint32_t *expires_at)
{
assert(covered);
// TODO this function creates some signatures twice (for checking)
// maybe merge the two functions into one
int result = remove_expired_rrsigs(covered, covered->rrsigs, zone_keys,
policy, changeset);
policy, changeset, expires_at);
if (result != KNOT_EOK) {
return result;
}
......@@ -473,7 +491,8 @@ static bool rr_should_be_signed(const knot_node_t *node,
static int sign_node_rrsets(const knot_node_t *node,
const knot_zone_keys_t *zone_keys,
const knot_dnssec_policy_t *policy,
knot_changeset_t *changeset)
knot_changeset_t *changeset,
uint32_t *expires_at)
{
assert(node);
assert(policy);
......@@ -499,7 +518,7 @@ static int sign_node_rrsets(const knot_node_t *node,
changeset);
} else {
result = resign_rrset(rrset, zone_keys, policy,
changeset);
changeset, expires_at);
}
if (result != KNOT_EOK) {
......@@ -517,6 +536,7 @@ typedef struct node_sign_args {
const knot_zone_keys_t *zone_keys;
const knot_dnssec_policy_t *policy;
knot_changeset_t *changeset;
uint32_t expires_at;
int result;
} node_sign_args_t;
......@@ -545,24 +565,26 @@ static void sign_node(knot_node_t **node, void *data)
}
args->result = sign_node_rrsets(*node, args->zone_keys, args->policy,
args->changeset);
args->changeset, &args->expires_at);
knot_node_clear_replaced_nsec(*node);
}
/*!
* \brief Update RRSIGs in a given zone tree by updating changeset.
*
* \param tree Zone tree to be signed.
* \param zone_keys Zone keys.
* \param policy DNSSEC policy.
* \param changeset Changeset to be updated.
* \param tree Zone tree to be signed.
* \param zone_keys Zone keys.
* \param policy DNSSEC policy.
* \param changeset Changeset to be updated.
* \param expires_at Expiration time of the oldest signature in zone.
*
* \return Error code, KNOT_EOK if successful.
*/
static int zone_tree_sign(knot_zone_tree_t *tree,
const knot_zone_keys_t *zone_keys,
const knot_dnssec_policy_t *policy,
knot_changeset_t *changeset)
knot_changeset_t *changeset,
uint32_t *expires_at)
{
assert(tree);
assert(zone_keys);
......@@ -570,8 +592,11 @@ static int zone_tree_sign(knot_zone_tree_t *tree,
assert(changeset);
node_sign_args_t args = {.zone_keys = zone_keys, .policy = policy,
.changeset = changeset, .result = KNOT_EOK};
.changeset = changeset, .result = KNOT_EOK,
.expires_at = time(NULL) + (policy->sign_lifetime -
policy->sign_refresh)};
knot_zone_tree_apply(tree, sign_node, &args);
*expires_at = args.expires_at;
return args.result;
}
......@@ -984,7 +1009,8 @@ static int sign_changeset_wrap(knot_rrset_t *chg_rrset, void *data)
int knot_zone_sign(const knot_zone_contents_t *zone,
const knot_zone_keys_t *zone_keys,
const knot_dnssec_policy_t *policy,
knot_changeset_t *changeset)
knot_changeset_t *changeset,
uint32_t *expires_at)
{
if (!zone || !zone_keys || !policy || !changeset) {
return KNOT_EINVAL;
......@@ -998,19 +1024,26 @@ int knot_zone_sign(const knot_zone_contents_t *zone,
return result;
}
result = zone_tree_sign(zone->nodes, zone_keys, policy, changeset);
uint32_t normal_tree_expiration = 0;
result = zone_tree_sign(zone->nodes, zone_keys, policy, changeset,
&normal_tree_expiration);
if (result != KNOT_EOK) {
dbg_dnssec_detail("zone_tree_sign() on normal nodes failed\n");
return result;
}
uint32_t nsec3_tree_expiration = 0;
result = zone_tree_sign(zone->nsec3_nodes, zone_keys, policy,
changeset);
changeset, &nsec3_tree_expiration);
if (result != KNOT_EOK) {
dbg_dnssec_detail("zone_tree_sign() on nsec3 nodes failed\n");
return result;
}
// We need the earlier value of these two
*expires_at = normal_tree_expiration <= nsec3_tree_expiration ?
normal_tree_expiration : nsec3_tree_expiration;
return KNOT_EOK;
}
......
......@@ -40,17 +40,18 @@
*
* Updates RRSIGs, NSEC(3)s, and DNSKEYs.
*
* \param zone Zone to be signed.
* \param zone_keys Zone keys.
* \param policy DNSSEC policy.
* \param changeset Changeset to be updated.
* \param zone Zone to be signed.
* \param zone_keys Zone keys.
* \param policy DNSSEC policy.
* \param changeset Changeset to be updated.
* \param expires_at Pointer to expiration time of the oldest signature in zone
*
* \return Error code, KNOT_EOK if successful.
*/
int knot_zone_sign(const knot_zone_contents_t *zone,
const knot_zone_keys_t *zone_keys,
const knot_dnssec_policy_t *policy,
knot_changeset_t *out_ch);
knot_changeset_t *out_ch, uint32_t *expires_at);
/*!
* \brief Update and sign SOA and store performed changes in changeset.
......
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