Skip to content
Snippets Groups Projects
Commit 7fa8ad0b authored by Libor Peltan's avatar Libor Peltan
Browse files

Merge branch 'oldkeyremoval' into 'master'

dnssec: remove retired keys

Closes #343

See merge request !597
parents d9114821 2a976193
Branches
Tags
1 merge request!597dnssec: remove retired keys
......@@ -438,3 +438,15 @@ of the limitations will be hopefully removed in the near future.
- Legacy key import requires a private key.
- Legacy key export is not implemented.
- DS record export is not implemented.
.. _dnssec-keyusage:
DNSSEC keys used by multiple zones
----------------------------------
Using same key for multiple zones with automatic key management is possible.
However, all zones must be listed in keyusage (keys directory) or they will be deleted,
when they retire in any zone.
If keys are added manually as published, but not active (for next rollover event), they are added automatically.
......@@ -65,6 +65,7 @@ include_dnssec_HEADERS = \
lib/dnssec/keyid.h \
lib/dnssec/keystate.h \
lib/dnssec/keystore.h \
lib/dnssec/keyusage.h \
lib/dnssec/keytag.h \
lib/dnssec/list.h \
lib/dnssec/nsec.h \
......@@ -124,6 +125,7 @@ libdnssec_la_SOURCES = \
lib/keystore/pkcs11.c \
lib/keystore/pkcs8.c \
lib/keystore/pkcs8_dir.c \
lib/keyusage/keyusage.c \
lib/list/list.c \
lib/list/ucw_clists.h \
lib/nsec/bitmap.c \
......
/* 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/>.
*/
#pragma once
#include <dnssec/list.h>
typedef struct record {
char *keytag;
dnssec_list_t *zones;
} record_keyusage_t;
typedef dnssec_list_t dnssec_keyusage_t;
int dnssec_keyusage_add(dnssec_keyusage_t *keyusage, const char *keytag, char *zone);
int dnssec_keyusage_remove(dnssec_keyusage_t *keyusage, const char *keytag, char *zone);
bool dnssec_keyusage_is_used(dnssec_keyusage_t *keyusage, const char *keytag);
int dnssec_keyusage_load(dnssec_keyusage_t *keyusage, const char *filename);
int dnssec_keyusage_save(dnssec_keyusage_t *keyusage, const char *filename);
dnssec_keyusage_t *dnssec_keyusage_new(void);
void dnssec_keyusage_free(dnssec_keyusage_t *keyusage);
......@@ -22,6 +22,7 @@
#include "shared.h"
#include "event/action.h"
#include "event/utils.h"
#include "dnssec/keyusage.h"
/*!
* Scan zone keys and check if ZSK and KSK key exists.
......@@ -58,6 +59,19 @@ static int generate_initial_key(dnssec_event_ctx_t *ctx, bool ksk)
return r;
}
if (!ksk) {
char *path;
if (asprintf(&path, "%s/keyusage", ctx->kasp->functions->base_path(ctx->kasp->ctx)) == -1){
return DNSSEC_ENOMEM;
}
dnssec_keyusage_t *keyusage = dnssec_keyusage_new();
dnssec_keyusage_load(keyusage, path);
dnssec_keyusage_add(keyusage, key->id, ctx->zone->name);
dnssec_keyusage_save(keyusage, path);
dnssec_keyusage_free(keyusage);
free(path);
}
key->timing.active = ctx->now;
key->timing.publish = ctx->now;
......
......@@ -18,6 +18,7 @@
#include "dnssec/error.h"
#include "dnssec/event.h"
#include "dnssec/keyusage.h"
#include "event/action.h"
#include "dnssec/keystate.h"
#include "event/utils.h"
......@@ -169,6 +170,17 @@ static int exec_new_signatures(dnssec_event_ctx_t *ctx)
active->timing.retire = ctx->now;
rolling->timing.active = ctx->now;
char *path;
if (asprintf(&path, "%s/keyusage", ctx->kasp->functions->base_path(ctx->kasp->ctx)) == -1){
return DNSSEC_ENOMEM;
}
dnssec_keyusage_t *keyusage = dnssec_keyusage_new();
dnssec_keyusage_load(keyusage, path);
dnssec_keyusage_add(keyusage, rolling->id, ctx->zone->name);
dnssec_keyusage_save(keyusage, path);
dnssec_keyusage_free(keyusage);
free(path);
return dnssec_kasp_zone_save(ctx->kasp, ctx->zone);
}
......@@ -179,7 +191,33 @@ static int exec_remove_old_key(dnssec_event_ctx_t *ctx)
return DNSSEC_EINVAL;
}
char *path;
if (asprintf(&path, "%s/keyusage", ctx->kasp->functions->base_path(ctx->kasp->ctx)) == -1){
return DNSSEC_ENOMEM;
}
dnssec_keyusage_t *keyusage = dnssec_keyusage_new();
dnssec_keyusage_load(keyusage, path);
dnssec_keyusage_remove(keyusage, retired->id, ctx->zone->name);
dnssec_keyusage_save(keyusage, path);
retired->timing.remove = ctx->now;
dnssec_list_foreach(item, ctx->zone->keys) {
dnssec_kasp_key_t *key = dnssec_item_get(item);
if (key->id == retired->id) {
dnssec_list_remove(item);
}
}
if (dnssec_keyusage_is_used(keyusage, retired->id)) {
dnssec_keyusage_free(keyusage);
free(path);
return dnssec_kasp_zone_save(ctx->kasp, ctx->zone);
}
dnssec_keyusage_free(keyusage);
free(path);
dnssec_keystore_remove_key(ctx->keystore, retired->id);
return dnssec_kasp_zone_save(ctx->kasp, ctx->zone);
}
......
/* Copyright (C) 2015 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/keyusage.h"
#include "error.h"
#include "string.h"
#include "dnssec.h"
#include <assert.h>
#include "error.h"
#include "kasp/dir/json.h"
#include "kasp/internal.h"
#include "shared.h"
#include <string.h>
int dnssec_keyusage_add(dnssec_keyusage_t *keyusage, const char *keytag, char *zone) {
char *lzone = strdup(zone);
dnssec_list_foreach(item, keyusage) {
record_keyusage_t *record = dnssec_item_get(item);
if (strcmp(record->keytag, keytag) == 0) {
if(!dnssec_list_contains(record->zones, lzone)) {
dnssec_list_append(record->zones, lzone);
}
return DNSSEC_EOK;
}
}
record_keyusage_t *record = malloc(sizeof(*record));
record->keytag = strdup(keytag);
record->zones = dnssec_list_new();
dnssec_list_append(record->zones, lzone);
dnssec_list_append(keyusage, record);
return DNSSEC_EOK;
}
int dnssec_keyusage_remove(dnssec_keyusage_t *keyusage, const char *keytag, char *zone) {
dnssec_list_foreach(item, keyusage) {
record_keyusage_t *record = dnssec_item_get(item);
if (strcmp(record->keytag, keytag) == 0) {
dnssec_list_foreach(item2, record->zones) {
char *tmp = dnssec_item_get(item2);
if(strcmp(tmp,zone)==0) {
free(tmp);
dnssec_list_remove(item2);
if (dnssec_list_is_empty(record->zones)) {
free(record->keytag);
dnssec_list_free(record->zones);
free(record);
dnssec_list_remove(item);
}
return DNSSEC_EOK;
}
}
}
}
return DNSSEC_ENOENT;
}
bool dnssec_keyusage_is_used(dnssec_keyusage_t *keyusage, const char *keytag) {
if (dnssec_list_is_empty(keyusage)) {
return false;
}
dnssec_list_foreach(item, keyusage) {
record_keyusage_t *record = dnssec_item_get(item);
if(record == NULL || record->keytag == NULL) {
return false;
}
if (strcmp(record->keytag, keytag) == 0) {
return !dnssec_list_is_empty(record->zones);
}
}
return false;
}
static int import_keyusage(dnssec_keyusage_t *keyusage, const json_t *json)
{
json_t *jrecord = NULL;
int a, b;
if (!json_is_array(json)) {
if (json_is_null(json)) {
return DNSSEC_EOK;
} else {
return DNSSEC_CONFIG_MALFORMED;
}
}
json_array_foreach(json, a, jrecord) {
json_t *jkeytag = NULL;
jkeytag = json_object_get(jrecord, "keytag");
record_keyusage_t *record = malloc(sizeof(*record));
if (record == NULL) {
return DNSSEC_ENOMEM;
}
int r = decode_string(jkeytag, &record->keytag);
if (r != DNSSEC_EOK) {
return r;
}
json_t *jzones = NULL, *jzone = NULL;
jzones = json_object_get(jrecord, "zones");
record->zones = dnssec_list_new();
json_array_foreach(jzones, b, jzone) {
char *zone;
int r = decode_string(jzone, &zone);
if (r != DNSSEC_EOK) {
return r;
}
dnssec_list_append(record->zones, zone);
}
dnssec_list_append(keyusage, record);
}
return DNSSEC_EOK;
}
static int export_keyusage(dnssec_keyusage_t *keyusage, json_t **json)
{
assert(keyusage);
assert(json);
record_keyusage_t *record;
int r;
if (dnssec_list_is_empty(keyusage)) {
return DNSSEC_EOK;
}
json_t *jrecords = json_array();
if (!jrecords) {
return DNSSEC_ENOMEM;
}
json_t *jkeytag = NULL;
json_t *jzone = NULL;
json_t *jzones = NULL;
dnssec_list_foreach(item, keyusage) {
record = dnssec_item_get(item);
json_t *jzones = json_array();
if (!jzones) {
json_array_clear(jrecords);
return DNSSEC_ENOMEM;
}
r = encode_string(&record->keytag, &jkeytag);
if (r != DNSSEC_EOK) {
json_decref(jkeytag);
goto error;
}
dnssec_list_foreach(item, record->zones) {
const char *zone = dnssec_item_get(item);
r = encode_string(&zone, &jzone);
if (r != DNSSEC_EOK) {
json_decref(jzone);
goto error;
}
if (json_array_append_new(jzones, jzone)) {
r = DNSSEC_ENOMEM;
goto error;
}
}
json_t *jrecord = json_object();
if (!jrecord) {
r = DNSSEC_ENOMEM;
goto error;
}
if (json_object_set(jrecord, "keytag",jkeytag)) {
json_object_clear(jrecord);
r = DNSSEC_ENOMEM;
goto error;
}
json_decref(jkeytag);
if (json_object_set(jrecord, "zones",jzones)) {
json_object_clear(jrecord);
r = DNSSEC_ENOMEM;
goto error;
}
json_decref(jzones);
if (json_array_append_new(jrecords, jrecord)) {
json_object_clear(jrecord);
r = DNSSEC_ENOMEM;
goto error;
}
}
*json = jrecords;
return DNSSEC_EOK;
error:
json_array_clear(jzones);
json_array_clear(jrecords);
return r;
}
int dnssec_keyusage_load(dnssec_keyusage_t *keyusage, const char *filename)
{
assert(keyusage);
assert(filename);
dnssec_list_clear(keyusage);
_cleanup_fclose_ FILE *file = fopen(filename, "r");
if (!file) {
return DNSSEC_NOT_FOUND;
}
json_error_t error = { 0 };
_json_cleanup_ json_t *json = json_loadf(file, JSON_LOAD_OPTIONS, &error);
if (!json) {
if (error.position != 1) {
return DNSSEC_CONFIG_MALFORMED;
} else {
return DNSSEC_EOK;
}
}
return import_keyusage(keyusage, json);
}
int dnssec_keyusage_save(dnssec_keyusage_t *keyusage, const char *filename)
{
assert(keyusage);
assert(filename);
_json_cleanup_ json_t *json = NULL;
int r = export_keyusage(keyusage, &json);
if (r != DNSSEC_EOK) {
return r;
}
_cleanup_fclose_ FILE *file = fopen(filename, "w");
if (!file) {
return DNSSEC_NOT_FOUND;
}
if (json)
{
r = json_dumpf(json, file, JSON_DUMP_OPTIONS);
if (r != DNSSEC_EOK) {
return r;
}
}
fputc('\n', file);
return DNSSEC_EOK;
}
/* -- public API ----------------------------------------------------------- */
_public_
dnssec_keyusage_t *dnssec_keyusage_new()
{
dnssec_keyusage_t *keyusage = dnssec_list_new();
return keyusage;
}
_public_
void dnssec_keyusage_free(dnssec_keyusage_t *keyusage)
{
if (keyusage == NULL) {
return;
}
if (!dnssec_list_is_empty(keyusage)) {
dnssec_list_foreach(item, keyusage) {
record_keyusage_t *record = dnssec_item_get(item);
free(record->keytag);
dnssec_list_foreach(item2, record->zones) {
char *zone = dnssec_item_get(item2);
free(zone);
}
dnssec_list_free(record->zones);
free(record);
}
}
dnssec_list_free(keyusage);
keyusage = NULL;
}
......@@ -9,6 +9,7 @@
/event_nsec3_resalt
/kasp_dir_escape
/kasp_dir_file
/keyusage
/kasp_policy
/kasp_store
/key
......
......@@ -34,6 +34,7 @@ check_PROGRAMS = \
keystore_pkcs8 \
keystore_pkcs8_dir \
keytag \
keyusage \
list \
nsec_bitmap \
nsec_hash \
......
/* 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 <stdio.h>
#include "dnssec/keyusage.h"
#include "dnssec/error.h"
#include "dnssec.h"
static void test_keyusage_empty(void)
{
diag("%s", __func__);
dnssec_keyusage_t *k = dnssec_keyusage_new();
remove("/tmp/keyusage.json");
ok(dnssec_keyusage_load(k, "/tmp/keyusage.json")== DNSSEC_NOT_FOUND, "kayusage_load empty, no keyusage");
ok(dnssec_list_is_empty(k), "List of records is empty.");
ok(dnssec_keyusage_save(k, "/tmp/keyusage.json") == DNSSEC_EOK , "kayusage_save empty");
ok(dnssec_keyusage_load(k, "/tmp/keyusage.json")== DNSSEC_EOK , "kayusage_load empty");
ok(dnssec_list_is_empty(k), "List of records is empty.");
dnssec_keyusage_free(k);
}
static void test_keyusage_basic(void)
{
diag("%s", __func__);
dnssec_keyusage_t *k = NULL;
k = dnssec_keyusage_new();
ok(dnssec_list_is_empty(k), "List of records is empty.");
dnssec_keyusage_add(k, "prvni", "zona");
ok(!dnssec_list_is_empty(k), "List of records is not empty.");
// Check added key being used
ok(dnssec_keyusage_is_used(k, "prvni"), "Key is used.");
// Remove added key and check empty record
dnssec_keyusage_remove(k, "prvni", "zona");
ok(dnssec_list_is_empty(k), "List of records is empty.");
ok(!dnssec_keyusage_is_used(k, "prvni"), "Key is not used.");
// Check if key is used when one of zones is deleted
dnssec_keyusage_add(k, "prvni", "zona");
dnssec_keyusage_add(k, "prvni", "zona2");
dnssec_keyusage_remove(k, "prvni", "zona");
ok(dnssec_keyusage_is_used(k, "prvni"), "Key is used.");
// Check if key is unused after deleting both zones
dnssec_keyusage_remove(k, "prvni", "zona2");
ok(!dnssec_keyusage_is_used(k, "prvni"), "Key is not used.");
dnssec_keyusage_free(k);
}
static void test_keyusage_file(void)
{
dnssec_keyusage_t *k = NULL;
k = dnssec_keyusage_new();
dnssec_keyusage_add(k, "prvni", "zona");
dnssec_keyusage_add(k, "prvni", "zona2");
dnssec_keyusage_add(k, "druhy", "zona");
ok(dnssec_keyusage_save(k, "/tmp/keyusage.json") == DNSSEC_EOK , "kayusage_save");
dnssec_keyusage_free(k);
k = dnssec_keyusage_new();
ok(!dnssec_keyusage_is_used(k, "prvni"), "Key is not used - freed succesfully.");
ok(!dnssec_keyusage_is_used(k, "druhy"), "Key is not used - freed succesfully.");
ok(dnssec_keyusage_load(k, "/tmp/keyusage.json")== DNSSEC_EOK , "kayusage_load");
ok(dnssec_keyusage_is_used(k, "prvni"), "Key is used - loaded succesfully.");
ok(dnssec_keyusage_is_used(k, "druhy"), "Key is used - loaded succesfully.");
dnssec_keyusage_free(k);
}
int main(int argc, char *argv[])
{
plan_lazy();
test_keyusage_empty();
test_keyusage_basic();
test_keyusage_file();
return 0;
}
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