Commit cc662ec1 authored by Konrad Sztyber's avatar Konrad Sztyber Committed by Tomasz Zawadzki
Browse files

lib: add keyring library



The purpose of this library is to provide other libraries with an
interface for retrieving cryptographic keys.  This interface consists of
functions managing the keyring (add/remove), keeping track of key
references (get/put), and a set of callbacks used to define modules
responsible for providing access to the keys in a secure manner.

Key objects (called spdk_key) are identified by a unique name.  They
don't hold the keying material themselves, but only act as references to
the actual key.  Once the key is needed to perform a crypto operation,
this reference is used to retrieve keying material.  These objects are
refcounted, so it is safe to remove a key from the keyring while it's
still in use.  If that happens, the spdk_key object remains valid, but
it cannot be used to obtain the key (i.e. spdk_key_get_key() will result
in -ENOKEY).

Signed-off-by: default avatarKonrad Sztyber <konrad.sztyber@intel.com>
Change-Id: I5324fa28d5828a989b003bb5f715cfcac2077262
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/21734


Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarJim Harris <jim.harris@samsung.com>
Reviewed-by: default avatarAleksey Marchuk <alexeymar@nvidia.com>
parent 68745a9c
Loading
Loading
Loading
Loading

include/spdk/keyring.h

0 → 100644
+67 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright (c) 2024 Intel Corporation. All rights reserved.
 */

#ifndef SPDK_KEYRING_H
#define SPDK_KEYRING_H

#include "spdk/stdinc.h"

struct spdk_key;

/**
 * Get a reference to a key from the keyring.  The key must have been added to the keyring by
 * the appropriate keyring module.  The reference will be kept alive until its released via
 * `spdk_keyring_put_key()`.  If the key is removed from the keyring, the reference is kept alive, but
 * the key won't be usable anymore.
 *
 * \param name Name of a key.  The name can be optionally prepended with the name of a keyring to
 * retrieve the key from followed by a ":" character.  For instance, "keyring0:key0" would retrieve
 * a key "key0" from keyring "keyring0".  If omitted, "global" keyring will be used.  To get a key
 * with a ":" character in its name from the global keyring, empty keyring name should be specified
 * (e.g. ":key0:foo" refers to a key "key0:foo" in the global keyring).
 *
 * \return Reference to a key or NULL if the key doesn't exist.
 */
struct spdk_key *spdk_keyring_get_key(const char *name);

/**
 * Release a reference to a key obtained from `spdk_keyring_get_key()`.
 *
 * \param key Reference to a key.  If NULL, this function is a no-op.
 */
void spdk_keyring_put_key(struct spdk_key *key);

/**
 * Get the name of a key.
 *
 * \param key Reference to a key.
 *
 * \return Name of the key.
 */
const char *spdk_key_get_name(struct spdk_key *key);

/**
 * Retrieve keying material from a key reference.
 *
 * \param key Reference to a key.
 * \param buf Buffer to write the data to.
 * \param len Size of the `buf` buffer.
 *
 * \return The number of bytes written to `buf` or negative errno on error.
 */
int spdk_key_get_key(struct spdk_key *key, void *buf, int len);

/**
 * Initialize the keyring library.
 *
 * \return 0 on success, negative errno otherwise.
 */
int spdk_keyring_init(void);

/**
 * Free any resources acquired by the keyring library.  This function will free all of the keys.
 */
void spdk_keyring_cleanup(void);

#endif /* SPDK_KEYRING_H */
+83 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright (c) 2024 Intel Corporation. All rights reserved.
 */

#ifndef SPDK_KEYRING_MODULE_H
#define SPDK_KEYRING_MODULE_H

#include "spdk/stdinc.h"
#include "spdk/keyring.h"
#include "spdk/queue.h"

struct spdk_keyring_module;

struct spdk_key_opts {
	/** Size of this structure */
	size_t size;
	/** Name of the key */
	const char *name;
	/** Keyring module */
	struct spdk_keyring_module *module;
	/** Context passed to the add_key() callback */
	void *ctx;
};

/**
 * Add a key to the keyring.
 *
 * \param opts Key options.
 *
 * \return 0 on success, negative errno otherwise.
 */
int spdk_keyring_add_key(const struct spdk_key_opts *opts);

/**
 * Remove a key from the keyring.
 *
 * \param name Name of the key to remove.
 */
void spdk_keyring_remove_key(const char *name);

struct spdk_keyring_module {
	/** Name of the module */
	const char *name;

