Commit 5bc8f147 authored by Jens Axboe's avatar Jens Axboe
Browse files

Merge tag 'nvme-6.7-2023-10-17' of git://git.infradead.org/nvme into for-6.7/block

Pull NVMe updates from Keith:

"nvme updates for Linux 6.7

 - nvme-auth updates (Mark)
 - nvme-tcp tls (Hannes)
 - nvme-fc annotaions (Kees)"

* tag 'nvme-6.7-2023-10-17' of git://git.infradead.org/nvme: (24 commits)
  nvme-auth: allow mixing of secret and hash lengths
  nvme-auth: use transformed key size to create resp
  nvme-auth: alloc nvme_dhchap_key as single buffer
  nvmet-tcp: use 'spin_lock_bh' for state_lock()
  nvme: rework NVME_AUTH Kconfig selection
  nvmet-tcp: peek icreq before starting TLS
  nvmet-tcp: control messages for recvmsg()
  nvmet-tcp: enable TLS handshake upcall
  nvmet: Set 'TREQ' to 'required' when TLS is enabled
  nvmet-tcp: allocate socket file
  nvmet-tcp: make nvmet_tcp_alloc_queue() a void function
  nvmet: make TCP sectype settable via configfs
  nvme-fabrics: parse options 'keyring' and 'tls_key'
  nvme-tcp: improve icreq/icresp logging
  nvme-tcp: control message handling for recvmsg()
  nvme-tcp: enable TLS handshake upcall
  nvme-tcp: allocate socket file
  security/keys: export key_lookup()
  nvme-keyring: implement nvme_tls_psk_default()
  nvme-tcp: add definitions for TLS cipher suites
  ...
parents ec8cf230 32445526
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -2,3 +2,16 @@

config NVME_COMMON
       tristate

config NVME_KEYRING
       bool
       select KEYS

config NVME_AUTH
	bool
	select CRYPTO
	select CRYPTO_HMAC
	select CRYPTO_SHA256
	select CRYPTO_SHA512
	select CRYPTO_DH
	select CRYPTO_DH_RFC7919_GROUPS
+2 −1
Original line number Diff line number Diff line
@@ -4,4 +4,5 @@ ccflags-y += -I$(src)

obj-$(CONFIG_NVME_COMMON)	+= nvme-common.o

nvme-common-y			+= auth.o
nvme-common-$(CONFIG_NVME_AUTH)	+= auth.o
nvme-common-$(CONFIG_NVME_KEYRING) += keyring.o
+39 −29
Original line number Diff line number Diff line
@@ -150,6 +150,14 @@ size_t nvme_auth_hmac_hash_len(u8 hmac_id)
}
EXPORT_SYMBOL_GPL(nvme_auth_hmac_hash_len);

u32 nvme_auth_key_struct_size(u32 key_len)
{
	struct nvme_dhchap_key key;

	return struct_size(&key, key, key_len);
}
EXPORT_SYMBOL_GPL(nvme_auth_key_struct_size);

