Commit b4fbb11b authored by Libor Peltan's avatar Libor Peltan

kasp preparation: introduced kasp db and kasp zone incl test

parent 36a9a2a7
......@@ -234,6 +234,10 @@ src/knot/ctl/process.c
src/knot/ctl/process.h
src/knot/dnssec/context.c
src/knot/dnssec/context.h
src/knot/dnssec/kasp/kasp_db.c
src/knot/dnssec/kasp/kasp_db.h
src/knot/dnssec/kasp/kasp_zone.c
src/knot/dnssec/kasp/kasp_zone.h
src/knot/dnssec/nsec-chain.c
src/knot/dnssec/nsec-chain.h
src/knot/dnssec/nsec3-chain.c
......@@ -600,6 +604,7 @@ tests/test_confio.c
tests/test_dthreads.c
tests/test_fdset.c
tests/test_journal.c
tests/test_kasp_db.c
tests/test_node.c
tests/test_process_query.c
tests/test_query_module.c
......
......@@ -247,6 +247,10 @@ libknotd_la_SOURCES = \
knot/ctl/process.h \
knot/dnssec/context.c \
knot/dnssec/context.h \
knot/dnssec/kasp/kasp_db.c \
knot/dnssec/kasp/kasp_db.h \
knot/dnssec/kasp/kasp_zone.c \
knot/dnssec/kasp/kasp_zone.h \
knot/dnssec/nsec-chain.c \
knot/dnssec/nsec-chain.h \
knot/dnssec/nsec3-chain.c \
......
This diff is collapsed.
/* Copyright (C) 2017 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 <time.h>
#include "contrib/ucw/lists.h"
#include "dnssec/lib/dnssec/kasp.h"
#include "libknot/db/db_lmdb.h"
#include "knot/zone/zone.h"
typedef struct kasp_db kasp_db_t;
/*!
* \brief Returns kasp_db_t singleton, to be used for signing all zones.
*
* De/initialized with server_t, used in zone contents signing context.
*/
kasp_db_t **kaspdb(void);
/*!
* \brief Initialize kasp_db_t, prepare to simple open on-demand.
*
* \param db structure to initialize
* \param path path to the LMDB directory (will be created)
* \param mapsize LMDB map size
*
* \return KNOT_E*
*/
int kasp_db_init(kasp_db_t **db, const char *path, size_t mapsize);
/*!
* \brief Re-initialize kasp_db_t if not already open.
*
* \param db structure to initialize
* \param new_path new path to LMDB
* \param new_mapsize new LMDB map size
*
* \retval KNOT_EBUSY can't reconfigure DB path because already open
* \retval KNOT_EEXIST can't reconfigure mapsize because already open
* \retval KNOT_ENODIFF already open, but no change needed => OK
* \retval KNOT_EINVAL, KNOT_ENOMEM, etc. standard errors
* \return KNOT_EOK reconfigured successfully
*/
int kasp_db_reconfigure(kasp_db_t **db, const char *new_path, size_t new_mapsize);
/*!
* \brief Perform real ctreate/open of KASP db.
*/
int kasp_db_open(kasp_db_t *db);
/*!
* \brief Close KASP db if open and free the structure.
*/
void kasp_db_close(kasp_db_t **db);
/*!
* \brief For given zone, list all keys (their IDs) belonging to it.
*
* \param db KASP db
* \param zone_name name of the zone in question
* \param dst output if KNOT_EOK: ptrlist of strings with keys' IDs
*
* \return KNOT_E* (KNOT_ENOENT if no keys)
*/
int kasp_db_list_keys(kasp_db_t *db, const knot_dname_t *zone_name, list_t *dst);
/*!
* \brief For given key ID, gather the info into params structure.
*
* \param db KASP db
* \param key_id key ID
* \param params output if KNOT_EOK: all key parameters
*
* \return KNOT_E*
*/
int kasp_db_key_params(kasp_db_t *db, const char *key_id, key_params_t *params);
/*!
* \brief Remove a key from zone. Delete the key if no zone has it anymore.
*
* \param db KASP db
* \param zone_name zone to be removed from
* \param key_id ID of key to be removed
* \param still_used output if KNOT_EOK: is the key still in use by other zones?
*
* \return KNOT_E*
*/
int kasp_db_delete_key(kasp_db_t *db, const knot_dname_t *zone_name, const char *key_id, bool *still_used);
/*!
* \brief Add a key to the DB (possibly overwrite) and link it to a zone.
*
* Stores new key with given params into KASP db. If a key with the same ID had been present
* in KASP db already, its params get silently overwritten by those new params.
* Moreover, the key ID is linked to the zone.
*
* \param db KASP db
* \param zone_name name of the zone the new key shall belong to
* \param params key params, incl. ID
*
* \return KNOT_E*
*/
int kasp_db_add_key(kasp_db_t *db, const knot_dname_t *zone_name, const key_params_t *params);
/*!
* \brief Link an existing key with a zone.
*
* The key with this ID must be already present in KASP db.
*
* \param db KASP db
* \param zone_name zone to be linked to
* \param key_id key ID
*
* \return KNOT_E*
*/
int kasp_db_share_key(kasp_db_t *db, const knot_dname_t *zone_name, const char *key_id);
/*!
* \brief Store NSEC3 salt for given zone (possibly overwrites old salt).
*
* \param db KASP db
* \param zone_name zone name
* \param nsec3salt new NSEC3 salt
* \param salt_created timestamp when the salt was created
*
* \return KNOT_E*
*/
int kasp_db_store_nsec3salt(kasp_db_t *db, const knot_dname_t *zone_name,
const dnssec_binary_t *nsec3salt, time_t salt_created);
/*!
* \brief Load NSEC3 salt for given zone.
*
* \param db KASP db
* \param zone_name zone name
* \param nsec3salt output if KNOT_EOK: the zone's NSEC3 salt
* \param salt_created output if KNOT_EOK: timestamp when the salt was created
*
* \return KNOT_E* (KNOT_ENOENT if not stored before)
*/
int kasp_db_load_nsec3salt(kasp_db_t *db, const knot_dname_t *zone_name,
dnssec_binary_t *nsec3salt, time_t *salt_created);
/* Copyright (C) 2017 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 "kasp_zone.h"
#include "dnssec/lib/dnssec/binary.h"
#include "knot/dnssec/zone-keys.h"
// FIXME DNSSEC errors versus knot errors
/*!
* Check if key parameters allow to create a key.
*/
static int key_params_check(key_params_t *params)
{
assert(params);
if (params->algorithm == 0) {
return KNOT_INVALID_KEY_ALGORITHM;
}
if (params->public_key.size == 0) {
return KNOT_NO_PUBLIC_KEY;
}
return KNOT_EOK;
}
static int params2dnskey(const knot_dname_t *dname, key_params_t *params,
dnssec_key_t **key_ptr)
{
assert(dname);
assert(params);
assert(key_ptr);
int result = key_params_check(params);
if (result != KNOT_EOK) {
return result;
}
dnssec_key_t *key = NULL;
result = dnssec_key_new(&key);
if (result != KNOT_EOK) {
return knot_error_from_libdnssec(result);
}
result = dnssec_key_set_dname(key, dname);
if (result != KNOT_EOK) {
dnssec_key_free(key);
return knot_error_from_libdnssec(result);
}
dnssec_key_set_algorithm(key, params->algorithm);
uint16_t flags = dnskey_flags(params->is_ksk);
dnssec_key_set_flags(key, flags);
result = dnssec_key_set_pubkey(key, &params->public_key);
if (result != KNOT_EOK) {
dnssec_key_free(key);
return knot_error_from_libdnssec(result);
}
*key_ptr = key;
return KNOT_EOK;
}
static int params2kaspkey(const knot_dname_t *dname, key_params_t *params,
dnssec_kasp_key_t *key)
{
int ret = params2dnskey(dname, params, &key->key);
if (ret != KNOT_EOK) {
return ret;
}
key->id = strdup(params->id);
if (key->id == NULL) {
dnssec_key_free(key->key);
return KNOT_ENOMEM;
}
key->timing = params->timing;
return KNOT_EOK;
}
static void kaspkey2params(dnssec_kasp_key_t *key, key_params_t *params)
{
assert(key);
assert(params);
params->id = key->id;
params->keytag = dnssec_key_get_keytag(key->key);
dnssec_key_get_pubkey(key->key, &params->public_key);
params->algorithm = dnssec_key_get_algorithm(key->key);
params->is_ksk = dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_KSK;
params->timing = key->timing;
}
static void ptrlist_deep_free(list_t *l)
{
ptrnode_t *n;
WALK_LIST(n, *l) {
free(n->d);
}
ptrlist_free(l, NULL);
}
int kasp_zone_load(knot_kasp_zone_t *zone,
const knot_dname_t *zone_name,
kasp_db_t *kdb)
{
dnssec_kasp_key_t *dkeys = NULL;
size_t num_dkeys = 0;
dnssec_binary_t salt = { 0 };
time_t sc = 0;
list_t key_ids;
init_list(&key_ids);
int ret = kasp_db_list_keys(kdb, zone_name, &key_ids);
if (ret == KNOT_ENOENT) {
zone->keys = NULL;
zone->num_keys = 0;
ret = KNOT_EOK;
goto kzl_salt;
} else if (ret != KNOT_EOK) {
goto kzl_end;
}
num_dkeys = list_size(&key_ids);
dkeys = calloc(num_dkeys, sizeof(*dkeys));
if (dkeys == NULL) {
goto kzl_end;
}
ptrnode_t *n;
key_params_t parm = { 0 };
size_t i = 0;
WALK_LIST(n, key_ids) {
ret = kasp_db_key_params(kdb, n->d, &parm);
if (ret == KNOT_EOK) {
ret = params2kaspkey(zone_name, &parm, &dkeys[i++]);
free(parm.id); // TODO put this into a method
free(parm.public_key.data);
memset(&parm, 0, sizeof(parm));
}
if (ret != KNOT_EOK) {
goto kzl_end;
}
}
assert(i == num_dkeys);
kzl_salt:
(void)kasp_db_load_nsec3salt(kdb, zone_name, &salt, &sc);
// if error, salt was probably not present, no problem to have zero ?
zone->dname = knot_dname_copy(zone_name, NULL);
if (zone->dname == NULL) {
ret = KNOT_ENOMEM;
goto kzl_end;
}
zone->keys = dkeys;
zone->num_keys = num_dkeys;
zone->nsec3_salt = salt;
zone->nsec3_salt_created = sc;
kzl_end:
ptrlist_deep_free(&key_ids);
if (ret != KNOT_EOK) {
free(dkeys);
}
return ret;
}
int kasp_zone_append(knot_kasp_zone_t *zone, const dnssec_kasp_key_t *appkey)
{
size_t new_num_keys = zone->num_keys + 1;
dnssec_kasp_key_t *new_keys = calloc(new_num_keys, sizeof(*new_keys));
if (!new_keys) {
return KNOT_ENOMEM;
}
memcpy(new_keys, zone->keys, zone->num_keys * sizeof(*new_keys));
memcpy(&new_keys[new_num_keys - 1], appkey, sizeof(*appkey));
free(zone->keys);
zone->keys = new_keys;
zone->num_keys = new_num_keys;
return KNOT_EOK;
}
int kasp_zone_save(const knot_kasp_zone_t *zone,
const knot_dname_t *zone_name,
kasp_db_t *kdb)
{
key_params_t parm;
int ret;
for (size_t i = 0; i < zone->num_keys; i++) {
kaspkey2params(&zone->keys[i], &parm);
ret = kasp_db_add_key(kdb, zone_name, &parm);
// force overwrite already existing key-val pairs
if (ret != KNOT_EOK) {
goto kzs_end;
}
}
ret = kasp_db_store_nsec3salt(kdb, zone_name, &zone->nsec3_salt, zone->nsec3_salt_created);
if (ret != KNOT_EOK) {
goto kzs_end;
}
kzs_end:
return ret;
}
int kasp_zone_init(knot_kasp_zone_t **zone)
{
*zone = calloc(1, sizeof(**zone));
return (*zone ? KNOT_EOK : KNOT_ENOMEM);
}
void kasp_zone_clear(knot_kasp_zone_t *zone)
{
if (zone == NULL) {
return;
}
knot_dname_free(&zone->dname, NULL);
for (size_t i = 0; i < zone->num_keys; i++) {
dnssec_key_free(zone->keys[i].key);
free(zone->keys[i].id);
}
free(zone->keys);
free(zone->nsec3_salt.data);
memset(zone, 0, sizeof(*zone));
}
void kasp_zone_free(knot_kasp_zone_t **zone)
{
if (zone != NULL) {
kasp_zone_clear(*zone);
free(*zone);
*zone = NULL;
}
}
/* Copyright (C) 2017 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/lib/dnssec/kasp.h"
#include "knot/dnssec/kasp/kasp_db.h"
#include "knot/zone/zone.h"
typedef struct knot_kasp_zone {
knot_dname_t *dname;
dnssec_kasp_key_t *keys;
size_t num_keys;
dnssec_binary_t nsec3_salt;
time_t nsec3_salt_created;
} knot_kasp_zone_t;
int kasp_zone_load(knot_kasp_zone_t *zone,
const knot_dname_t *zone_name,
kasp_db_t *kdb);
int kasp_zone_save(const knot_kasp_zone_t *zone,
const knot_dname_t *zone_name,
kasp_db_t *kdb);
int kasp_zone_append(knot_kasp_zone_t *zone,
const dnssec_kasp_key_t *appkey);
void kasp_zone_clear(knot_kasp_zone_t *zone);
void kasp_zone_free(knot_kasp_zone_t **zone);
......@@ -53,6 +53,7 @@
/test_dthreads
/test_fdset
/test_journal
/test_kasp_db
/test_node
/test_process_answer
/test_process_query
......
......@@ -67,6 +67,7 @@ check_PROGRAMS += \
test_dthreads \
test_fdset \
test_journal \
test_kasp_db \
test_node \
test_process_query \
test_query_module \
......
/* Copyright (C) 2017 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 <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <tap/basic.h>
#include <tap/files.h>
#include "libknot/libknot.h"
#include "test_conf.h"
#include "knot/dnssec/kasp/kasp_db.c"
#define CHARS500_1 "kTZgFfrHPP2EOSK24zRjY9GlgCUEkZNBF5UwqsTWisCxGQT4ieinitjXWT1c" \
"pj+OR8UX/httSugee+MFsm5yOU/4/211BpLKwwOIAt4Yf8K7Bc+oXTdk15cH" \
"TRZtshM1AtfjRsX9rsLDsnaFCyMXzty9AQoRxSphjxnUUC6fszfrSsRx7htl" \
"/Xn1PAuwp9Bfn+FxAws98LYVuwiDqUgn4BR5lELdGd16zNOZnN7v023pmPDM" \
"nGyvIATuqTCPbFeXTfw7aIDyx2DF+y95/kSnPtY3c1b0Yf+oCv4t3Hx2jjWT" \
"9zuC6H+d+PL6HWqilJBs7ysn2FVpnE/Yo44VrQ8orw8QFZr1kR6z7AOVAcMk" \
"ac+44swsc8orGCyJx6OlUfN5oU3YahUfLqg9ewl13+P2chmeI6wUyttKsq/4" \
"Ud0YQAozBabiAKr1O/Eg7sZR6bV1YkCydQyYgmR/+VOu9D8Ld6uO4DcvhiE/" \
"2AmTkLsKLxtpMnQqsTnx"
#define CHARS500_2 "pzqkMLvpxUYYg0KuMCcBsk9aMm4b5Ny+vJ5UnTq8DVC0jHJJyyGcljKqfpi3" \
"MkjfrWY0rbzXFZbZZ6i8bmhhRBcSxE+tK/3AU1LR7ZJsTuITuoJo5LKRH7Uu" \
"MU7RBAzFuk3o+Pcyk+9UdbiRn9p4QqPTvb+2xfYn1pJvGHofJcQsSsPEe9Hw" \
"ycVW+kdImvWiidn0/e1G6B2xibovnPKDUBFmTbdZIBKHb/eUUoUCNA9CWt5N" \
"g8MutK2ixlBJlOvA6CA1V/VW56EJpLqvMxLaoRks5VY5Ls7zWAy97GEFH0Pl" \
"uO/Rba1du5tsC0MAC08hljlmu9uoPhsvHdBYHUnQ7jDuYnu9GN3DN0Z6oVbV" \
"N01JQZYhKQK/Bl61oM5JubLydtAypryDoG3IH75LhoVC8iGxDoDkxt3zoi/Q" \
"PVfPZZsm5j5UOs3wrQL0KWylm2IDK42mrHK8F/XebnOYLNLQaan2a90C+fhH" \
"a6hvu0RorkZzGNAZkq/D"
const knot_dname_t *zone1 = (const knot_dname_t *)"\x05""zonea";
const knot_dname_t *zone2 = (const knot_dname_t *)"\x05""zoneb";
kasp_db_t *db;
const key_params_t params1 = { .id = "key id 1", .keytag = 1, .timing = { 1, 11, 111, 1111, 11111 },
.public_key = { 520, (uint8_t *)"pk1 plus 500 chars: " CHARS500_1 } };
const key_params_t params2 = { .id = "key id 2", .keytag = 2, .timing = { 2, 22, 222, 2222, 22222 },
.public_key = { 520, (uint8_t *)"pk2 plus 500 chars: " CHARS500_2 } };
bool params_eq(const key_params_t *a, const key_params_t *b)
{
return ((a->keytag == b->keytag) && (a->public_key.size == b->public_key.size) &&
(a->timing.retire == b->timing.retire) && (strcmp(a->id, b->id) == 0) &&
(memcmp(a->public_key.data, b->public_key.data, b->public_key.size) == 0));
}
int main(int argc, char *argv[])
{
plan_lazy();
char *test_dir_name = test_mkdtemp();
bool ignore = false;
list_t l;
key_params_t params = { 0 };
#define free_params free(params.id); free(params.public_key.data); params.id = NULL; params.public_key.data = NULL;
int ret = kasp_db_init(&db, test_dir_name, 500*1024*1024);
is_int(KNOT_EOK, ret, "kasp_db: init eok");
ret = kasp_db_open(db);
is_int(KNOT_EOK, ret, "kasp_db: open eok");
ok(db->keys_db != NULL, "kasp_db: keys db notnull");
ok(db->zones_db != NULL, "kasp_db: zones db notnull");
ret = kasp_db_add_key(db, zone1, &params1);
is_int(KNOT_EOK, ret, "kasp_db: add key 1 eok");
ret = kasp_db_list_keys(db, zone1, &l);
is_int(KNOT_EOK, ret, "kasp_db: list keys 1 eok");
is_int(1, list_size(&l), "kasp_db: list keys reports one key 1");
ptrlist_deep_free(&l);
ret = kasp_db_key_params(db, params1.id, &params);
is_int(KNOT_EOK, ret, "kasp_db: key params 1 eok");
ok(params_eq(&params, &params1), "kasp_db: key params equal 1");
free_params
ret = kasp_db_list_keys(db, zone2, &l);
is_int(KNOT_ENOENT, ret, "kasp_db: list keys 1 enoent");
is_int(0, list_size(&l), "kasp_db: list keys reports no keys 1");
ptrlist_deep_free(&l);
ret = kasp_db_key_params(db, params2.id, &params);
is_int(KNOT_ENOENT, ret, "kasp_db: key params 1 enoent");
ret = kasp_db_share_key(db, zone2, params1.id);
is_int(KNOT_EOK, ret, "kasp_db: share key eok");
ret = kasp_db_list_keys(db, zone2, &l);
is_int(KNOT_EOK, ret, "kasp_db: list keys 3 eok");
is_int(1, list_size(&l), "kasp_db: list keys reports one key 2");
ptrlist_deep_free(&l);
ret = kasp_db_add_key(db, zone2, &params2);
is_int(KNOT_EOK, ret, "kasp_db: add key 2 eok");
ret = kasp_db_list_keys(db, zone2, &l);
is_int(KNOT_EOK, ret, "kasp_db: list keys 4 eok");
is_int(2, list_size(&l), "kasp_db: list keys reports two keys 1");
ptrlist_deep_free(&l);
ret = kasp_db_key_params(db, params2.id, &params);
is_int(KNOT_EOK, ret, "kasp_db: key_params 2 eok");
ok(params_eq(&params, &params2), "kasp_db: key params equal 2");
free_params
ret = kasp_db_delete_key(db, zone1, params1.id, &ignore);
is_int(KNOT_EOK, ret, "kasp_db: delete key 1 eok");
ret = kasp_db_list_keys(db, zone1, &l);
is_int(KNOT_ENOENT, ret, "kasp_db: list keys 2 enoent");
is_int(list_size(&l), 0, "kasp_db: list keys reports no keys 2");
ptrlist_deep_free(&l);
ret = kasp_db_key_params(db, params1.id, &params);
is_int(KNOT_EOK, ret, "kasp_db: key params 1 eok");
ok(params_eq(&params, &params1), "kasp_db: key params equal 1");
free_params
ret = kasp_db_delete_key(db, zone2, params1.id, &ignore);
is_int(KNOT_EOK, ret, "kasp_db: delete key 2 eok");
ret = kasp_db_list_keys(db, zone2, &l);
is_int(KNOT_EOK, ret, "kasp_db: list keys 5 eok");
is_int(1, list_size(&l), "kasp_db: list keys reports one key 3");
ptrlist_deep_free(&l);
ret = kasp_db_key_params(db, params1.id, &params);
is_int(KNOT_ENOENT, ret, "kasp_db: key params 2 enoent");
kasp_db_close(&db);
test_rm_rf(test_dir_name);
free(test_dir_name);
return 0;
}
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