Commit 3171f32f authored by Daniel Salzman's avatar Daniel Salzman

libdnssec: remove PKCS #8 keystore abstraction

parent ce50b6a1
......@@ -269,7 +269,6 @@ src/libdnssec/keystore/internal.h
src/libdnssec/keystore/keystore.c
src/libdnssec/keystore/pkcs11.c
src/libdnssec/keystore/pkcs8.c
src/libdnssec/keystore/pkcs8_dir.c
src/libdnssec/keytag.h
src/libdnssec/list.h
src/libdnssec/list/list.c
......@@ -485,7 +484,6 @@ tests/libdnssec/test_key_ds.c
tests/libdnssec/test_keyid.c
tests/libdnssec/test_keystore_pkcs11.c
tests/libdnssec/test_keystore_pkcs8.c
tests/libdnssec/test_keystore_pkcs8_dir.c
tests/libdnssec/test_keytag.c
tests/libdnssec/test_list.c
tests/libdnssec/test_nsec_bitmap.c
......
/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2019 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
......@@ -49,7 +49,7 @@ int keystore_load(const char *config, unsigned backend,
switch (backend) {
case KEYSTORE_BACKEND_PEM:
ret = dnssec_keystore_init_pkcs8_dir(keystore);
ret = dnssec_keystore_init_pkcs8(keystore);
fixed_config = fix_path(config, kasp_base_path);
break;
case KEYSTORE_BACKEND_PKCS11:
......
......@@ -63,7 +63,6 @@ libdnssec_la_SOURCES = \
libdnssec/keystore/keystore.c \
libdnssec/keystore/pkcs11.c \
libdnssec/keystore/pkcs8.c \
libdnssec/keystore/pkcs8_dir.c \
libdnssec/list/list.c \
libdnssec/list/ucw_clists.h \
libdnssec/nsec/bitmap.c \
......
/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2019 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
......@@ -23,7 +23,7 @@
* The module provides abstraction for private key store. Basically, PKCS #8
* and PKCS #11 interfaces are supported.
*
* PKCS #8 uses unencrypted PEM, and allows implementation of custom stores.
* PKCS #8 uses unencrypted PEM.
*
* PKCS #11 provides access Hardware Security Modules.
*
......@@ -97,82 +97,6 @@ struct dnssec_keystore;
*/
typedef struct dnssec_keystore dnssec_keystore_t;
/*!
* PKCS #8 key store callback functions for custom providers.
*/
typedef struct dnssec_keystore_pkcs8_functions {
/*!
* Callback to allocate key store handle.
*
* \param[out] handle_ptr Allocated key store handle.
*/
int (*handle_new)(void **handle_ptr);
/*!
* Callback to deallocate key store handle.
*
* \param handle Key store handle.
*/
int (*handle_free)(void *handle);
/*!
* Callback to initialize the key store.
*
* \param handle Key store handle.
* \param config Configuration string.
*/
int (*init)(void *handle, const char *config);
/*!
* Callback to open the key store.
*
* \param[out] handle Key store handle.
* \param[in] config Configuration string.
*/
int (*open)(void *handle, const char *config);
/*!
* Callback to close the key store.
*
* \param handle Key store handle.
*/
int (*close)(void *handle);
/*!
* Callback to read a PEM key.
*
* \param[in] handle Key store handle.
* \param[in] id Key ID of the key to be retrieved (ASCII form).
* \param[out] pem Key material in uncencrypted PEM format.
*/
int (*read)(void *handle, const char *id, dnssec_binary_t *pem);
/*!
* Callback to write a PEM key.
*
* \param handle Key store handle.
* \param id Key ID of the key to be saved (ASCII form).
* \param pem Key material in unencrypted PEM format.
*/
int (*write)(void *handle, const char *id, const dnssec_binary_t *pem);
/*!
* Callback to get a list of all PEM key IDs.
*
* \param[in] handle Key store handle.
* \param[out] list Allocated list of key IDs.
*/
int (*list)(void *handle, dnssec_list_t **list);
/*!
* Callback to remove a PEM key.
*
* \param handle Key store handle.
* \param id Key ID of the key to be removed (ASCII form).
*/
int (*remove)(void *handle, const char *id);
} dnssec_keystore_pkcs8_functions_t;
/*!
* Create default PKCS #8 private key store context.
*
......@@ -184,18 +108,7 @@ typedef struct dnssec_keystore_pkcs8_functions {
*
* \return Error code, DNSSEC_EOK if successful.
*/
int dnssec_keystore_init_pkcs8_dir(dnssec_keystore_t **store);
/*!
* Create custom PKCS #8 private key store context.
*
* \param[out] store Opened key store.
* \param[in] impl Implementation of the key store provider.
*
* \return Error code, DNSSEC_EOK if successful.
*/
int dnssec_keystore_init_pkcs8_custom(dnssec_keystore_t **store,
const dnssec_keystore_pkcs8_functions_t *impl);
int dnssec_keystore_init_pkcs8(dnssec_keystore_t **store);
/*!
* Crate new PKCS #11 private key store context.
......
/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2019 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
......@@ -26,8 +26,8 @@
typedef struct keystore_functions {
// construction of internal context
int (*ctx_new)(void **ctx_ptr, void *data);
int (*ctx_free)(void *ctx);
int (*ctx_new)(void **ctx_ptr);
void (*ctx_free)(void *ctx);
// keystore init/open/close
int (*init)(void *ctx, const char *config);
int (*open)(void *ctx, const char *config);
......@@ -48,5 +48,4 @@ struct dnssec_keystore {
};
int keystore_create(dnssec_keystore_t **store_ptr,
const keystore_functions_t *functions,
void *ctx_custom_data);
const keystore_functions_t *functions);
/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2019 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
......@@ -31,8 +31,7 @@
/* -- internal API --------------------------------------------------------- */
int keystore_create(dnssec_keystore_t **store_ptr,
const keystore_functions_t *functions,
void *ctx_custom_data)
const keystore_functions_t *functions)
{
assert(store_ptr);
assert(functions);
......@@ -44,7 +43,7 @@ int keystore_create(dnssec_keystore_t **store_ptr,
store->functions = functions;
int result = functions->ctx_new(&store->ctx, ctx_custom_data);
int result = functions->ctx_new(&store->ctx);
if (result != DNSSEC_EOK) {
free(store);
return DNSSEC_ENOMEM;
......
/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2019 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
......@@ -147,7 +147,7 @@ static void disable_pkcs11_callbacks(void)
gnutls_pkcs11_set_token_function(NULL, NULL);
}
static int pkcs11_ctx_new(void **ctx_ptr, _unused_ void *data)
static int pkcs11_ctx_new(void **ctx_ptr)
{
static pthread_once_t once = PTHREAD_ONCE_INIT;
pthread_once(&once, disable_pkcs11_callbacks);
......@@ -162,13 +162,9 @@ static int pkcs11_ctx_new(void **ctx_ptr, _unused_ void *data)
return DNSSEC_EOK;
}
static int pkcs11_ctx_free(void *ctx)
static void pkcs11_ctx_free(void *ctx)
{
if (ctx) {
free(ctx);
}
return DNSSEC_EOK;
free(ctx);
}
static int pkcs11_init(void *ctx, const char *config)
......@@ -429,7 +425,7 @@ int dnssec_keystore_init_pkcs11(dnssec_keystore_t **store_ptr)
.get_private = pkcs11_get_private,
};
return keystore_create(store_ptr, &IMPLEMENTATION, NULL);
return keystore_create(store_ptr, &IMPLEMENTATION);
}
#else // !ENABLE_PKCS11
......
This diff is collapsed.
/* Copyright (C) 2018 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 <https://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "contrib/files.h"
#include "libdnssec/binary.h"
#include "libdnssec/error.h"
#include "libdnssec/key.h"
#include "libdnssec/keystore.h"
#include "libdnssec/keystore/internal.h"
#include "libdnssec/shared/shared.h"
#define DIR_INIT_MODE 0750
/*!
* Context for PKCS #8 key directory.
*/
typedef struct pkcs8_dir_handle {
char *dir_name;
} pkcs8_dir_handle_t;
/* -- internal functions --------------------------------------------------- */
/*!
* Get path to a private key in PKCS #8 PEM format.
*/
static char *key_path(const char *dir, const char *id)
{
char *strp = NULL;
int ret = asprintf(&strp, "%s/%s.pem", dir, id);
if (ret < 0) {
return NULL;
}
return strp;
}
/*!
* Get size of the file and reset the position to the beginning of the file.
*/
static int file_size(int fd, size_t *size)
{
off_t offset = lseek(fd, 0, SEEK_END);
if (offset == -1) {
return dnssec_errno_to_error(errno);
}
if (lseek(fd, 0, SEEK_SET) == -1) {
return dnssec_errno_to_error(errno);
}
assert(offset >= 0);
*size = offset;
return DNSSEC_EOK;
}
/*!
* Open a key file and get the descriptor.
*/
static int key_open(const char *dir_name, const char *id, int flags,
mode_t mode, int *fd_ptr)
{
assert(dir_name);
assert(id);
assert(fd_ptr);
_cleanup_free_ char *filename = key_path(dir_name, id);
if (!filename) {
return DNSSEC_ENOMEM;
}
int fd = open(filename, flags, mode);
if (fd == -1) {
return dnssec_errno_to_error(errno);
}
*fd_ptr = fd;
return DNSSEC_EOK;
}
static int key_open_read(const char *dir_name, const char *id, int *fd_ptr)
{
return key_open(dir_name, id, O_RDONLY, 0, fd_ptr);
}
static int key_open_write(const char *dir_name, const char *id, int *fd_ptr)
{
return key_open(dir_name, id, O_WRONLY|O_CREAT|O_EXCL,
S_IRUSR|S_IWUSR|S_IRGRP, fd_ptr);
}
/*!
* Strip '.pem' suffix, basename has to be valid key ID.
*/
static char *filename_to_keyid(const char *filename)
{
assert(filename);
size_t len = strlen(filename);
const char ext[] = ".pem";
const size_t ext_len = sizeof(ext) - 1;
if (len < ext_len || strcmp(filename + len - ext_len, ext) != 0) {
return NULL;
}
return strndup(filename, len - ext_len);
}
/* -- PKCS #8 dir access API ----------------------------------------------- */
static int pkcs8_dir_handle_new(void **handle_ptr)
{
if (!handle_ptr) {
return DNSSEC_EINVAL;
}
pkcs8_dir_handle_t *handle = calloc(1, sizeof(*handle));
if (!handle) {
return DNSSEC_ENOMEM;
}
*handle_ptr = handle;
return DNSSEC_EOK;
}
static int pkcs8_dir_handle_free(void *handle)
{
free(handle);
return DNSSEC_EOK;
}
static int pkcs8_dir_init(void *handle, const char *path)
{
if (!handle || !path) {
return DNSSEC_EINVAL;
}
return make_dir(path, DIR_INIT_MODE, true);
}
static int pkcs8_dir_open(void *_handle, const char *config)
{
if (!_handle || !config) {
return DNSSEC_EINVAL;
}
pkcs8_dir_handle_t *handle = _handle;
char *path = realpath(config, NULL);
if (!path) {
return DNSSEC_NOT_FOUND;
}
handle->dir_name = path;
return DNSSEC_EOK;
}
static int pkcs8_dir_close(void *_handle)
{
if (!_handle) {
return DNSSEC_EINVAL;
}
pkcs8_dir_handle_t *handle = _handle;
free(handle->dir_name);
memset(handle, 0, sizeof(*handle));
return DNSSEC_EOK;
}
static int pkcs8_dir_read(void *_handle, const char *id, dnssec_binary_t *pem)
{
if (!_handle || !id || !pem) {
return DNSSEC_EINVAL;
}
pkcs8_dir_handle_t *handle = _handle;
// open file and get it's size
_cleanup_close_ int file = 0;
int result = key_open_read(handle->dir_name, id, &file);
if (result != DNSSEC_EOK) {
return result;
}
size_t size = 0;
result = file_size(file, &size);
if (result != DNSSEC_EOK) {
return result;
}
if (size == 0) {
return DNSSEC_MALFORMED_DATA;
}
// read the stored data
dnssec_binary_t read_pem = { 0 };
result = dnssec_binary_alloc(&read_pem, size);
if (result != DNSSEC_EOK) {
return result;
}
ssize_t read_count = read(file, read_pem.data, read_pem.size);
if (read_count == -1) {
dnssec_binary_free(&read_pem);
return dnssec_errno_to_error(errno);
}
assert(read_count == read_pem.size);
*pem = read_pem;
return DNSSEC_EOK;
}
static bool key_is_duplicate(int open_error, pkcs8_dir_handle_t *handle,
const char *id, const dnssec_binary_t *pem)
{
assert(handle);
assert(id);
assert(pem);
if (open_error != dnssec_errno_to_error(EEXIST)) {
return false;
}
_cleanup_binary_ dnssec_binary_t old = { 0 };
int r = pkcs8_dir_read(handle, id, &old);
if (r != DNSSEC_EOK) {
return false;
}
return dnssec_binary_cmp(&old, pem) == 0;
}
static int pkcs8_dir_write(void *_handle, const char *id, const dnssec_binary_t *pem)
{
if (!_handle || !id || !pem) {
return DNSSEC_EINVAL;
}
if (pem->size == 0 || pem->data == NULL) {
return DNSSEC_MALFORMED_DATA;
}
pkcs8_dir_handle_t *handle = _handle;
// create the file
_cleanup_close_ int file = 0;
int result = key_open_write(handle->dir_name, id, &file);
if (result != DNSSEC_EOK) {
if (key_is_duplicate(result, handle, id, pem)) {
return DNSSEC_EOK;
}
return result;
}
// write the data
ssize_t wrote_count = write(file, pem->data, pem->size);
if (wrote_count == -1) {
return dnssec_errno_to_error(errno);
}
assert(wrote_count == pem->size);
return DNSSEC_EOK;
}
static int pkcs8_dir_list(void *_handle, dnssec_list_t **list_ptr)
{
if (!_handle || !list_ptr) {
return DNSSEC_EINVAL;
}
pkcs8_dir_handle_t *handle = _handle;
_cleanup_closedir_ DIR *dir = opendir(handle->dir_name);
if (!dir) {
return DNSSEC_NOT_FOUND;
}
dnssec_list_t *list = dnssec_list_new();
if (!list) {
return DNSSEC_ENOMEM;
}
errno = 0;
struct dirent *result;
while ((result = readdir(dir)) != NULL) {
char *keyid = filename_to_keyid(result->d_name);
if (keyid) {
dnssec_list_append(list, keyid);
}
}
if (errno != 0) {
dnssec_list_free_full(list, NULL, NULL);
return dnssec_errno_to_error(errno);
}
*list_ptr = list;
return DNSSEC_EOK;
}
static int pkcs8_dir_remove(void *_handle, const char *id)
{
if (!_handle || !id) {
return DNSSEC_EINVAL;
}
pkcs8_dir_handle_t *handle = _handle;
_cleanup_free_ char *filename = key_path(handle->dir_name, id);
if (!filename) {
return DNSSEC_ENOMEM;
}
if (unlink(filename) == -1) {
return dnssec_errno_to_error(errno);
}
return DNSSEC_EOK;
}
const dnssec_keystore_pkcs8_functions_t PKCS8_DIR_FUNCTIONS = {
.handle_new = pkcs8_dir_handle_new,
.handle_free = pkcs8_dir_handle_free,
.init = pkcs8_dir_init,
.open = pkcs8_dir_open,
.close = pkcs8_dir_close,
.read = pkcs8_dir_read,
.write = pkcs8_dir_write,
.list = pkcs8_dir_list,
.remove = pkcs8_dir_remove,
};
/* -- public API ----------------------------------------------------------- */
_public_
int dnssec_keystore_init_pkcs8_dir(dnssec_keystore_t **store_ptr)
{
if (!store_ptr) {
return DNSSEC_EINVAL;
}
return dnssec_keystore_init_pkcs8_custom(store_ptr, &PKCS8_DIR_FUNCTIONS);
}
......@@ -65,7 +65,6 @@ check_PROGRAMS += \
libdnssec/test_keyid \
libdnssec/test_keystore_pkcs11 \
libdnssec/test_keystore_pkcs8 \
libdnssec/test_keystore_pkcs8_dir \
libdnssec/test_keytag \
libdnssec/test_list \
libdnssec/test_nsec_bitmap \
......
/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2019 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
......@@ -14,206 +14,79 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <tap/basic.h>
#include <stdbool.h>
#include "binary.h"
#include "crypto.h"
#include "error.h"
#include "key.h"
#include "keyid.h"
#include "keystore.h"
/* -- mock key store ------------------------------------------------------- */
static void *test_handle = (void *)0x42;
static bool test_handle_new_ok = false;
static int test_handle_new(void **handle_ptr)
{
if (handle_ptr) {
*handle_ptr = test_handle;
test_handle_new_ok = true;
}
return DNSSEC_EOK;
}
static bool test_handle_free_ok = false;
static int test_handle_free(void *handle)
{
test_handle_free_ok = (handle == test_handle);
return DNSSEC_EOK;
}
static bool test_init_ok = false;
static int test_init(void *handle, const char *config)
{
test_init_ok = (handle == test_handle && config && strcmp(config, "init config") == 0);
return DNSSEC_EOK;
}
static bool test_open_ok = false;
static int test_open(void *handle, const char *config)
{
test_open_ok = (handle == test_handle && config && strcmp(config, "open config") == 0);
return DNSSEC_EOK;
}
static bool test_close_ok = false;
static int test_close(void *handle)
{
test_close_ok = (handle == test_handle);
return DNSSEC_EOK;
}
static bool test_write_ok = false;
static char *test_write_id = NULL;
static dnssec_binary_t test_write_pem = { 0 };
static int test_write(void *handle, const char *id, const dnssec_binary_t *pem)
{
if (handle == test_handle && id && pem) {
test_write_ok = true;
test_write_id = dnssec_keyid_copy(id);
dnssec_binary_dup(pem, &test_write_pem);
}
return DNSSEC_EOK;
}
static bool test_read_ok = false;
static char *test_read_id = NULL;
static int test_read(void *handle, const char *id, dnssec_binary_t *pem)
{
if (handle == test_handle && id && pem) {
test_read_ok = true;
test_read_id = dnssec_keyid_copy(id);
dnssec_binary_dup(&test_write_pem, pem);
}
return DNSSEC_EOK;
}
static bool test_list_ok = false;
static int test_list(void *handle, dnssec_list_t **list_ptr)
{
if (handle == test_handle && list_ptr) {
test_list_ok = true;
}
if (list_ptr) {
*list_ptr = dnssec_list_new();
}
return DNSSEC_EOK;
}
static bool test_remove_ok = false;
static char *test_remove_id = NULL;
static int test_remove(void *handle, const char *id)