5bc46b49c8
key_lookup() will always return a key, even if that key is revoked or invalidated. So check for invalid keys before continuing. Signed-off-by: Hannes Reinecke <hare@kernel.org> Reviewed-by: Sagi Grimberg <sagi@grimberg.me> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Keith Busch <kbusch@kernel.org>
225 lines
5.7 KiB
C
225 lines
5.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2023 Hannes Reinecke, SUSE Labs
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/key.h>
|
|
#include <linux/key-type.h>
|
|
#include <keys/user-type.h>
|
|
#include <linux/nvme.h>
|
|
#include <linux/nvme-tcp.h>
|
|
#include <linux/nvme-keyring.h>
|
|
|
|
static struct key *nvme_keyring;
|
|
|
|
key_serial_t nvme_keyring_id(void)
|
|
{
|
|
return nvme_keyring->serial;
|
|
}
|
|
EXPORT_SYMBOL_GPL(nvme_keyring_id);
|
|
|
|
static bool nvme_tls_psk_revoked(struct key *psk)
|
|
{
|
|
return test_bit(KEY_FLAG_REVOKED, &psk->flags) ||
|
|
test_bit(KEY_FLAG_INVALIDATED, &psk->flags);
|
|
}
|
|
|
|
struct key *nvme_tls_key_lookup(key_serial_t key_id)
|
|
{
|
|
struct key *key = key_lookup(key_id);
|
|
|
|
if (IS_ERR(key)) {
|
|
pr_err("key id %08x not found\n", key_id);
|
|
return key;
|
|
}
|
|
if (nvme_tls_psk_revoked(key)) {
|
|
pr_err("key id %08x revoked\n", key_id);
|
|
return ERR_PTR(-EKEYREVOKED);
|
|
}
|
|
return key;
|
|
}
|
|
EXPORT_SYMBOL_GPL(nvme_tls_key_lookup);
|
|
|
|
static void nvme_tls_psk_describe(const struct key *key, struct seq_file *m)
|
|
{
|
|
seq_puts(m, key->description);
|
|
seq_printf(m, ": %u", key->datalen);
|
|
}
|
|
|
|
static bool nvme_tls_psk_match(const struct key *key,
|
|
const struct key_match_data *match_data)
|
|
{
|
|
const char *match_id;
|
|
size_t match_len;
|
|
|
|
if (!key->description) {
|
|
pr_debug("%s: no key description\n", __func__);
|
|
return false;
|
|
}
|
|
if (!match_data->raw_data) {
|
|
pr_debug("%s: no match data\n", __func__);
|
|
return false;
|
|
}
|
|
match_id = match_data->raw_data;
|
|
match_len = strlen(match_id);
|
|
pr_debug("%s: match '%s' '%s' len %zd\n",
|
|
__func__, match_id, key->description, match_len);
|
|
return !memcmp(key->description, match_id, match_len);
|
|
}
|
|
|
|
static int nvme_tls_psk_match_preparse(struct key_match_data *match_data)
|
|
{
|
|
match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
|
|
match_data->cmp = nvme_tls_psk_match;
|
|
return 0;
|
|
}
|
|
|
|
static struct key_type nvme_tls_psk_key_type = {
|
|
.name = "psk",
|
|
.flags = KEY_TYPE_NET_DOMAIN,
|
|
.preparse = user_preparse,
|
|
.free_preparse = user_free_preparse,
|
|
.match_preparse = nvme_tls_psk_match_preparse,
|
|
.instantiate = generic_key_instantiate,
|
|
.revoke = user_revoke,
|
|
.destroy = user_destroy,
|
|
.describe = nvme_tls_psk_describe,
|
|
.read = user_read,
|
|
};
|
|
|
|
static struct key *nvme_tls_psk_lookup(struct key *keyring,
|
|
const char *hostnqn, const char *subnqn,
|
|
u8 hmac, u8 psk_ver, bool generated)
|
|
{
|
|
char *identity;
|
|
size_t identity_len = (NVMF_NQN_SIZE) * 2 + 11;
|
|
key_ref_t keyref;
|
|
key_serial_t keyring_id;
|
|
|
|
identity = kzalloc(identity_len, GFP_KERNEL);
|
|
if (!identity)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
snprintf(identity, identity_len, "NVMe%u%c%02u %s %s",
|
|
psk_ver, generated ? 'G' : 'R', hmac, hostnqn, subnqn);
|
|
|
|
if (!keyring)
|
|
keyring = nvme_keyring;
|
|
keyring_id = key_serial(keyring);
|
|
pr_debug("keyring %x lookup tls psk '%s'\n",
|
|
keyring_id, identity);
|
|
keyref = keyring_search(make_key_ref(keyring, true),
|
|
&nvme_tls_psk_key_type,
|
|
identity, false);
|
|
if (IS_ERR(keyref)) {
|
|
pr_debug("lookup tls psk '%s' failed, error %ld\n",
|
|
identity, PTR_ERR(keyref));
|
|
kfree(identity);
|
|
return ERR_PTR(-ENOKEY);
|
|
}
|
|
kfree(identity);
|
|
|
|
return key_ref_to_ptr(keyref);
|
|
}
|
|
|
|
/*
|
|
* NVMe PSK priority list
|
|
*
|
|
* 'Retained' PSKs (ie 'generated == false') should be preferred to 'generated'
|
|
* PSKs, PSKs with hash (psk_ver 1) should be preferred to PSKs without hash
|
|
* (psk_ver 0), and SHA-384 should be preferred to SHA-256.
|
|
*/
|
|
static struct nvme_tls_psk_priority_list {
|
|
bool generated;
|
|
u8 psk_ver;
|
|
enum nvme_tcp_tls_cipher cipher;
|
|
} nvme_tls_psk_prio[] = {
|
|
{ .generated = false,
|
|
.psk_ver = 1,
|
|
.cipher = NVME_TCP_TLS_CIPHER_SHA384, },
|
|
{ .generated = false,
|
|
.psk_ver = 1,
|
|
.cipher = NVME_TCP_TLS_CIPHER_SHA256, },
|
|
{ .generated = false,
|
|
.psk_ver = 0,
|
|
.cipher = NVME_TCP_TLS_CIPHER_SHA384, },
|
|
{ .generated = false,
|
|
.psk_ver = 0,
|
|
.cipher = NVME_TCP_TLS_CIPHER_SHA256, },
|
|
{ .generated = true,
|
|
.psk_ver = 1,
|
|
.cipher = NVME_TCP_TLS_CIPHER_SHA384, },
|
|
{ .generated = true,
|
|
.psk_ver = 1,
|
|
.cipher = NVME_TCP_TLS_CIPHER_SHA256, },
|
|
{ .generated = true,
|
|
.psk_ver = 0,
|
|
.cipher = NVME_TCP_TLS_CIPHER_SHA384, },
|
|
{ .generated = true,
|
|
.psk_ver = 0,
|
|
.cipher = NVME_TCP_TLS_CIPHER_SHA256, },
|
|
};
|
|
|
|
/*
|
|
* nvme_tls_psk_default - Return the preferred PSK to use for TLS ClientHello
|
|
*/
|
|
key_serial_t nvme_tls_psk_default(struct key *keyring,
|
|
const char *hostnqn, const char *subnqn)
|
|
{
|
|
struct key *tls_key;
|
|
key_serial_t tls_key_id;
|
|
int prio;
|
|
|
|
for (prio = 0; prio < ARRAY_SIZE(nvme_tls_psk_prio); prio++) {
|
|
bool generated = nvme_tls_psk_prio[prio].generated;
|
|
u8 ver = nvme_tls_psk_prio[prio].psk_ver;
|
|
enum nvme_tcp_tls_cipher cipher = nvme_tls_psk_prio[prio].cipher;
|
|
|
|
tls_key = nvme_tls_psk_lookup(keyring, hostnqn, subnqn,
|
|
cipher, ver, generated);
|
|
if (!IS_ERR(tls_key)) {
|
|
tls_key_id = tls_key->serial;
|
|
key_put(tls_key);
|
|
return tls_key_id;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(nvme_tls_psk_default);
|
|
|
|
static int __init nvme_keyring_init(void)
|
|
{
|
|
int err;
|
|
|
|
nvme_keyring = keyring_alloc(".nvme",
|
|
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
|
|
current_cred(),
|
|
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
|
(KEY_USR_ALL & ~KEY_USR_SETATTR),
|
|
KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
|
|
if (IS_ERR(nvme_keyring))
|
|
return PTR_ERR(nvme_keyring);
|
|
|
|
err = register_key_type(&nvme_tls_psk_key_type);
|
|
if (err) {
|
|
key_put(nvme_keyring);
|
|
return err;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void __exit nvme_keyring_exit(void)
|
|
{
|
|
unregister_key_type(&nvme_tls_psk_key_type);
|
|
key_revoke(nvme_keyring);
|
|
key_put(nvme_keyring);
|
|
}
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_AUTHOR("Hannes Reinecke <hare@suse.de>");
|
|
MODULE_DESCRIPTION("NVMe Keyring implementation");
|
|
module_init(nvme_keyring_init);
|
|
module_exit(nvme_keyring_exit);
|