struct nvme_dhchap_key *nvme_auth_extract_key(unsigned char *secret,
					      u8 key_hash)
{
@@ -163,14 +171,9 @@ struct nvme_dhchap_key *nvme_auth_extract_key(unsigned char *secret,
	p = strrchr(secret, ':');
	if (p)
		allocated_len = p - secret;
	key = kzalloc(sizeof(*key), GFP_KERNEL);
	key = nvme_auth_alloc_key(allocated_len, 0);
	if (!key)
		return ERR_PTR(-ENOMEM);
	key->key = kzalloc(allocated_len, GFP_KERNEL);
	if (!key->key) {
		ret = -ENOMEM;
		goto out_free_key;
	}

	key_len = base64_decode(secret, allocated_len, key->key);
	if (key_len < 0) {
@@ -187,14 +190,6 @@ struct nvme_dhchap_key *nvme_auth_extract_key(unsigned char *secret,
		goto out_free_secret;
	}

	if (key_hash > 0 &&
	    (key_len - 4) != nvme_auth_hmac_hash_len(key_hash)) {
		pr_err("Mismatched key len %d for %s\n", key_len,
		       nvme_auth_hmac_name(key_hash));
		ret = -EINVAL;
		goto out_free_secret;
	}

	/* The last four bytes is the CRC in little-endian format */
	key_len -= 4;
	/*
@@ -213,37 +208,51 @@ struct nvme_dhchap_key *nvme_auth_extract_key(unsigned char *secret,
	key->hash = key_hash;
	return key;
out_free_secret:
	kfree_sensitive(key->key);
out_free_key:
	kfree(key);
	nvme_auth_free_key(key);
	return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(nvme_auth_extract_key);

struct nvme_dhchap_key *nvme_auth_alloc_key(u32 len, u8 hash)
{
	u32 num_bytes = nvme_auth_key_struct_size(len);
	struct nvme_dhchap_key *key = kzalloc(num_bytes, GFP_KERNEL);

	if (key) {
		key->len = len;
		key->hash = hash;
	}
	return key;
}
EXPORT_SYMBOL_GPL(nvme_auth_alloc_key);

void nvme_auth_free_key(struct nvme_dhchap_key *key)
{
	if (!key)
		return;
	kfree_sensitive(key->key);
	kfree(key);
	kfree_sensitive(key);
}
EXPORT_SYMBOL_GPL(nvme_auth_free_key);

u8 *nvme_auth_transform_key(struct nvme_dhchap_key *key, char *nqn)
struct nvme_dhchap_key *nvme_auth_transform_key(
		struct nvme_dhchap_key *key, char *nqn)
{
	const char *hmac_name;
	struct crypto_shash *key_tfm;
	struct shash_desc *shash;
	u8 *transformed_key;
	int ret;
	struct nvme_dhchap_key *transformed_key;
	int ret, key_len;

	if (!key || !key->key) {
	if (!key) {
		pr_warn("No key specified\n");
		return ERR_PTR(-ENOKEY);
	}
	if (key->hash == 0) {
		transformed_key = kmemdup(key->key, key->len, GFP_KERNEL);
		return transformed_key ? transformed_key : ERR_PTR(-ENOMEM);
		key_len = nvme_auth_key_struct_size(key->len);
		transformed_key = kmemdup(key, key_len, GFP_KERNEL);
		if (!transformed_key)
			return ERR_PTR(-ENOMEM);
		return transformed_key;
	}
	hmac_name = nvme_auth_hmac_name(key->hash);
	if (!hmac_name) {
@@ -253,7 +262,7 @@ u8 *nvme_auth_transform_key(struct nvme_dhchap_key *key, char *nqn)

	key_tfm = crypto_alloc_shash(hmac_name, 0, 0);
	if (IS_ERR(key_tfm))
		return (u8 *)key_tfm;
		return ERR_CAST(key_tfm);

	shash = kmalloc(sizeof(struct shash_desc) +
			crypto_shash_descsize(key_tfm),
@@ -263,7 +272,8 @@ u8 *nvme_auth_transform_key(struct nvme_dhchap_key *key, char *nqn)
		goto out_free_key;
	}

	transformed_key = kzalloc(crypto_shash_digestsize(key_tfm), GFP_KERNEL);
	key_len = crypto_shash_digestsize(key_tfm);
	transformed_key = nvme_auth_alloc_key(key_len, key->hash);
	if (!transformed_key) {
		ret = -ENOMEM;
		goto out_free_shash;
@@ -282,7 +292,7 @@ u8 *nvme_auth_transform_key(struct nvme_dhchap_key *key, char *nqn)
	ret = crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
	if (ret < 0)
		goto out_free_transformed_key;
	ret = crypto_shash_final(shash, transformed_key);
	ret = crypto_shash_final(shash, transformed_key->key);
	if (ret < 0)
		goto out_free_transformed_key;

@@ -292,7 +302,7 @@ u8 *nvme_auth_transform_key(struct nvme_dhchap_key *key, char *nqn)
	return transformed_key;

out_free_transformed_key:
	kfree_sensitive(transformed_key);
	nvme_auth_free_key(transformed_key);
out_free_shash:
	kfree(shash);
out_free_key:
+182 −0
Original line number Diff line number Diff line
// 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 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;
	}
	match_len = strlen(key->description);
	pr_debug("%s: id %s len %zd\n", __func__, key->description, match_len);

	if (!match_data->raw_data) {
		pr_debug("%s: no match data\n", __func__);
		return false;
	}
	match_id = match_data->raw_data;
	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,
		int hmac, 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, "NVMe0%c%02d %s %s",
		 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,
 * and SHA-384 should be preferred to SHA-256.
 */
struct nvme_tls_psk_priority_list {
	bool generated;
	enum nvme_tcp_tls_cipher cipher;
} nvme_tls_psk_prio[] = {
	{ .generated = false,
	  .cipher = NVME_TCP_TLS_CIPHER_SHA384, },
	{ .generated = false,
	  .cipher = NVME_TCP_TLS_CIPHER_SHA256, },
	{ .generated = true,
	  .cipher = NVME_TCP_TLS_CIPHER_SHA384, },
	{ .generated = true,
	  .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;
		enum nvme_tcp_tls_cipher cipher = nvme_tls_psk_prio[prio].cipher;

		tls_key = nvme_tls_psk_lookup(keyring, hostnqn, subnqn,
					      cipher, 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);

int 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;
}
EXPORT_SYMBOL_GPL(nvme_keyring_init);

void nvme_keyring_exit(void)
{
	unregister_key_type(&nvme_tls_psk_key_type);
	key_revoke(nvme_keyring);
	key_put(nvme_keyring);
}
EXPORT_SYMBOL_GPL(nvme_keyring_exit);
+17 −7
Original line number Diff line number Diff line
@@ -92,16 +92,26 @@ config NVME_TCP

	  If unsure, say N.

config NVME_AUTH
config NVME_TCP_TLS
	bool "NVMe over Fabrics TCP TLS encryption support"
	depends on NVME_TCP
	select NVME_COMMON
	select NVME_KEYRING
	select NET_HANDSHAKE
	select KEYS
	help
	  Enables TLS encryption for NVMe TCP using the netlink handshake API.

	  The TLS handshake daemon is availble at
	  https://github.com/oracle/ktls-utils.

	  If unsure, say N.

config NVME_HOST_AUTH
	bool "NVM Express over Fabrics In-Band Authentication"
	depends on NVME_CORE
	select NVME_COMMON
	select CRYPTO
	select CRYPTO_HMAC
	select CRYPTO_SHA256
	select CRYPTO_SHA512
	select CRYPTO_DH
	select CRYPTO_DH_RFC7919_GROUPS
	select NVME_AUTH
	help
	  This provides support for NVMe over Fabrics In-Band Authentication.

Loading