Commit d16f4618 authored by Daniel Salzman's avatar Daniel Salzman

Merge branch 'dnssec-nsec3-resalt' into 'master'

NSEC3 resalt



See merge request !547
parents eacbcfe2 18ef7d39
......@@ -251,7 +251,6 @@ DNSKEY algorithm number or IANA mnemonic.
.TP
\fBdnskey\-ttl\fP \fIseconds\fP
TTL value for DNSKEY records.
\fBNote\fP, the value is temporarily overridden by the SOA TTL.
.TP
\fBksk\-size\fP \fIbits\fP
Size of the KSK.
......@@ -270,8 +269,15 @@ Period before signature expiration when the signature will be refreshed.
.TP
\fBnsec3\fP \fIenable\fP
Specifies if NSEC3 will be used instead of NSEC.
\fBNote\fP, currently unused (the setting is derived from NSEC3PARAM presence
in the zone).
.TP
\fBnsec3\-iterations\fP \fIiterations\fP
Specifies the number of additional iterations in NSEC3 computation.
.TP
\fBnsec3\-salt\-length\fP \fIbytes\fP
Specifies salt length for NSEC3 computation.
.TP
\fBnsec3\-salt\-lifetime\fP \fIseconds\fP
Period after which a new NSEC3 salt is generated.
.TP
\fBsoa\-min\-ttl\fP \fIseconds\fP
SOA Minimum TTL field.
......
......@@ -211,7 +211,6 @@ Available *policy-parameter*\ s:
**dnskey-ttl** *seconds*
TTL value for DNSKEY records.
**Note**, the value is temporarily overridden by the SOA TTL.
**ksk-size** *bits*
Size of the KSK.
......@@ -230,8 +229,15 @@ Available *policy-parameter*\ s:
**nsec3** *enable*
Specifies if NSEC3 will be used instead of NSEC.
**Note**, currently unused (the setting is derived from NSEC3PARAM presence
in the zone).
**nsec3-iterations** *iterations*
Specifies the number of additional iterations in NSEC3 computation.
**nsec3-salt-length** *bytes*
Specifies salt length for NSEC3 computation.
**nsec3-salt-lifetime** *seconds*
Period after which a new NSEC3 salt is generated.
**soa-min-ttl** *seconds*
SOA Minimum TTL field.
......
......@@ -78,6 +78,7 @@ libdnssec_la_SOURCES = \
lib/error.c \
lib/event/action.h \
lib/event/action/initial_key.c \
lib/event/action/nsec3_resalt.c \
lib/event/action/zsk_rollover.c \
lib/event/event.c \
lib/event/keystate.c \
......@@ -127,6 +128,7 @@ libdnssec_la_SOURCES = \
lib/list/ucw_clists.h \
lib/nsec/bitmap.c \
lib/nsec/hash.c \
lib/nsec/nsec.c \
lib/random.c \
lib/sign/der.c \
lib/sign/der.h \
......
......@@ -27,6 +27,7 @@ enum dnssec_event_type {
DNSSEC_EVENT_ZSK_ROLL_PUBLISH_NEW_KEY,
DNSSEC_EVENT_ZSK_ROLL_REPLACE_SIGNATURES,
DNSSEC_EVENT_ZSK_ROLL_REMOVE_OLD_KEY,
DNSSEC_EVENT_NSEC3_RESALT,
};
typedef enum dnssec_event_type dnssec_event_type_t;
......
......@@ -150,7 +150,9 @@ typedef struct dnssec_kasp_zone {
char *policy;
dnssec_list_t *keys;
dnssec_binary_t *nsec3_salt;
dnssec_binary_t nsec3_salt;
time_t nsec3_salt_created;
} dnssec_kasp_zone_t;
/*!
......@@ -302,8 +304,9 @@ typedef struct dnssec_kasp_policy {
uint32_t rrsig_refresh_before;
// NSEC3
bool nsec3_enabled;
uint32_t nsec3_resalt;
dnssec_nsec3_params_t nsec3_params;
uint32_t nsec3_salt_lifetime;
uint16_t nsec3_iterations;
uint8_t nsec3_salt_length;
// SOA
uint32_t soa_minimal_ttl;
// zone
......
......@@ -30,6 +30,7 @@ typedef struct event_action_functions event_action_functions_t;
extern const event_action_functions_t event_action_initial_key;
extern const event_action_functions_t event_action_zsk_rollover;
extern const event_action_functions_t event_action_nsec3_resalt;
/*!
* List of event implementations sorted by priority.
......@@ -37,5 +38,6 @@ extern const event_action_functions_t event_action_zsk_rollover;
static const event_action_functions_t * const EVENT_ACTION_HANDLERS[] = {
&event_action_initial_key,
&event_action_zsk_rollover,
&event_action_nsec3_resalt,
NULL,
};
/* Copyright (C) 2016 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>
#include "dnssec/binary.h"
#include "dnssec/error.h"
#include "dnssec/random.h"
#include "event/action.h"
#include "event/utils.h"
static bool responds_to(dnssec_event_type_t event)
{
return event == DNSSEC_EVENT_NSEC3_RESALT;
}
static bool params_match(const dnssec_binary_t *salt,
const dnssec_kasp_policy_t *policy)
{
return salt->size == policy->nsec3_salt_length;
}
static int plan(dnssec_event_ctx_t *ctx, dnssec_event_t *event)
{
assert(ctx);
assert(event);
if (!ctx->policy->nsec3_enabled || ctx->policy->nsec3_salt_length == 0) {
return DNSSEC_EOK;
}
time_t next = 0;
if (!params_match(&ctx->zone->nsec3_salt, ctx->policy)) {
next = ctx->now;
} else {
if (ctx->now < ctx->zone->nsec3_salt_created) {
return DNSSEC_EINVAL;
}
time_t age = ctx->now - ctx->zone->nsec3_salt_created;
if (age >= ctx->policy->nsec3_salt_lifetime) {
next = ctx->now;
} else {
next = ctx->now + (ctx->policy->nsec3_salt_lifetime - age);
}
}
event->type = DNSSEC_EVENT_NSEC3_RESALT;
event->time = next;
return DNSSEC_EOK;
}
static int generate_salt(dnssec_binary_t *salt, uint16_t length)
{
assert(salt);
dnssec_binary_t new_salt = { 0 };
if (length > 0) {
int r = dnssec_binary_alloc(&new_salt, length);
if (r != DNSSEC_EOK) {
return r;
}
r = dnssec_random_binary(&new_salt);
if (r != DNSSEC_EOK) {
dnssec_binary_free(&new_salt);
return r;
}
}
dnssec_binary_free(salt);
*salt = new_salt;
return DNSSEC_EOK;
}
static int exec(dnssec_event_ctx_t *ctx, const dnssec_event_t *event)
{
assert(ctx);
assert(event);
assert(event->type == DNSSEC_EVENT_NSEC3_RESALT);
int r = generate_salt(&ctx->zone->nsec3_salt, ctx->policy->nsec3_salt_length);
if (r != DNSSEC_EOK) {
return r;
}
ctx->zone->nsec3_salt_created = ctx->now;
return dnssec_kasp_zone_save(ctx->kasp, ctx->zone);
}
/*! Event API. */
const event_action_functions_t event_action_nsec3_resalt = {
.responds_to = responds_to,
.plan = plan,
.exec = exec,
};
......@@ -36,6 +36,8 @@ const char *dnssec_event_name(dnssec_event_type_t event)
return "ZSK rollover, replace signatures";
case DNSSEC_EVENT_ZSK_ROLL_REMOVE_OLD_KEY:
return "ZSK rollover, remove old key";
case DNSSEC_EVENT_NSEC3_RESALT:
return "refresh NSEC3 salt";
default:
return "unknown event";
}
......
......@@ -34,6 +34,9 @@ static const encode_attr_t POLICY_ATTRS[] = {
{ attr(rrsig_lifetime), encode_uint32, decode_uint32 },
{ attr(rrsig_refresh_before), encode_uint32, decode_uint32 },
{ attr(nsec3_enabled), encode_bool, decode_bool },
{ attr(nsec3_iterations), encode_uint16, decode_uint16 },
{ attr(nsec3_salt_length), encode_uint8, decode_uint8 },
{ attr(nsec3_salt_lifetime), encode_uint32, decode_uint32 },
{ attr(soa_minimal_ttl), encode_uint32, decode_uint32 },
{ attr(zone_maximal_ttl), encode_uint32, decode_uint32 },
{ attr(propagation_delay), encode_uint32, decode_uint32 },
......
......@@ -282,8 +282,8 @@ static int export_zone_config(const dnssec_kasp_zone_t *zone, json_t **config_pt
}
_json_cleanup_ json_t *salt = NULL;
if (zone->nsec3_salt) {
r = encode_binary(zone->nsec3_salt, &salt);
if (zone->nsec3_salt.size > 0) {
r = encode_binary(&zone->nsec3_salt, &salt);
if (r != DNSSEC_EOK) {
return r;
}
......@@ -291,13 +291,24 @@ static int export_zone_config(const dnssec_kasp_zone_t *zone, json_t **config_pt
salt = json_null();
}
_json_cleanup_ json_t *salt_created = NULL;
r = encode_time(&zone->nsec3_salt_created, &salt_created);
if (r != DNSSEC_EOK) {
return r;
} else if (salt_created == NULL) {
salt_created = json_null();
}
_json_cleanup_ json_t *policy = zone->policy ? json_string(zone->policy) : json_null();
if (!policy) {
return DNSSEC_ENOMEM;
}
json_t *config = json_pack("{sOsOsO}", "policy", policy,
"nsec3_salt", salt, "keys", keys);
json_t *config = json_pack("{sOsOsOsO}",
"policy", policy,
"nsec3_salt", salt,
"nsec3_salt_created", salt_created,
"keys", keys);
if (!config) {
return DNSSEC_ENOMEM;
}
......@@ -322,21 +333,24 @@ static int parse_zone_config(dnssec_kasp_zone_t *zone, json_t *config)
}
}
// get nsec3_salt
// get NSEC3 salt
dnssec_binary_t *salt = NULL;
dnssec_binary_t salt = { 0 };
json_t *json_salt = json_object_get(config, "nsec3_salt");
if (json_salt && !json_is_null(json_salt)) {
salt = malloc(sizeof(*salt));
if (!salt) {
int r = decode_binary(json_salt, &salt);
if (r != DNSSEC_EOK) {
free(policy);
return DNSSEC_ENOMEM;
return r;
}
clear_struct(salt);
}
int r = decode_binary(json_salt, salt);
time_t salt_created = 0;
json_t *json_salt_created = json_object_get(config, "nsec3_salt_created");
if (json_salt_created && !json_is_null(json_salt_created)) {
int r = decode_time(json_salt_created, &salt_created);
if (r != DNSSEC_EOK) {
free(salt);
dnssec_binary_free(&salt);
free(policy);
return r;
}
......@@ -348,8 +362,7 @@ static int parse_zone_config(dnssec_kasp_zone_t *zone, json_t *config)
json_t *json_keys = json_object_get(config, "keys");
int r = load_zone_keys(zone->dname, &keys, json_keys);
if (r != DNSSEC_EOK) {
dnssec_binary_free(salt);
free(salt);
dnssec_binary_free(&salt);
free(policy);
return r;
}
......@@ -357,7 +370,9 @@ static int parse_zone_config(dnssec_kasp_zone_t *zone, json_t *config)
// store the result
zone->policy = policy;
dnssec_binary_free(&zone->nsec3_salt);
zone->nsec3_salt = salt;
zone->nsec3_salt_created = salt_created;
kasp_zone_keys_free(zone->keys);
zone->keys = keys;
......
......@@ -66,8 +66,6 @@ void dnssec_kasp_policy_defaults(dnssec_kasp_policy_t *policy)
return;
}
// TODO: determine defaults for NSEC
clear_policy(policy);
policy->algorithm = DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256;
......@@ -83,6 +81,9 @@ void dnssec_kasp_policy_defaults(dnssec_kasp_policy_t *policy)
policy->propagation_delay = HOURS(1);
policy->nsec3_enabled = false;
policy->nsec3_iterations = 10;
policy->nsec3_salt_length = 8;
policy->nsec3_salt_lifetime = DAYS(30);
}
_public_
......@@ -94,7 +95,6 @@ void dnssec_kasp_policy_free(dnssec_kasp_policy_t *policy)
free(policy->name);
free(policy->keystore);
dnssec_nsec3_params_free(&policy->nsec3_params);
free(policy);
}
......
......@@ -82,8 +82,7 @@ void dnssec_kasp_zone_free(dnssec_kasp_zone_t *zone)
free(zone->dname);
free(zone->name);
free(zone->policy);
dnssec_binary_free(zone->nsec3_salt);
free(zone->nsec3_salt);
dnssec_binary_free(&zone->nsec3_salt);
free(zone);
}
......
......@@ -91,63 +91,6 @@ static gnutls_digest_algorithm_t algorithm_d2g(dnssec_nsec3_algorithm_t dnssec)
/* -- public API ----------------------------------------------------------- */
/*!
* Free NSEC3 parameters.
*/
_public_
void dnssec_nsec3_params_free(dnssec_nsec3_params_t *params)
{
if (!params) {
return;
}
dnssec_binary_free(&params->salt);
clear_struct(&params);
}
/*!
* Parse NSEC3 parameters from NSEC3PARAM RDATA.
*
* \see RFC 5155 (section 4.2)
*/
_public_
int dnssec_nsec3_params_from_rdata(dnssec_nsec3_params_t *params,
const dnssec_binary_t *rdata)
{
if (!params || !rdata || !rdata->data) {
return DNSSEC_EINVAL;
}
dnssec_nsec3_params_t new_params = { 0 };
wire_ctx_t wire = wire_init_binary(rdata);
if (wire_available(&wire) < 5) {
return DNSSEC_MALFORMED_DATA;
}
new_params.algorithm = wire_read_u8(&wire);
new_params.flags = wire_read_u8(&wire);
new_params.iterations = wire_read_u16(&wire);
new_params.salt.size = wire_read_u8(&wire);
if (wire_available(&wire) != new_params.salt.size) {
return DNSSEC_MALFORMED_DATA;
}
new_params.salt.data = malloc(new_params.salt.size);
if (new_params.salt.data == NULL) {
return DNSSEC_ENOMEM;
}
wire_read_binary(&wire, &new_params.salt);
assert(wire_tell(&wire) == rdata->size);
*params = new_params;
return DNSSEC_EOK;
}
/*!
* Compute NSEC3 hash for given data.
*/
......
/* Copyright (C) 2016 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 "dnssec/nsec.h"
#include "shared.h"
#include "wire.h"
#include "dnssec/binary.h"
#include "dnssec/error.h"
/*!
* Free NSEC3 parameters.
*/
_public_
void dnssec_nsec3_params_free(dnssec_nsec3_params_t *params)
{
if (!params) {
return;
}
dnssec_binary_free(&params->salt);
clear_struct(params);
}
/*!
* Parse NSEC3 parameters from NSEC3PARAM RDATA.
*
* \see RFC 5155 (section 4.2)
*/
_public_
int dnssec_nsec3_params_from_rdata(dnssec_nsec3_params_t *params,
const dnssec_binary_t *rdata)
{
if (!params || !rdata || !rdata->data) {
return DNSSEC_EINVAL;
}
dnssec_nsec3_params_t new_params = { 0 };
wire_ctx_t wire = wire_init_binary(rdata);
if (wire_available(&wire) < 5) {
return DNSSEC_MALFORMED_DATA;
}
new_params.algorithm = wire_read_u8(&wire);
new_params.flags = wire_read_u8(&wire);
new_params.iterations = wire_read_u16(&wire);
new_params.salt.size = wire_read_u8(&wire);
if (wire_available(&wire) != new_params.salt.size) {
return DNSSEC_MALFORMED_DATA;
}
new_params.salt.data = malloc(new_params.salt.size);
if (new_params.salt.data == NULL) {
return DNSSEC_ENOMEM;
}
wire_read_binary(&wire, &new_params.salt);
assert(wire_tell(&wire) == rdata->size);
*params = new_params;
return DNSSEC_EOK;
}
......@@ -6,6 +6,7 @@
/binary
/crypto
/event_keystate
/event_nsec3_resalt
/kasp_dir_escape
/kasp_dir_file
/kasp_policy
......
......@@ -21,6 +21,7 @@ check_PROGRAMS = \
binary \
crypto \
event_keystate \
event_nsec3_resalt \
kasp_dir_escape \
kasp_dir_file \
kasp_policy \
......
/* Copyright (C) 2016 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 <tap/basic.h>
#include <string.h>
#include "dnssec/error.h"
#include "dnssec/kasp.h"
#include "event/action.h"
#include "shared.h"
static const event_action_functions_t *api(void)
{
return &event_action_nsec3_resalt;
}
static void test_responds_to(void)
{
diag("responds_to");
ok(api()->responds_to(DNSSEC_EVENT_NSEC3_RESALT), "valid");
ok(!api()->responds_to(DNSSEC_EVENT_GENERATE_INITIAL_KEY), "invalid");
}
static void test_plan(void)
{
diag("plan");
dnssec_kasp_zone_t zone = { 0 };
dnssec_kasp_policy_t policy = {
.nsec3_enabled = false,
.nsec3_salt_lifetime = 1000,
.nsec3_iterations = 10,
.nsec3_salt_length = 16,
};
dnssec_event_ctx_t ctx = {
.zone = &zone,
.policy = &policy,
.now = 42000,
};
dnssec_event_t event = { 0 };
int r = api()->plan(&ctx, &event);
ok(r == DNSSEC_EOK && event.type == DNSSEC_EVENT_NONE,
"NSEC3 disabled");
policy.nsec3_enabled = true;
clear_struct(&event);
r = api()->plan(&ctx, &event);
ok(r == DNSSEC_EOK && event.type == DNSSEC_EVENT_NSEC3_RESALT &&
event.time == 42000, "salt not generated");
dnssec_binary_alloc(&zone.nsec3_salt, 16);
zone.nsec3_salt_created = 43000;
clear_struct(&event);
r = api()->plan(&ctx, &event);
ok(r == DNSSEC_EINVAL, "salt from future");
zone.nsec3_salt_created = 40000;
clear_struct(&event);
r = api()->plan(&ctx, &event);
ok(r == DNSSEC_EOK && event.type == DNSSEC_EVENT_NSEC3_RESALT &&
event.time == 42000, "salt overdue");
zone.nsec3_salt_created = 41500;
clear_struct(&event);
r = api()->plan(&ctx, &event);
ok(r == DNSSEC_EOK && event.type == DNSSEC_EVENT_NSEC3_RESALT &&
event.time == 42500, "fresh salt");
policy.nsec3_salt_length = 20;
clear_struct(&event);
r = api()->plan(&ctx, &event);
ok(r == DNSSEC_EOK && event.type == DNSSEC_EVENT_NSEC3_RESALT &&
event.time == 42000, "salt size incorrect");
policy.nsec3_salt_length = 0;
clear_struct(&event);
r = api()->plan(&ctx, &event);
ok(r == DNSSEC_EOK && event.type == DNSSEC_EVENT_NONE,
"nothing to resalt");
dnssec_binary_free(&zone.nsec3_salt);
}
static int mock_zone_save(void *ctx, const dnssec_kasp_zone_t *zone)
{
return DNSSEC_EOK;
}
static const dnssec_kasp_store_functions_t mock_kasp = {
.zone_save = mock_zone_save,
};
static void test_exec(void)
{
diag("exec");
dnssec_kasp_t *kasp = NULL;
dnssec_kasp_init_custom(&kasp, &mock_kasp);
dnssec_kasp_zone_t zone = { .name = ".", .dname = (uint8_t *)"" };
dnssec_binary_alloc(&zone.nsec3_salt, 10);
dnssec_kasp_policy_t policy = {
.nsec3_enabled = true,
.nsec3_salt_lifetime = 1000,
.nsec3_iterations = 10,
.nsec3_salt_length = 16,
};
dnssec_event_ctx_t ctx = {
.kasp = kasp,
.zone = &zone,
.policy = &policy,
.now = 7000,
};
dnssec_event_t event = {
.type = DNSSEC_EVENT_NSEC3_RESALT,
.time = 7000,
};
int r = api()->exec(&ctx, &event);
ok(r == DNSSEC_EOK &&
zone.nsec3_salt.size == 16 && zone.nsec3_salt_created == 7000,
"generate salt");
policy.nsec3_salt_length = 0;
ctx.now = 8000;
r = api()->exec(&ctx, &event);
ok(r == DNSSEC_EOK &&
zone.nsec3_salt.size == 0 && zone.nsec3_salt_created == 8000,
"generate salt of zero length");
dnssec_binary_free(&zone.nsec3_salt);
dnssec_kasp_deinit(kasp);
}
int main(int argc, char *argv[])
{
plan_lazy();
test_responds_to();
test_plan();
test_exec();
return 0;
}
/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2016 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
......@@ -12,7 +12,7 @@
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 <string.h>
#include <tap/basic.h>
......@@ -21,24 +21,25 @@
#include "error.h"
#include "nsec.h"
void test_length(void)
static const dnssec_binary_t RDATA = { .size = 9, .data = (uint8_t []) {
0x01, // algorithm
0x00, // flags