Newer
Older
/* Copyright (C) 2013 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 <config.h>
#include <assert.h>
#include "knot/conf/conf.h"
#include "knot/server/zones.h"
#include "libknot/dnssec/zone-events.h"
#include "libknot/dnssec/zone-keys.h"
#include "libknot/dnssec/zone-nsec.h"
#include "libknot/dnssec/zone-sign.h"
static uint32_t time_now(void)
{
return (uint32_t)time(NULL);
}
static void init_default_policy(knot_dnssec_policy_t *p,
knot_update_serial_t soa_up)
Jan Kadlec
committed
knot_dnssec_policy_t p_image = DEFAULT_DNSSEC_POLICY;
memcpy(p, &p_image, sizeof(knot_dnssec_policy_t));
Jan Kadlec
committed
}
static void init_forced_policy(knot_dnssec_policy_t *p,
knot_update_serial_t soa_up)
Jan Kadlec
committed
{
knot_dnssec_policy_t p_image = FORCED_DNSSEC_POLICY;
memcpy(p, &p_image, sizeof(knot_dnssec_policy_t));
Jan Kadlec
committed
}
static int init_dnssec_structs(const knot_zone_t *zone,
knot_zone_keys_t *zone_keys,
knot_dnssec_policy_t *policy,
knot_update_serial_t soa_up, bool force)
{
assert(zone);
assert(zone_keys);
assert(policy);
// Read zone keys from disk
bool nsec3_enabled = is_nsec3_enabled(zone->contents);
int result = load_zone_keys(conf()->dnssec_keydir,
zone->contents->apex->owner,
nsec3_enabled, zone_keys);
if (result != KNOT_EOK) {
char *zname = knot_dname_to_str(zone->name);
log_zone_error("DNSSEC keys could not be loaded (%s). "
"Not signing the %s zone!\n",
knot_strerror(result), zname);
free(zname);
free_zone_keys(zone_keys);
return result;
}
// Init sign policy
if (force) {
init_forced_policy(policy, soa_up);
} else {
init_default_policy(policy, soa_up);
}
// Override signature lifetime, if set in config
zonedata_t *zd = (zonedata_t *)zone->data;
pthread_mutex_lock(&zd->lock);
int sig_lf = zd->conf->sig_lifetime;
pthread_mutex_unlock(&zd->lock);
if (sig_lf > 0) {
policy->sign_lifetime = sig_lf;
}
static int zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch, bool force,
knot_update_serial_t soa_up, uint32_t *expires_at)
Jan Kadlec
committed
{
assert(zone);
assert(zone->contents);
assert(out_ch);
dbg_dnssec_verb("Changeset empty before generating NSEC chain: %d\n",
knot_changeset_is_empty(out_ch));
Jan Kadlec
committed
conf_zone_t *zone_config = ((zonedata_t *)knot_zone_data(zone))->conf;
if (!zone_config->dnssec_enable) {
char *zname = knot_dname_to_str(zone->name);
log_server_warning("DNSSEC not enabled for '%s'.\n", zname);
free(zname);
return KNOT_EOK;
// Init needed structs
knot_dnssec_policy_t policy = { '\0' };
int result = init_dnssec_structs(zone, &zone_keys, &policy, soa_up,
force);
log_zone_error("Failed to init DNSSEC signer (%s)\n",
knot_strerror(result));
Jan Kadlec
committed
// generate NSEC records
result = knot_zone_create_nsec_chain(zone->contents, out_ch,
&zone_keys, &policy);
if (result != KNOT_EOK) {
Jan Kadlec
committed
char *zname = knot_dname_to_str(zone->name);
log_zone_error("Could not create NSEC(3) chain (%s). "
"Not signing the %s zone!\n",
knot_strerror(result), zname);
free(zname);
return result;
}
dbg_dnssec_verb("Changeset empty after generating NSEC chain: %d\n",
knot_changeset_is_empty(out_ch));
Jan Kadlec
committed
// add missing signatures
result = knot_zone_sign(zone->contents, &zone_keys, &policy, out_ch,
expires_at);
Jan Kadlec
committed
if (result != KNOT_EOK) {
char *zname = knot_dname_to_str(zone->name);
log_zone_error("Error signing zone %s (%s).\n",
zname, knot_strerror(result));
free(zname);
Jan Kadlec
committed
return result;
dbg_dnssec_verb("Changeset emtpy after signing: %d\n",
knot_changeset_is_empty(out_ch));
Jan Kadlec
committed
// Check if only SOA changed
if (knot_changeset_is_empty(out_ch) &&
!knot_zone_sign_soa_expired(zone->contents, &zone_keys, &policy)) {
char *zname = knot_dname_to_str(zone->name);
log_server_info("No signing performed, zone %s is valid.\n",
zname);
free(zname);
Jan Kadlec
committed
return KNOT_EOK;
Jan Kadlec
committed
// update SOA if there were any changes
const knot_rrset_t *soa = knot_node_rrset(zone->contents->apex,
KNOT_RRTYPE_SOA);
assert(soa);
result = knot_zone_sign_update_soa(soa, &zone_keys, &policy,
Jan Kadlec
committed
out_ch);
if (result != KNOT_EOK) {
Jan Kadlec
committed
char *zname = knot_dname_to_str(zone->name);
log_server_error("Cannot update SOA record (%s)."
" Not signing the %s zone!\n",
knot_strerror(result), zname);
free(zname);
return result;
}
dbg_dnssec_detail("Zone signed: changes=%zu\n",
knot_changeset_size(out_ch));
return KNOT_EOK;
}
Jan Kadlec
committed
int knot_dnssec_zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch,
knot_update_serial_t soa_up, uint32_t *expires_at)
Jan Kadlec
committed
{
if (zone == NULL || zone->contents == NULL || out_ch == NULL) {
return KNOT_EINVAL;
}
return zone_sign(zone, out_ch, false, soa_up, expires_at);
Jan Kadlec
committed
}
int knot_dnssec_zone_sign_force(knot_zone_t *zone,
Jan Kadlec
committed
{
if (zone == NULL || zone->contents == NULL || out_ch == NULL) {
return KNOT_EINVAL;
}
return zone_sign(zone, out_ch, true, KNOT_SOA_SERIAL_INC, expires_at);
Jan Kadlec
committed
}
int knot_dnssec_sign_changeset(const knot_zone_contents_t *zone,
const knot_changeset_t *in_ch,
knot_changeset_t *out_ch,
knot_update_serial_t soa_up)
{
if (!conf()->dnssec_enable) {
return KNOT_EOK;
}
if (zone == NULL || in_ch == NULL || out_ch == NULL) {
return KNOT_EINVAL;
}
// Init needed structures
knot_zone_keys_t zone_keys = { '\0' };
knot_dnssec_policy_t policy = { '\0' };
int ret = init_dnssec_structs(zone->zone, &zone_keys, &policy, soa_up,
false);
if (ret != KNOT_EOK) {
log_zone_error("Failed to init DNSSEC signer (%s)\n",
knot_strerror(ret));
return ret;
// Fix NSEC(3) chain
ret = knot_zone_create_nsec_chain(zone, out_ch, &zone_keys, &policy);
if (ret != KNOT_EOK) {
log_zone_error("Failed to fix NSEC(3) chain (%s)\n",
knot_strerror(ret));
free_zone_keys(&zone_keys);
return ret;
}
// Sign added and removed RRSets in changeset
ret = knot_zone_sign_changeset(zone, in_ch, out_ch, &zone_keys,
&policy);
if (ret != KNOT_EOK) {
log_zone_error("Failed to sign changeset (%s)\n",
knot_strerror(ret));
free_zone_keys(&zone_keys);
return ret;
}
// Update SOA RRSIGs
ret = knot_zone_sign_update_soa(knot_node_rrset(zone->apex,
KNOT_RRTYPE_SOA),
&zone_keys, &policy,
out_ch);
if (ret != KNOT_EOK) {
log_zone_error("Failed to sign SOA RR (%s)\n",
knot_strerror(ret));
}
free_zone_keys(&zone_keys);
return ret;
}