Skip to content
Snippets Groups Projects
Commit 80852ee0 authored by Daniel Salzman's avatar Daniel Salzman
Browse files

knot: add NSEC3 salt generation

parent 860e0084
Branches
Tags
1 merge request!543DNSSEC policy in configuration
......@@ -337,6 +337,7 @@ static zone_node_t *create_nsec3_node_for_node(zone_node_t *node,
}
if (node == apex) {
dnssec_nsec_bitmap_add(rr_types, KNOT_RRTYPE_DNSKEY);
dnssec_nsec_bitmap_add(rr_types, KNOT_RRTYPE_NSEC3PARAM);
}
zone_node_t *nsec3_node;
......
......@@ -18,6 +18,7 @@
#include "dnssec/error.h"
#include "dnssec/event.h"
#include "dnssec/random.h"
#include "contrib/macros.h"
#include "libknot/libknot.h"
#include "knot/conf/conf.h"
......@@ -30,28 +31,69 @@
#include "knot/dnssec/zone-sign.h"
#include "knot/zone/serial.h"
static int sign_init(const zone_contents_t *zone, int flags, kdnssec_ctx_t *ctx)
static int salt_init(kdnssec_ctx_t *ctx)
{
assert(zone);
assert(ctx);
const knot_dname_t *zone_name = zone->apex->owner;
if (!ctx->policy->nsec3_enabled) {
// Remove unused salt.
if (ctx->zone->nsec3_salt != NULL) {
dnssec_binary_free(ctx->zone->nsec3_salt);
free(ctx->zone->nsec3_salt);
ctx->zone->nsec3_salt = NULL;
return dnssec_kasp_zone_save(ctx->kasp, ctx->zone);
}
conf_val_t val = conf_zone_get(conf(), C_STORAGE, zone_name);
char *storage = conf_abs_path(&val, NULL);
val = conf_zone_get(conf(), C_KASP_DB, zone_name);
char *kasp_db = conf_abs_path(&val, storage);
free(storage);
return KNOT_EOK;
}
const size_t policy_size = ctx->policy->nsec3_params.salt.size;
// Reuse existing salt if has configured size.
if (ctx->zone->nsec3_salt != NULL &&
ctx->zone->nsec3_salt->size == policy_size) {
return KNOT_EOK;
}
char *zone_name_str = knot_dname_to_str_alloc(zone_name);
if (zone_name_str == NULL) {
free(kasp_db);
dnssec_binary_t *new_salt = malloc(sizeof(*new_salt));
if (new_salt == NULL) {
return KNOT_ENOMEM;
}
int r = kdnssec_ctx_init(ctx, kasp_db, zone_name_str);
free(zone_name_str);
free(kasp_db);
// Generate new salt.
if (policy_size > 0) {
int ret = dnssec_binary_alloc(new_salt, policy_size);
if (ret != DNSSEC_EOK) {
free(new_salt);
return ret;
}
ret = dnssec_random_binary(new_salt);
if (ret != DNSSEC_EOK) {
dnssec_binary_free(new_salt);
free(new_salt);
return ret;
}
} else {
memset(new_salt, 0, sizeof(*new_salt));
}
// Switch to the new salt.
dnssec_binary_free(ctx->zone->nsec3_salt);
free(ctx->zone->nsec3_salt);
ctx->zone->nsec3_salt = new_salt;
return dnssec_kasp_zone_save(ctx->kasp, ctx->zone);
}
static int sign_init(const zone_contents_t *zone, int flags, kdnssec_ctx_t *ctx)
{
assert(zone);
assert(ctx);
const knot_dname_t *zone_name = zone->apex->owner;
int r = kdnssec_ctx_init(ctx, zone_name);
if (r != KNOT_EOK) {
return r;
}
......@@ -60,6 +102,15 @@ static int sign_init(const zone_contents_t *zone, int flags, kdnssec_ctx_t *ctx)
update_policy_from_zone(ctx->policy, zone);
// initialize NSEC3 salt
if (!ctx->legacy) {
r = salt_init(ctx);
if (r != KNOT_EOK) {
return r;
}
}
// RRSIG handling
ctx->rrsig_drop_existing = flags & ZONE_SIGN_DROP_SIGNATURES;
......@@ -70,7 +121,7 @@ static int sign_init(const zone_contents_t *zone, int flags, kdnssec_ctx_t *ctx)
if (flags & ZONE_SIGN_KEEP_SOA_SERIAL) {
ctx->new_serial = ctx->old_serial;
} else {
val = conf_zone_get(conf(), C_SERIAL_POLICY, zone_name);
conf_val_t val = conf_zone_get(conf(), C_SERIAL_POLICY, zone_name);
ctx->new_serial = serial_next(ctx->old_serial, conf_opt(&val));
}
......@@ -139,7 +190,8 @@ static uint32_t schedule_next(kdnssec_ctx_t *kctx, const zone_keyset_t *keyset,
// DNSKEY modification
uint32_t dnskey_update = MIN(MAX(knot_get_next_zone_key_event(keyset), 0), UINT32_MAX);
uint32_t dnskey_update = MIN(MAX(knot_get_next_zone_key_event(keyset), 0),
UINT32_MAX);
// zone events
......@@ -174,14 +226,14 @@ int knot_dnssec_zone_sign(zone_contents_t *zone, changeset_t *out_ch,
result = sign_init(zone, flags, &ctx);
if (result != KNOT_EOK) {
log_zone_error(zone_name, "DNSSEC, failed to initialize (%s)",
knot_strerror(result));
knot_strerror(result));
goto done;
}
result = sign_process_events(zone_name, &ctx);
if (result != KNOT_EOK) {
log_zone_error(zone_name, "DNSSEC, failed to process events (%s)",
knot_strerror(result));
knot_strerror(result));
goto done;
}
......@@ -197,7 +249,8 @@ int knot_dnssec_zone_sign(zone_contents_t *zone, changeset_t *out_ch,
result = knot_zone_create_nsec_chain(zone, out_ch, &keyset, &ctx);
if (result != KNOT_EOK) {
log_zone_error(zone_name, "DNSSEC, failed to create NSEC chain (%s)",
log_zone_error(zone_name, "DNSSEC, failed to create NSEC%s chain (%s)",
ctx.policy->nsec3_enabled ? "3" : "",
knot_strerror(result));
goto done;
}
......@@ -257,7 +310,7 @@ int knot_dnssec_sign_changeset(const zone_contents_t *zone,
result = sign_init(zone, ZONE_SIGN_KEEP_SOA_SERIAL, &ctx);
if (result != KNOT_EOK) {
log_zone_error(zone_name, "DNSSEC, failed to initialize (%s)",
knot_strerror(result));
knot_strerror(result));
goto done;
}
......@@ -278,7 +331,8 @@ int knot_dnssec_sign_changeset(const zone_contents_t *zone,
result = knot_zone_create_nsec_chain(zone, out_ch, &keyset, &ctx);
if (result != KNOT_EOK) {
log_zone_error(zone_name, "DNSSEC, failed to create NSEC chain (%s)",
log_zone_error(zone_name, "DNSSEC, failed to create NSEC%s chain (%s)",
ctx.policy->nsec3_enabled ? "3" : "",
knot_strerror(result));
goto done;
}
......
......@@ -22,10 +22,12 @@
#include "libknot/rrtype/soa.h"
#include "knot/dnssec/nsec-chain.h"
#include "knot/dnssec/nsec3-chain.h"
#include "knot/dnssec/rrset-sign.h"
#include "knot/dnssec/zone-nsec.h"
#include "knot/dnssec/zone-sign.h"
#include "knot/zone/zone-diff.h"
#include "contrib/base32hex.h"
#include "contrib/wire_ctx.h"
/*!
* \brief Deletes NSEC3 chain if NSEC should be used.
......@@ -203,12 +205,142 @@ knot_dname_t *knot_create_nsec3_owner(const knot_dname_t *owner,
return result;
}
static int set_nsec3param(knot_rrset_t *rrset, const kdnssec_ctx_t *dnssec_ctx)
{
assert(rrset);
assert(dnssec_ctx);
dnssec_nsec3_params_t *new_params = &dnssec_ctx->policy->nsec3_params;
dnssec_binary_t *new_salt = dnssec_ctx->zone->nsec3_salt;
// Prepare wire rdata.
size_t rdata_len = 3 * sizeof(uint8_t) + sizeof(uint16_t) + new_salt->size;
uint8_t rdata[rdata_len];
wire_ctx_t wire = wire_ctx_init(rdata, rdata_len);
wire_ctx_write_u8(&wire, new_params->algorithm);
wire_ctx_write_u8(&wire, new_params->flags);
wire_ctx_write_u16(&wire, new_params->iterations);
wire_ctx_write_u8(&wire, new_salt->size);
wire_ctx_write(&wire, new_salt->data, new_salt->size);
if (wire.error != KNOT_EOK) {
return wire.error;
}
return knot_rrset_add_rdata(rrset, rdata, rdata_len, 0, NULL);
}
static bool match_nsec3param(const knot_nsec3_params_t *params,
const kdnssec_ctx_t *dnssec_ctx)
{
assert(params);
assert(dnssec_ctx);
dnssec_nsec3_params_t *new_params = &dnssec_ctx->policy->nsec3_params;
dnssec_binary_t *new_salt = dnssec_ctx->zone->nsec3_salt;
return params->algorithm == new_params->algorithm &&
params->flags == new_params->flags &&
params->iterations == new_params->iterations &&
params->salt_length == new_salt->size &&
memcmp(params->salt, new_salt->data, new_salt->size) == 0;
}
static int update_nsec3param(const zone_contents_t *zone,
const kdnssec_ctx_t *dnssec_ctx,
changeset_t *changeset)
{
assert(zone);
assert(dnssec_ctx);
assert(changeset);
dnssec_kasp_policy_t *policy = dnssec_ctx->policy;
knot_rdataset_t *nsec3param = node_rdataset(zone->apex, KNOT_RRTYPE_NSEC3PARAM);
// Check for changed NSEC3PARAM record.
bool changed = false;
if (nsec3param != NULL && policy->nsec3_enabled &&
match_nsec3param(&zone->nsec3_params, dnssec_ctx)) {
changed = true;
}
// Remove redundant or changed NSEC3PARAM record and its RRSIG.
if ((nsec3param != NULL && !policy->nsec3_enabled) || changed) {
knot_rrset_t rrset = node_rrset(zone->apex, KNOT_RRTYPE_NSEC3PARAM);
int ret = changeset_rem_rrset(changeset, &rrset, 0);
if (ret != KNOT_EOK) {
return ret;
}
rrset = node_rrset(zone->apex, KNOT_RRTYPE_RRSIG);
if (!knot_rrset_empty(&rrset)) {
knot_rrset_t synth_rrsig;
knot_rrset_init(&synth_rrsig, zone->apex->owner,
KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN);
ret = knot_synth_rrsig(KNOT_RRTYPE_NSEC3PARAM, &rrset.rrs,
&synth_rrsig.rrs, NULL);
if (ret != KNOT_EOK) {
return ret;
}
ret = changeset_rem_rrset(changeset, &synth_rrsig, 0);
knot_rdataset_clear(&synth_rrsig.rrs, NULL);
if (ret != KNOT_EOK) {
return ret;
}
}
// Also remove the record from the zone.
knot_rdataset_clear(nsec3param, NULL);
node_remove_rdataset(zone->apex, KNOT_RRTYPE_NSEC3PARAM);
// Reset zone nsec3 paramaters.
knot_nsec3param_free((knot_nsec3_params_t *)&zone->nsec3_params);
memset((knot_nsec3_params_t *)&zone->nsec3_params, 0,
sizeof(knot_nsec3_params_t));
}
// Add new NSEC3PARAM record.
if ((nsec3param == NULL && policy->nsec3_enabled) || changed) {
knot_rrset_t *rrset = knot_rrset_new(zone->apex->owner,
KNOT_RRTYPE_NSEC3PARAM,
KNOT_CLASS_IN, NULL);
if (rrset == NULL) {
return KNOT_ENOMEM;
}
int ret = set_nsec3param(rrset, dnssec_ctx);
if (ret != KNOT_EOK) {
knot_rrset_free(&rrset, NULL);
return ret;
}
ret = changeset_add_rrset(changeset, rrset, 0);
if (ret != KNOT_EOK) {
knot_rrset_free(&rrset, NULL);
return ret;
}
// Update zone nsec3 paramaters.
ret = knot_nsec3param_from_wire((knot_nsec3_params_t *)&zone->nsec3_params,
&rrset->rrs);
knot_rrset_free(&rrset, NULL);
if (ret != KNOT_EOK) {
return ret;
}
}
return KNOT_EOK;
}
int knot_zone_create_nsec_chain(const zone_contents_t *zone,
changeset_t *changeset,
const zone_keyset_t *zone_keys,
const kdnssec_ctx_t *dnssec_ctx)
{
if (zone == NULL || changeset == NULL) {
if (zone == NULL || changeset == NULL || dnssec_ctx == NULL) {
return KNOT_EINVAL;
}
......@@ -216,31 +348,39 @@ int knot_zone_create_nsec_chain(const zone_contents_t *zone,
if (soa == NULL) {
return KNOT_EINVAL;
}
uint32_t nsec_ttl = knot_soa_minimum(soa);
bool nsec3_enabled = dnssec_ctx->policy->nsec3_enabled;
int ret;
// Update NSEC3PARAM record.
if (!dnssec_ctx->legacy) {
int ret = update_nsec3param(zone, dnssec_ctx, changeset);
if (ret != KNOT_EOK) {
return ret;
}
}
if (nsec3_enabled) {
ret = knot_nsec3_create_chain(zone, nsec_ttl, changeset);
if (dnssec_ctx->policy->nsec3_enabled) {
int ret = knot_nsec3_create_chain(zone, nsec_ttl, changeset);
if (ret != KNOT_EOK) {
return ret;
}
} else {
ret = knot_nsec_create_chain(zone, nsec_ttl, changeset);
}
int ret = knot_nsec_create_chain(zone, nsec_ttl, changeset);
if (ret != KNOT_EOK) {
return ret;
}
if (ret == KNOT_EOK && !nsec3_enabled) {
ret = delete_nsec3_chain(zone, changeset);
}
if (ret != KNOT_EOK) {
return ret;
}
if (ret == KNOT_EOK) {
// Mark removed NSEC3 nodes, so that they are not signed later
// Mark removed NSEC3 nodes, so that they are not signed later.
ret = mark_removed_nsec3(zone, changeset);
if (ret != KNOT_EOK) {
return ret;
}
}
if (ret != KNOT_EOK) {
return ret;
}
// Sign newly created records right away
// Sign newly created records right away.
return knot_zone_sign_nsecs_in_changeset(zone_keys, dnssec_ctx, changeset);
}
......@@ -1280,7 +1280,8 @@ int knot_zone_sign_nsecs_in_changeset(const zone_keyset_t *zone_keys,
knot_rrset_t rr = changeset_iter_next(&itt);
while (!knot_rrset_empty(&rr)) {
if (rr.type == KNOT_RRTYPE_NSEC ||
rr.type == KNOT_RRTYPE_NSEC3) {
rr.type == KNOT_RRTYPE_NSEC3 ||
rr.type == KNOT_RRTYPE_NSEC3PARAM) {
int ret = add_missing_rrsigs(&rr, NULL, zone_keys,
dnssec_ctx, changeset);
if (ret != KNOT_EOK) {
......
......@@ -883,6 +883,7 @@ static int load_nsec3param(zone_contents_t *contents)
const knot_rdataset_t *rrs = node_rdataset(contents->apex,
KNOT_RRTYPE_NSEC3PARAM);
if (rrs == NULL) {
knot_nsec3param_free(&contents->nsec3_params);
memset(&contents->nsec3_params, 0, sizeof(knot_nsec3_params_t));
return KNOT_EOK;
}
......
......@@ -81,5 +81,9 @@ int knot_nsec3param_from_wire(knot_nsec3_params_t *params,
static inline
void knot_nsec3param_free(knot_nsec3_params_t *params)
{
if (params == NULL) {
return;
}
free(params->salt);
}
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