	/** Initializes a module, called during keyring's initialization */
	int (*init)(void);
	/** Clean up resources allocated by a module.  Called during keyring's cleanup  */
	void (*cleanup)(void);
	/** Add a key to the keyring */
	int (*add_key)(struct spdk_key *key, void *ctx);
	/** Remove a key from the keyring */
	void (*remove_key)(struct spdk_key *key);
	/** Get keying material from a key */
	int (*get_key)(struct spdk_key *key, void *buf, int len);
	/** Get the size of the context associated with a key */
	size_t (*get_ctx_size)(void);

	TAILQ_ENTRY(spdk_keyring_module) tailq;
};

/**
 * Register a keyring module.
 *
 * \param module Keyring module to register.
 */
void spdk_keyring_register_module(struct spdk_keyring_module *module);

#define SPDK_KEYRING_REGISTER_MODULE(name, module) \
static void __attribute__((constructor)) _spdk_keyring_register_##name(void) \
{ \
	spdk_keyring_register_module(module); \
}

/**
 * Get pointer to the module context associated with a key.
 *
 * \param key Key.
 *
 * \return Key context.
 */
void *spdk_key_get_ctx(struct spdk_key *key);

#endif /* SPDK_KEYRING_H */
+1 −1
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ include $(SPDK_ROOT_DIR)/mk/spdk.lib_deps.mk

DIRS-y += bdev blob blobfs conf dma accel event json jsonrpc \
          log lvol rpc sock thread trace util nvme vmd nvmf scsi \
          ioat ut_mock iscsi notify init trace_parser
          ioat ut_mock iscsi notify init trace_parser keyring
ifeq ($(OS),Linux)
DIRS-y += nbd ftl vfio_user
ifeq ($(CONFIG_UBLK),y)

lib/keyring/Makefile

0 → 100644
+15 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2024 Intel Corporation. All rights reserved.

SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk

SO_VER := 1
SO_MINOR := 0

C_SRCS = keyring.c
LIBNAME = keyring

SPDK_MAP_FILE = $(abspath $(CURDIR)/spdk_keyring.map)

include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk

lib/keyring/keyring.c

0 → 100644
+284 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright (c) 2024 Intel Corporation. All rights reserved.
 */

#include "spdk/keyring.h"
#include "spdk/keyring_module.h"
#include "spdk/log.h"
#include "spdk/queue.h"
#include "spdk/string.h"

struct spdk_key {
	char				*name;
	int				refcnt;
	bool				removed;
	struct spdk_keyring_module	*module;
	TAILQ_ENTRY(spdk_key)		tailq;
};

struct spdk_keyring {
	pthread_mutex_t				mutex;
	TAILQ_HEAD(, spdk_keyring_module)	modules;
	TAILQ_HEAD(, spdk_key)			keys;
	TAILQ_HEAD(, spdk_key)			removed_keys;
};

static struct spdk_keyring g_keyring = {
	.mutex = PTHREAD_MUTEX_INITIALIZER,
	.keys = TAILQ_HEAD_INITIALIZER(g_keyring.keys),
	.removed_keys = TAILQ_HEAD_INITIALIZER(g_keyring.removed_keys),
	.modules = TAILQ_HEAD_INITIALIZER(g_keyring.modules),
};

static const char *
keyring_get_key_name(const char *name)
{
	const char *keyname;

	/* Both "key0" and ":key0" refer to "key0" in the global keyring */
	keyname = strstr(name, ":");
	if (keyname == NULL) {
		return name;
	}

	return keyname + 1;
}

static struct spdk_key *
keyring_find_key(const char *name)
{
	struct spdk_key *key;

	TAILQ_FOREACH(key, &g_keyring.keys, tailq) {
		if (strcmp(keyring_get_key_name(key->name),
			   keyring_get_key_name(name)) == 0) {
			return key;
		}
	}

	return NULL;
}

static void
keyring_free_key(struct spdk_key *key)
{
	assert(key->refcnt == 0);

	free(key->name);
	free(key);
}

static void
keyring_put_key(struct spdk_key *key)
{
	assert(key->refcnt > 0);
	key->refcnt--;

	if (key->refcnt == 0) {
		assert(key->removed);
		TAILQ_REMOVE(&g_keyring.removed_keys, key, tailq);
		keyring_free_key(key);
	}
}

int
spdk_keyring_add_key(const struct spdk_key_opts *opts)
{
	struct spdk_key *key = NULL;
	struct spdk_keyring_module *module = opts->module;
	const char *keyname;
	int rc = 0;

	/* For now, only global keyring is supported */
	keyname = strstr(opts->name, ":");
	if (keyname != NULL && keyname != opts->name) {
		SPDK_ERRLOG("Couldn't add key '%s' to the keyring: keyring doesn't exist\n",
			    opts->name);
		return -EINVAL;
	}

	pthread_mutex_lock(&g_keyring.mutex);
	if (keyring_find_key(opts->name) != NULL) {
		SPDK_ERRLOG("Key '%s' already exists\n", opts->name);
		rc = -EEXIST;
		goto out;
	}

	key = calloc(1, sizeof(*key) + module->get_ctx_size());
	if (key == NULL) {
		rc = -ENOMEM;
		goto out;
	}

	key->name = strdup(opts->name);
	if (key->name == NULL) {
		rc = -ENOMEM;
		goto out;
	}

	rc = module->add_key(key, opts->ctx);
	if (rc != 0) {
		SPDK_ERRLOG("Failed to add key '%s' to the keyring\n", opts->name);
		goto out;
	}

	key->module = module;
	key->refcnt = 1;
	TAILQ_INSERT_TAIL(&g_keyring.keys, key, tailq);
out:
	pthread_mutex_unlock(&g_keyring.mutex);
	if (rc != 0 && key != NULL) {
		keyring_free_key(key);
	}

	return rc;
}

static void
keyring_remove_key(struct spdk_key *key)
{
	assert(!key->removed);
	key->removed = true;
	key->module->remove_key(key);
	TAILQ_REMOVE(&g_keyring.keys, key, tailq);
	TAILQ_INSERT_TAIL(&g_keyring.removed_keys, key, tailq);
	keyring_put_key(key);
}

void
spdk_keyring_remove_key(const char *name)
{
	struct spdk_key *key;

	pthread_mutex_lock(&g_keyring.mutex);
	key = keyring_find_key(name);
	if (key == NULL) {
		SPDK_WARNLOG("Key '%s' does not exist\n", name);
		goto out;
	}

	keyring_remove_key(key);
out:
	pthread_mutex_unlock(&g_keyring.mutex);
}

struct spdk_key *
spdk_keyring_get_key(const char *name)
{
	struct spdk_key *key;

	pthread_mutex_lock(&g_keyring.mutex);
	key = keyring_find_key(name);
	if (key == NULL) {
		goto out;
	}

	key->refcnt++;
out:
	pthread_mutex_unlock(&g_keyring.mutex);

	return key;
}

void
spdk_keyring_put_key(struct spdk_key *key)
{
	if (key == NULL) {
		return;
	}

	pthread_mutex_lock(&g_keyring.mutex);
	keyring_put_key(key);
	pthread_mutex_unlock(&g_keyring.mutex);
}

const char *
spdk_key_get_name(struct spdk_key *key)
{
	return key->name;
}

int
spdk_key_get_key(struct spdk_key *key, void *buf, int len)
{
	struct spdk_keyring_module *module = key->module;

	if (key->removed) {
		return -ENOKEY;
	}

	return module->get_key(key, buf, len);
}

void *
spdk_key_get_ctx(struct spdk_key *key)
{
	return key + 1;
}

void
spdk_keyring_register_module(struct spdk_keyring_module *module)
{
	TAILQ_INSERT_TAIL(&g_keyring.modules, module, tailq);
}

int
spdk_keyring_init(void)
{
	struct spdk_keyring_module *module, *tmp;
	int rc = 0;

	TAILQ_FOREACH(module, &g_keyring.modules, tailq) {
		if (module->init != NULL) {
			rc = module->init();
			if (rc != 0) {
				SPDK_ERRLOG("Failed to initialize module %s: %s\n", module->name,
					    spdk_strerror(-rc));
				break;
			}
		}

		SPDK_INFOLOG(keyring, "Initialized module %s\n", module->name);
	}

	if (rc != 0) {
		TAILQ_FOREACH(tmp, &g_keyring.modules, tailq) {
			if (tmp == module) {
				break;
			}
			if (tmp->cleanup != NULL) {
				tmp->cleanup();
			}
		}
	}

	return rc;
}

void
spdk_keyring_cleanup(void)
{
	struct spdk_keyring_module *module;
	struct spdk_key *key;

	while (!TAILQ_EMPTY(&g_keyring.keys)) {
		key = TAILQ_FIRST(&g_keyring.keys);
		keyring_remove_key(key);
	}

	while (!TAILQ_EMPTY(&g_keyring.removed_keys)) {
		key = TAILQ_FIRST(&g_keyring.removed_keys);
		SPDK_WARNLOG("Key '%s' still has %d references\n", key->name, key->refcnt);
		key->refcnt = 0;
		TAILQ_REMOVE(&g_keyring.removed_keys, key, tailq);
		keyring_free_key(key);
	}

	TAILQ_FOREACH(module, &g_keyring.modules, tailq) {
		if (module->cleanup != NULL) {
			module->cleanup();
		}
	}
}

SPDK_LOG_REGISTER_COMPONENT(keyring)
Loading