nvme: implement secure concatenation
Subsystems (5)
v4 cover nvme: implement secure concatenation
Hi all,
here's my attempt to implement secure concatenation for NVMe-of TCP as outlined in TP8018.
Secure concatenation means that a TLS PSK is generated from the key material negotiated by the DH-HMAC-CHAP protocol, and the TLS PSK is then used for a subsequent TLS connection.
The difference between the original definition of secure concatenation and the method outlined in TP8018 is that with TP8018 the connection is reset after DH-HMAC-CHAP negotiation, and a new connection is setup with the generated TLS PSK.
To implement that I have decided on resetting the connection from the nvme-tcp driver after the initial connection has been set up. Another way would have been to offload the connection reset to userspace, and let nvme-cli reset the connection. But that would be a modification to the userspace interface, and hence I didn't go that way. The drawback with this approach is that we'll create all I/O queues before resetting for TLS, as the current implementation of the TCP driver doesn't allow us to skip I/O queue initialisation.
But the queues are never started, and the namespace scan is skipped, so the I/O queues are never visible to userspace until TLS has been enabled.
As usual, comments and reviews are welcome.
Patchset can be found at git.kernel.org:/pub/scm/linux/kernel/git/hare/nvme.git branch secure-concat.v4
Changes to v3:
- Include reviews from Sagi
- Do not start I/O queues after DH-HMAC-CHAP negotiation
- Use bool to indicate TLS has been enabled on a queue
- Add 'tls_keyring' sysfs attribute
- Add 'tls_configured_key' sysfs attribute
Changes to v2:
- Fixup reset after dhchap negotiation
- Disable namespace scanning on I/O queues after
dhchap negotiation
- Reworked TLS key handling (again)
Changes to the original submission:
- Sanitize TLS key handling
- Fixup modconfig compilation
Hannes Reinecke (19):
nvme-keyring: restrict match length for version '1' identifiers crypto,fs: Separate out hkdf_extract() and hkdf_expand()
nvme: add nvme_auth_generate_psk()
nvme: add nvme_auth_generate_digest()
nvme: add nvme_auth_derive_tls_psk()
nvme-keyring: add nvme_tls_psk_refresh()
nvme-tcp: sanitize TLS key handling
nvme-tcp: check for invalidated or revoked key
nvme: add a newline to the 'tls_key' sysfs attribute
nvme-sysfs: add 'tls_configured_key' sysfs attribute
nvme-sysfs: add 'tls_keyring' attribute
nvme-tcp: request secure channel concatenation
nvme-fabrics: reset connection for secure concatenation
nvme-tcp: reset after recovery for secure concatenation
nvme-tcp: do not start queues when TLS is not enabled for secure
concatenation
nvmet-auth: allow to clear DH-HMAC-CHAP keys
nvme-target: do not check authentication status for admin commands
twice
nvme-target: do not check authentication status for I/O commands twice
nvmet-tcp: support secure channel concatenation
crypto/Makefile | 1 + crypto/hkdf.c | 112 +++++++++ drivers/nvme/common/auth.c | 303 +++++++++++++++++++++++++ drivers/nvme/common/keyring.c | 103 ++++++++- drivers/nvme/host/auth.c | 105 ++++++++- drivers/nvme/host/core.c | 9 +- drivers/nvme/host/fabrics.c | 40 +++- drivers/nvme/host/fabrics.h | 3 + drivers/nvme/host/nvme.h | 2 +- drivers/nvme/host/sysfs.c | 37 ++- drivers/nvme/host/tcp.c | 130 +++++++++-- drivers/nvme/target/admin-cmd.c | 3 +- drivers/nvme/target/auth.c | 84 ++++++- drivers/nvme/target/core.c | 3 - drivers/nvme/target/fabrics-cmd-auth.c | 46 +++- drivers/nvme/target/fabrics-cmd.c | 29 ++- drivers/nvme/target/nvmet.h | 30 ++- drivers/nvme/target/tcp.c | 25 +- fs/crypto/hkdf.c | 68 +----- include/crypto/hkdf.h | 18 ++ include/linux/nvme-auth.h | 7 + include/linux/nvme-keyring.h | 10 +- include/linux/nvme.h | 7 + 23 files changed, 1051 insertions(+), 124 deletions(-) create mode 100644 crypto/hkdf.c create mode 100644 include/crypto/hkdf.h
Signature · 1 line
2.35.3
PATCH v4 1/19 nvme-keyring: restrict match length for version '1' identifiers
TP8018 changed the TLS PSK identifiers to append a PSK hash value, so to lookup identifiers we should just consider the length of the match value, not the length of the identifiers to compare against.
And we should modify the PSK lookup algorithm to prefer v1 identifiers as they can be uniquely identified.
--- drivers/nvme/common/keyring.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-)
drivers/nvme/common/keyring.c +25 -7
@@ -36,14 +36,12 @@ static bool nvme_tls_psk_match(const struct key *key,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; + 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);
@@ -71,7 +69,7 @@ static struct key_type nvme_tls_psk_key_type = {static struct key *nvme_tls_psk_lookup (struct key *keyring,const char *hostnqn,const char *subnqn, -int hmac,bool generated) +u8 hmac,u8 psk_ver,bool generated) {char *identity;size_t identity_len = (NVMF_NQN_SIZE ) *2 +11 ;
@@ -82,8 +80,8 @@ static struct key *nvme_tls_psk_lookup(struct key *keyring,if (!identity)return ERR_PTR (-ENOMEM ); -snprintf (identity, identity_len,"NVMe0%c%02d %s %s" , - generated ?'G' :'R' , hmac, hostnqn, subnqn); +snprintf (identity, identity_len,"NVMe%u%c%02u %s %s" , + psk_ver, generated ?'G' :'R' , hmac, hostnqn, subnqn);if (!keyring) keyring = nvme_keyring;
@@ -109,19 +107,38 @@ static struct key *nvme_tls_psk_lookup(struct key *keyring,* * 'Retained' PSKs (ie 'generated == false') * should be preferred to 'generated' PSKs, +* PSKs with hash (psk_ver 1) should be +* preferred to PSKs without (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 , }, };
@@ -137,10 +154,11 @@ key_serial_t nvme_tls_psk_default(struct key *keyring,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, generated); + cipher, ver, generated);if (!IS_ERR (tls_key)) { tls_key_id = tls_key->serial;key_put (tls_key);
Signature · 1 line
2.35.3
PATCH v4 2/19 crypto,fs: Separate out hkdf_extract() and hkdf_expand()
In reply to Hannes ReineckeSeparate out the HKDF functions into a separate file to make them available to other callers.
--- crypto/Makefile | 1 + crypto/hkdf.c | 112 ++++++++++++++++++++++++++++++++++++++++++ fs/crypto/hkdf.c | 68 ++++--------------------- include/crypto/hkdf.h | 18 +++++++ 4 files changed, 140 insertions(+), 59 deletions(-) create mode 100644 crypto/hkdf.c create mode 100644 include/crypto/hkdf.h
crypto/Makefile +1 -0
@@ -26,6 +26,7 @@ obj-$(CONFIG_CRYPTO_ECHAINIV) += echainiv.o crypto_hash-y += ahash.o crypto_hash-y += shash.o +crypto_hash-y += hkdf.o obj-$(CONFIG_CRYPTO_HASH2) += crypto_hash.o obj-$(CONFIG_CRYPTO_AKCIPHER2) += akcipher.o
crypto/hkdf.c new file +112 -0
112 lines not on this page · raw message
fs/crypto/hkdf.c +9 -59
96 lines not on this page · raw message
include/crypto/hkdf.h new file +18 -0
@@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* +* HKDF: HMAC-based Key Derivation Function (HKDF), RFC 5869 +* +* Extracted from fs/crypto/hkdf.c, which has +* Copyright 2019 Google LLC +*/ + +#ifndef _CRYPTO_HKDF_H +#define _CRYPTO_HKDF_H + +int hkdf_extract (struct crypto_shash *hmac_tfm,const u8 *ikm, +unsigned int ikmlen,u8 *prk); +int hkdf_expand (struct crypto_shash *hmac_tfm, +const u8 *info,unsigned int infolen, +u8 *okm,unsigned int okmlen); + +#endif
Signature · 1 line
2.35.3
PATCH v4 3/19 nvme: add nvme_auth_generate_psk()
In reply to Hannes ReineckeAdd a function to generate a NVMe PSK from the shared credentials negotiated by DH-HMAC-CHAP.
--- drivers/nvme/common/auth.c | 87 ++++++++++++++++++++++++++++++++++++++ include/linux/nvme-auth.h | 3 ++ 2 files changed, 90 insertions(+)
drivers/nvme/common/auth.c +87 -0
@@ -11,6 +11,7 @@#include <asm/unaligned.h> #include <crypto/hash.h> #include <crypto/dh.h> +#include <crypto/hkdf.h> #include <linux/nvme.h> #include <linux/nvme-auth.h>
@@ -471,5 +472,91 @@ int nvme_auth_generate_key(u8 *secret, struct nvme_dhchap_key **ret_key) }EXPORT_SYMBOL_GPL (nvme_auth_generate_key); +/* +* nvme_auth_generate_psk - Generate a PSK for TLS +* +* Generate a PSK for TLS as specified in NVMe base specification, section 8.13.5.9: +* Generated PSK for TLS +* +* The generated PSK for TLS shall be computed applying the HMAC function using the +* hash function H( ) selected by the HashID parameter in the DH-HMAC-CHAP_Challenge +* message with the session key KS as key to the concatenation of the two challenges +* C1 and C2 (i.e., generated PSK = HMAC(KS, C1 || C2)). +*/ +int nvme_auth_generate_psk (u8 hmac_id,u8 *skey,size_t skey_len, +u8 *c1,u8 *c2,size_t hash_len,u8 **ret_psk,size_t *ret_len) +{ +struct crypto_shash *tfm; +struct shash_desc *shash; +u8 *psk; +const char *hmac_name; +int ret, psk_len; + +if (!c1 || !c2) { +pr_warn ("%s: invalid parameter\n" , __func__); +return -EINVAL ; + } + + hmac_name =nvme_auth_hmac_name (hmac_id); +if (!hmac_name) { +pr_warn ("%s: invalid hash algoritm %d\n" , + __func__, hmac_id); +return -EINVAL ; + } + + tfm =crypto_alloc_shash (hmac_name,0 ,0 ); +if (IS_ERR (tfm)) +return PTR_ERR (tfm); + + psk_len =crypto_shash_digestsize (tfm); + psk =kzalloc (psk_len,GFP_KERNEL ); +if (!psk) { + ret = -ENOMEM ; + goto out_free_tfm; + } + + shash =kmalloc (sizeof (struct shash_desc ) + +crypto_shash_descsize (tfm), +GFP_KERNEL ); +if (!shash) { + ret = -ENOMEM ; + goto out_free_psk; + } + + shash->tfm = tfm; + ret =crypto_shash_setkey (tfm, skey, skey_len); +if (ret) + goto out_free_shash; + + ret =crypto_shash_init (shash); +if (ret) + goto out_free_shash; + + ret =crypto_shash_update (shash, c1, hash_len); +if (ret) + goto out_free_shash; + + ret =crypto_shash_update (shash, c2, hash_len); +if (ret) + goto out_free_shash; + + ret =crypto_shash_final (shash, psk); +if (!ret) { + *ret_psk = psk; + *ret_len = psk_len; + } + +out_free_shash: +kfree_sensitive (shash); +out_free_psk: +if (ret) +kfree_sensitive (psk); +out_free_tfm: +crypto_free_shash (tfm); + +return ret; +} +EXPORT_SYMBOL_GPL (nvme_auth_generate_psk); +MODULE_DESCRIPTION ("NVMe Authentication framework" );MODULE_LICENSE ("GPL v2" );
include/linux/nvme-auth.h +3 -0
@@ -40,5 +40,8 @@ int nvme_auth_gen_pubkey(struct crypto_kpp *dh_tfm,int nvme_auth_gen_shared_secret (struct crypto_kpp *dh_tfm,u8 *ctrl_key,size_t ctrl_key_len,u8 *sess_key,size_t sess_key_len); +int nvme_auth_generate_psk (u8 hmac_id,u8 *skey,size_t skey_len, +u8 *c1,u8 *c2,size_t hash_len, +u8 **ret_psk,size_t *ret_len);#endif /* _NVME_AUTH_H */
Signature · 1 line
2.35.3
PATCH v4 4/19 nvme: add nvme_auth_generate_digest()
In reply to Hannes ReineckeAdd a function to calculate the PSK digest as specified in TP8018.
--- drivers/nvme/common/auth.c | 126 +++++++++++++++++++++++++++++++++++++ include/linux/nvme-auth.h | 2 + 2 files changed, 128 insertions(+)
drivers/nvme/common/auth.c +126 -0
@@ -558,5 +558,131 @@ int nvme_auth_generate_psk(u8 hmac_id, u8 *skey, size_t skey_len, }EXPORT_SYMBOL_GPL (nvme_auth_generate_psk); +/* +* Generate a TLS PSK digest as specified in TP8018 Section 3.6.1.3: +* TLS PSK and PSK identity Derivation +* +* The PSK digest shall be computed by encoding in Base64 (refer to RFC 4648) +* the result of the application of the HMAC function using the hash function +* specified in item 4 above (ie the hash function of the cipher suite associated +* with the PSK identity) with the PSK as HMAC key to the concatenation of: +* - the NQN of the host (i.e., NQNh) not including the null terminator; +* - a space character; +* - the NQN of the NVM subsystem (i.e., NQNc) not including the null terminator; +* - a space character; and +* - the seventeen ASCII characters "NVMe-over-Fabrics" +* (i.e., <PSK digest> = Base64(HMAC(PSK, NQNh || " " || NQNc || " " || "NVMe-over-Fabrics"))). +* The length of the PSK digest depends on the hash function used to compute +* it as follows: +* - If the SHA-256 hash function is used, the resulting PSK digest is 44 characters long; or +* - If the SHA-384 hash function is used, the resulting PSK digest is 64 characters long. +*/ +int nvme_auth_generate_digest (u8 hmac_id,u8 *psk,size_t psk_len, +char *subsysnqn,char *hostnqn,u8 **ret_digest) +{ +struct crypto_shash *tfm; +struct shash_desc *shash; +u8 *digest, *hmac; +const char *hmac_name; +size_t digest_len, hmac_len; +int ret; + +if (WARN_ON (!subsysnqn || !hostnqn)) +return -EINVAL ; + + hmac_name =nvme_auth_hmac_name (hmac_id); +if (!hmac_name) { +pr_warn ("%s: invalid hash algoritm %d\n" , + __func__, hmac_id); +return -EINVAL ; + } + +switch (nvme_auth_hmac_hash_len (hmac_id)) { +case 32 : + hmac_len =44 ; +break ; +case 48 : + hmac_len =64 ; +break ; +default : +pr_warn ("%s: invalid hash algorithm '%s'\n" , + __func__, hmac_name); +return -EINVAL ; + } + + hmac =kzalloc (hmac_len +1 ,GFP_KERNEL ); +if (!hmac) +return -ENOMEM ; + + tfm =crypto_alloc_shash (hmac_name,0 ,0 ); +if (IS_ERR (tfm)) { + ret =PTR_ERR (tfm); + goto out_free_hmac; + } + + digest_len =crypto_shash_digestsize (tfm); + digest =kzalloc (digest_len,GFP_KERNEL ); +if (!digest) { + ret = -ENOMEM ; + goto out_free_tfm; + } + + shash =kmalloc (sizeof (struct shash_desc ) + +crypto_shash_descsize (tfm), +GFP_KERNEL ); +if (!shash) { + ret = -ENOMEM ; + goto out_free_digest; + } + + shash->tfm = tfm; + ret =crypto_shash_setkey (tfm, psk, psk_len); +if (ret) + goto out_free_shash; + + ret =crypto_shash_init (shash); +if (ret) + goto out_free_shash; + + ret =crypto_shash_update (shash, hostnqn,strlen (hostnqn)); +if (ret) + goto out_free_shash; + + ret =crypto_shash_update (shash," " ,1 ); +if (ret) + goto out_free_shash; + + ret =crypto_shash_update (shash, subsysnqn,strlen (subsysnqn)); +if (ret) + goto out_free_shash; + + ret =crypto_shash_update (shash," NVMe-over-Fabrics" ,18 ); +if (ret) + goto out_free_shash; + + ret =crypto_shash_final (shash, digest); +if (ret) + goto out_free_shash; + + ret =base64_encode (digest, digest_len, hmac); +if (ret < hmac_len) + ret = -ENOKEY ; + *ret_digest = hmac; + ret =0 ; + +out_free_shash: +kfree_sensitive (shash); +out_free_digest: +kfree_sensitive (digest); +out_free_tfm: +crypto_free_shash (tfm); +out_free_hmac: +if (ret) +kfree_sensitive (hmac); + +return ret; +} +EXPORT_SYMBOL_GPL (nvme_auth_generate_digest); +MODULE_DESCRIPTION ("NVMe Authentication framework" );MODULE_LICENSE ("GPL v2" );
include/linux/nvme-auth.h +2 -0
@@ -43,5 +43,7 @@ int nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm,int nvme_auth_generate_psk (u8 hmac_id,u8 *skey,size_t skey_len,u8 *c1,u8 *c2,size_t hash_len,u8 **ret_psk,size_t *ret_len); +int nvme_auth_generate_digest (u8 hmac_id,u8 *psk,size_t psk_len, +char *subsysnqn,char *hostnqn,u8 **ret_digest);#endif /* _NVME_AUTH_H */
Signature · 1 line
2.35.3
PATCH v4 5/19 nvme: add nvme_auth_derive_tls_psk()
In reply to Hannes ReineckeAdd a function to derive the TLS PSK as specified TP8018.
--- drivers/nvme/common/auth.c | 90 ++++++++++++++++++++++++++++++++++++++ include/linux/nvme-auth.h | 2 + 2 files changed, 92 insertions(+)
drivers/nvme/common/auth.c +90 -0
@@ -684,5 +684,95 @@ int nvme_auth_generate_digest(u8 hmac_id, u8 *psk, size_t psk_len, }EXPORT_SYMBOL_GPL (nvme_auth_generate_digest); +/* +* Derive a TLS PSK as specified in TP8018 Section 3.6.1.3: +* TLS PSK and PSK identity Derivation +* +* The TLS PSK shall be derived as follows from an input PSK +* (i.e., either a retained PSK or a generated PSK) and a PSK +* identity using the HKDF-Extract and HKDF-Expand-Label operations +* (refer to RFC 5869 and RFC 8446) where the hash function is the +* one specified by the hash specifier of the PSK identity: +* 1. PRK = HKDF-Extract(0, Input PSK); and +* 2. TLS PSK = HKDF-Expand-Label(PRK,"nvme-tls-psk", PskIdentityContext, L), +* where PskIdentityContext is the hash identifier indicated in +* the PSK identity concatenated to a space character and to the +* Base64 PSK digest (i.e., "<hash> <PSK digest>") and L is the +* output size in bytes of the hash function (i.e., 32 for SHA-256 +* and 48 for SHA-384). +*/ +int nvme_auth_derive_tls_psk (int hmac_id,u8 *psk,size_t psk_len, +u8 *psk_digest,u8 **ret_psk) +{ +struct crypto_shash *hmac_tfm; +const char *hmac_name; +const char *psk_prefix ="tls13 nvme-tls-psk" ; +size_t info_len, prk_len; +char *info; +unsigned char *prk, *tls_key; +int ret; + + hmac_name =nvme_auth_hmac_name (hmac_id); +if (!hmac_name) { +pr_warn ("%s: invalid hash algoritm %d\n" , + __func__, hmac_id); +return -EINVAL ; + } +if (hmac_id ==NVME_AUTH_HASH_SHA512 ) { +pr_warn ("%s: unsupported hash algorithm %s\n" , + __func__, hmac_name); +return -EINVAL ; + } + + hmac_tfm =crypto_alloc_shash (hmac_name,0 ,0 ); +if (IS_ERR (hmac_tfm)) +return PTR_ERR (hmac_tfm); + + prk_len =crypto_shash_digestsize (hmac_tfm); + prk =kzalloc (prk_len,GFP_KERNEL ); +if (!prk) { + ret = -ENOMEM ; + goto out_free_shash; + } + + ret =hkdf_extract (hmac_tfm, psk, psk_len, prk); +if (ret) + goto out_free_prk; + + ret =crypto_shash_setkey (hmac_tfm, prk, prk_len); +if (ret) + goto out_free_prk; + + info_len =strlen (psk_digest) +strlen (psk_prefix) +1 ; + info =kzalloc (info_len,GFP_KERNEL ); +if (!info) + goto out_free_prk; + +memcpy (info, psk_prefix,strlen (psk_prefix)); +memcpy (info +strlen (psk_prefix), psk_digest,strlen (psk_digest)); + + tls_key =kzalloc (psk_len,GFP_KERNEL ); +if (!tls_key) { + ret = -ENOMEM ; + goto out_free_info; + } + ret =hkdf_expand (hmac_tfm, info,strlen (info), tls_key, psk_len); +if (ret) { +kfree (tls_key); + goto out_free_info; + } + *ret_psk = tls_key; + +out_free_info: +kfree (info); +out_free_prk: +kfree (prk); +out_free_shash: +crypto_free_shash (hmac_tfm); + +return ret; +} +EXPORT_SYMBOL_GPL (nvme_auth_derive_tls_psk); +MODULE_DESCRIPTION ("NVMe Authentication framework" );MODULE_LICENSE ("GPL v2" );
include/linux/nvme-auth.h +2 -0
@@ -45,5 +45,7 @@ int nvme_auth_generate_psk(u8 hmac_id, u8 *skey, size_t skey_len, u8 **ret_psk, size_t *ret_len);int nvme_auth_generate_digest (u8 hmac_id,u8 *psk,size_t psk_len,char *subsysnqn,char *hostnqn,u8 **ret_digest); +int nvme_auth_derive_tls_psk (int hmac_id,u8 *psk,size_t psk_len, +u8 *psk_digest,u8 **ret_psk);#endif /* _NVME_AUTH_H */
Signature · 1 line
2.35.3
PATCH v4 6/19 nvme-keyring: add nvme_tls_psk_refresh()
In reply to Hannes ReineckeFrom: Hannes Reinecke <[email protected]>
Add a function to refresh a generated PSK in the specified keyring.
--- drivers/nvme/common/keyring.c | 50 +++++++++++++++++++++++++++++++++++ include/linux/nvme-keyring.h | 7 +++++ 2 files changed, 57 insertions(+)
drivers/nvme/common/keyring.c +50 -0
@@ -102,6 +102,56 @@ static struct key *nvme_tls_psk_lookup(struct key *keyring,return key_ref_to_ptr (keyref); } +struct key *nvme_tls_psk_refresh (struct key *keyring,char *hostnqn,char *subnqn, +u8 hmac_id,bool generated,u8 *data,size_t data_len,char *digest) +{ +key_perm_t keyperm = +KEY_POS_SEARCH |KEY_POS_VIEW |KEY_POS_READ | +KEY_POS_WRITE |KEY_POS_LINK |KEY_POS_SETATTR | +KEY_USR_SEARCH |KEY_USR_VIEW |KEY_USR_READ ; +char *identity; +size_t identity_len = (NVMF_NQN_SIZE ) *2 +77 ; +key_ref_t keyref; +key_serial_t keyring_id; +struct key *key; + +if (!hostnqn || !subnqn || !data || !data_len) +return ERR_PTR (-EINVAL ); + + identity =kzalloc (identity_len,GFP_KERNEL ); +if (!identity) +return ERR_PTR (-ENOMEM ); + +snprintf (identity, identity_len,"NVMe1%c%02d %s %s %s" , + generated ?'G' :'R' , hmac_id, hostnqn, subnqn, digest); + +if (!keyring) + keyring = nvme_keyring; + keyring_id =key_serial (keyring); +pr_debug ("keyring %x refresh tls psk '%s'\n" , + keyring_id, identity); + keyref =key_create_or_update (make_key_ref (keyring, true), +"psk" , identity, data, data_len, + keyperm,KEY_ALLOC_NOT_IN_QUOTA | +KEY_ALLOC_BUILT_IN | +KEY_ALLOC_BYPASS_RESTRICTION ); +if (IS_ERR (keyref)) { +pr_debug ("refresh tls psk '%s' failed, error %ld\n" , + identity,PTR_ERR (keyref)); +kfree (identity); +return ERR_PTR (-ENOKEY ); + } +kfree (identity); +/* +* Set the default timeout to 1 hour +* as suggested in TP8018. +*/ + key =key_ref_to_ptr (keyref); +key_set_timeout (key,3600 ); +return key; +} +EXPORT_SYMBOL_GPL (nvme_tls_psk_refresh); +/* * NVMe PSK priority list *
include/linux/nvme-keyring.h +7 -0
@@ -8,6 +8,8 @@#if IS_ENABLED (CONFIG_NVME_KEYRING ) +struct key *nvme_tls_psk_refresh (struct key *keyring,char *hostnqn,char *subnqn, +u8 hmac_id,bool generated,u8 *data,size_t data_len,char *digest);key_serial_t nvme_tls_psk_default (struct key *keyring,const char *hostnqn,const char *subnqn);
@@ -15,6 +17,11 @@ key_serial_t nvme_keyring_id(void);#else +static struct key *nvme_tls_psk_refresh (struct key *keyring,char *hostnqn,char *subnqn, +u8 hmac_id,bool generated,u8 *data,size_t data_len,char *digest) +{ +return -ENOTSUPP ; +}static inline key_serial_t nvme_tls_psk_default (struct key *keyring,const char *hostnqn,const char *subnqn) {
Signature · 1 line
2.35.3
PATCH v4 7/19 nvme-tcp: sanitize TLS key handling
In reply to Hannes ReineckeThere is a difference between TLS configured (ie the user has provisioned/requested a key) and TLS enabled (ie the connection is encrypted with TLS). This becomes important for secure concatenation, where the initial authentication is run unencrypted (ie with TLS configured, but not enabled), and then the queue is reset to run over TLS (ie TLS configured _and_ enabled).
So to differentiate between those two states store the provisioned key in opts->tls_key (as we're using the same TLS key for all queues) and only the key serial of the key negotiated by the TLS handshake in queue->tls_pskid.
--- drivers/nvme/host/core.c | 1 - drivers/nvme/host/nvme.h | 2 +- drivers/nvme/host/sysfs.c | 4 ++-- drivers/nvme/host/tcp.c | 47 ++++++++++++++++++++++++++++----------- 4 files changed, 37 insertions(+), 17 deletions(-)
drivers/nvme/host/core.c +0 -1
@@ -4595,7 +4595,6 @@ static void nvme_free_ctrl(struct device *dev)if (!subsys || ctrl->instance != subsys->instance)ida_free (&nvme_instance_ida, ctrl->instance); -key_put (ctrl->tls_key);nvme_free_cels (ctrl);nvme_mpath_uninit (ctrl);nvme_auth_stop (ctrl);
drivers/nvme/host/nvme.h +1 -1
@@ -364,7 +364,7 @@ struct nvme_ctrl {struct nvme_dhchap_key *ctrl_key;u16 transaction;#endif -struct key *tls_key; +key_serial_t tls_pskid;/* Power saving configuration */ u64 ps_max_latency_us;
drivers/nvme/host/sysfs.c +2 -2
@@ -671,9 +671,9 @@ static ssize_t tls_key_show(struct device *dev, {struct nvme_ctrl *ctrl =dev_get_drvdata (dev); -if (!ctrl->tls_key) +if (!ctrl->tls_pskid)return 0 ; -return sysfs_emit (buf,"%08x" ,key_serial (ctrl->tls_key)); +return sysfs_emit (buf,"%08x" , ctrl->tls_pskid); }static DEVICE_ATTR_RO (tls_key);#endif
drivers/nvme/host/tcp.c +34 -13
123 lines not on this page · raw message
Signature · 1 line
2.35.3
PATCH v4 8/19 nvme-tcp: check for invalidated or revoked key
In reply to Hannes Reineckekey_lookup() will always return a key, even if that key is revoked or invalidated. So check for invalid keys before continuing.
--- drivers/nvme/common/keyring.c | 21 +++++++++++++++++++++ drivers/nvme/host/fabrics.c | 2 +- drivers/nvme/host/tcp.c | 2 +- include/linux/nvme-keyring.h | 3 ++- 4 files changed, 25 insertions(+), 3 deletions(-)
drivers/nvme/common/keyring.c +21 -0
@@ -20,6 +20,27 @@ key_serial_t nvme_keyring_id(void) }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);
drivers/nvme/host/fabrics.c +1 -1
@@ -638,7 +638,7 @@ static struct key *nvmf_parse_key(int key_id)return ERR_PTR (-EINVAL ); } - key =key_lookup (key_id); + key =nvme_tls_key_lookup (key_id);if (IS_ERR (key))pr_err ("key id %08x not found\n" , key_id);else
drivers/nvme/host/tcp.c +1 -1
@@ -1590,7 +1590,7 @@ static void nvme_tcp_tls_done(void *data, int status, key_serial_t pskid) goto out_complete; } - tls_key =key_lookup (pskid); + tls_key =nvme_tls_key_lookup (pskid);if (IS_ERR (tls_key)) {dev_warn (ctrl->ctrl.device,"queue %d: Invalid key %x\n" , qid, pskid);
include/linux/nvme-keyring.h +2 -1
@@ -14,7 +14,7 @@ key_serial_t nvme_tls_psk_default(struct key *keyring, const char *hostnqn, const char *subnqn);key_serial_t nvme_keyring_id (void ); - +struct key *nvme_tls_key_lookup (key_serial_t key_id);#else static struct key *nvme_tls_psk_refresh(struct key *keyring, char *hostnqn, char *subnqn,
@@ -31,5 +31,6 @@ static inline key_serial_t nvme_keyring_id(void) {return 0 ; } +static inline struct key *nvme_tls_key_lookup (key_serial_t key_id) {return ERR_PTR (-ENOTSUPP ); }#endif /* !CONFIG_NVME_KEYRING */ #endif /* _NVME_KEYRING_H */
Signature · 1 line
2.35.3
PATCH v4 9/19 nvme: add a newline to the 'tls_key' sysfs attribute
In reply to Hannes ReineckePrint a newline for easier userspace handling.
--- drivers/nvme/host/sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/nvme/host/sysfs.c +1 -1
@@ -673,7 +673,7 @@ static ssize_t tls_key_show(struct device *dev,if (!ctrl->tls_pskid)return 0 ; - return sysfs_emit(buf, "%08x", ctrl->tls_pskid); + return sysfs_emit(buf, "%08x\n", ctrl->tls_pskid); }static DEVICE_ATTR_RO (tls_key);#endif
Signature · 1 line
2.35.3
PATCH v4 10/19 nvme-sysfs: add 'tls_configured_key' sysfs attribute
In reply to Hannes ReineckeThere is a difference between the negotiated TLS key (which is always present for a TLS encrypted connection) and the configured TLS key (which is specified with the --tls_key command line option). To differentate between these two add a new sysfs attribute 'tls_configured_key' to hold the specified on the command line.
--- drivers/nvme/host/sysfs.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
drivers/nvme/host/sysfs.c +17 -0
@@ -676,6 +676,19 @@ static ssize_t tls_key_show(struct device *dev,return sysfs_emit (buf,"%08x\n" , ctrl->tls_pskid); }static DEVICE_ATTR_RO (tls_key); + +static ssize_t tls_configured_key_show (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct nvme_ctrl *ctrl =dev_get_drvdata (dev); +struct key *key = ctrl->opts->tls_key; + +if (!key) +return 0 ; + +return sysfs_emit (buf,"%08x\n" ,key_serial (key)); +} +static DEVICE_ATTR_RO (tls_configured_key);#endif static struct attribute *nvme_dev_attrs[] = {
@@ -707,6 +720,7 @@ static struct attribute *nvme_dev_attrs[] = {#endif #ifdef CONFIG_NVME_TCP_TLS &dev_attr_tls_key.attr, + &dev_attr_tls_configured_key.attr, #endif &dev_attr_adm_passthru_err_log_enabled.attr,NULL
@@ -742,6 +756,9 @@ static umode_t nvme_dev_attrs_are_visible(struct kobject *kobj,if (a == &dev_attr_tls_key.attr && (!ctrl->opts ||strcmp (ctrl->opts->transport,"tcp" )))return 0 ; +if (a == &dev_attr_tls_configured_key.attr && + (!ctrl->opts ||strcmp (ctrl->opts->transport,"tcp" ))) +return 0 ;#endif return a->mode;
Signature · 1 line
2.35.3
PATCH v4 11/19 nvme-sysfs: add 'tls_keyring' attribute
In reply to Hannes ReineckeAdd a 'tls_keyring' attribute to display the contents of the --keyring option from the connect string. Adding this attribute allows us to recreate the original connect string from sysfs settings.
--- drivers/nvme/host/sysfs.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
drivers/nvme/host/sysfs.c +16 -0
@@ -689,6 +689,18 @@ static ssize_t tls_configured_key_show(struct device *dev,return sysfs_emit (buf,"%08x\n" ,key_serial (key)); }static DEVICE_ATTR_RO (tls_configured_key); + +static ssize_t tls_keyring_show (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct nvme_ctrl *ctrl =dev_get_drvdata (dev); +struct key *keyring = ctrl->opts->keyring; + +if (!keyring) +return 0 ; +return sysfs_emit (buf,"%s\n" , keyring->description); +} +static DEVICE_ATTR_RO (tls_keyring);#endif static struct attribute *nvme_dev_attrs[] = {
@@ -721,6 +733,7 @@ static struct attribute *nvme_dev_attrs[] = {#ifdef CONFIG_NVME_TCP_TLS &dev_attr_tls_key.attr, &dev_attr_tls_configured_key.attr, + &dev_attr_tls_keyring.attr, #endif &dev_attr_adm_passthru_err_log_enabled.attr,NULL
@@ -759,6 +772,9 @@ static umode_t nvme_dev_attrs_are_visible(struct kobject *kobj,if (a == &dev_attr_tls_configured_key.attr && (!ctrl->opts ||strcmp (ctrl->opts->transport,"tcp" )))return 0 ; +if (a == &dev_attr_tls_keyring.attr && + (!ctrl->opts ||strcmp (ctrl->opts->transport,"tcp" ))) +return 0 ;#endif return a->mode;
Signature · 1 line
2.35.3
PATCH v4 12/19 nvme-tcp: request secure channel concatenation
In reply to Hannes ReineckeAdd a fabrics option 'concat' to request secure channel concatenation. When secure channel concatenation is enabled a 'generated PSK' is inserted into the keyring such that it's available after reset.
--- drivers/nvme/host/auth.c | 105 ++++++++++++++++++++++++++++++++++-- drivers/nvme/host/fabrics.c | 32 ++++++++++- drivers/nvme/host/fabrics.h | 3 ++ drivers/nvme/host/tcp.c | 54 ++++++++++++++++--- include/linux/nvme.h | 7 +++ 5 files changed, 189 insertions(+), 12 deletions(-)
drivers/nvme/host/auth.c +101 -4
@@ -12,6 +12,7 @@#include "nvme.h" #include "fabrics.h" #include <linux/nvme-auth.h> +#include <linux/nvme-keyring.h> #define CHAP_BUF_SIZE 4096static struct kmem_cache *nvme_chap_buf_cache;
@@ -131,7 +132,12 @@ static int nvme_auth_set_dhchap_negotiate_data(struct nvme_ctrl *ctrl, data->auth_type =NVME_AUTH_COMMON_MESSAGES ; data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE ; data->t_id =cpu_to_le16 (chap->transaction); - data->sc_c =0 ;/* No secure channel concatenation */ +if (!ctrl->opts->concat || chap->qid !=0 ) + data->sc_c =NVME_AUTH_SECP_NOSC ; +else if (ctrl->opts->tls_key) + data->sc_c =NVME_AUTH_SECP_REPLACETLSPSK ; +else + data->sc_c =NVME_AUTH_SECP_NEWTLSPSK ; data->napd =1 ; data->auth_protocol[0 ].dhchap.authid =NVME_AUTH_DHCHAP_AUTH_ID ; data->auth_protocol[0 ].dhchap.halen =3 ;
@@ -311,8 +317,9 @@ static int nvme_auth_set_dhchap_reply_data(struct nvme_ctrl *ctrl, data->hl = chap->hash_len; data->dhvlen =cpu_to_le16 (chap->host_key_len);memcpy (data->rval, chap->response, chap->hash_len); -if (ctrl->ctrl_key) { +if (ctrl->ctrl_key) chap->bi_directional = true; +if (ctrl->ctrl_key || ctrl->opts->concat) {get_random_bytes (chap->c2, chap->hash_len); data->cvalid =1 ; memcpy(data->rval + chap->hash_len, chap->c2,
@@ -322,7 +329,10 @@ static int nvme_auth_set_dhchap_reply_data(struct nvme_ctrl *ctrl, }else {memset (chap->c2,0 , chap->hash_len); } - chap->s2 =nvme_auth_get_seqnum (); +if (ctrl->opts->concat) + chap->s2 =0 ; +else + chap->s2 =nvme_auth_get_seqnum (); data->seqnum =cpu_to_le32 (chap->s2);if (chap->host_key_len) { dev_dbg(ctrl->device, "%s: qid %d host public key %*ph\n",
@@ -677,6 +687,76 @@ static void nvme_auth_free_dhchap(struct nvme_dhchap_queue_context *chap)crypto_free_kpp (chap->dh_tfm); } +static int nvme_auth_secure_concat (struct nvme_ctrl *ctrl, +struct nvme_dhchap_queue_context *chap) +{ +u8 *psk, *digest, *tls_psk; +struct key *tls_key; +size_t psk_len; +int ret =0 ; + +if (!chap->sess_key) { +dev_warn (ctrl->device, +"%s: qid %d no session key negotiated\n" , + __func__, chap->qid); +return -ENOKEY ; + } + + ret =nvme_auth_generate_psk (chap->hash_id, chap->sess_key, + chap->sess_key_len, + chap->c1, chap->c2, + chap->hash_len, &psk, &psk_len); +if (ret) { +dev_warn (ctrl->device, +"%s: qid %d failed to generate PSK, error %d\n" , + __func__, chap->qid, ret); +return ret; + } +dev_dbg (ctrl->device, +"%s: generated psk %*ph\n" , __func__, (int )psk_len, psk); + + ret =nvme_auth_generate_digest (chap->hash_id, psk, psk_len, + ctrl->opts->subsysnqn, + ctrl->opts->host->nqn, &digest); +if (ret) { +dev_warn (ctrl->device, +"%s: qid %d failed to generate digest, error %d\n" , + __func__, chap->qid, ret); + goto out_free_psk; + }; +dev_dbg (ctrl->device,"%s: generated digest %s\n" , + __func__, digest); + ret =nvme_auth_derive_tls_psk (chap->hash_id, psk, psk_len, digest, &tls_psk); +if (ret) { +dev_warn (ctrl->device, +"%s: qid %d failed to derive TLS psk, error %d\n" , + __func__, chap->qid, ret); + goto out_free_digest; + }; + + tls_key =nvme_tls_psk_refresh (ctrl->opts->keyring, ctrl->opts->host->nqn, + ctrl->opts->subsysnqn, chap->hash_id, + true, tls_psk, psk_len, digest); +if (IS_ERR (tls_key)) { + ret =PTR_ERR (tls_key); +dev_warn (ctrl->device, +"%s: qid %d failed to insert generated key, error %d\n" , + __func__, chap->qid, ret); + tls_key =NULL ; +kfree_sensitive (tls_psk); + } +if (ctrl->opts->tls_key) { +key_revoke (ctrl->opts->tls_key); +key_put (ctrl->opts->tls_key); + } + ctrl->opts->tls_key = tls_key; +out_free_digest: +kfree_sensitive (digest); +out_free_psk: +kfree_sensitive (psk); +return ret; +} +static void nvme_queue_auth_work (struct work_struct *work) { struct nvme_dhchap_queue_context *chap =
@@ -831,10 +911,21 @@ static void nvme_queue_auth_work(struct work_struct *work)if (ret) chap->error = ret; } -if (!ret) { +if (ret) + goto fail2; +if (chap->qid || !ctrl->opts->concat) { chap->error =0 ;return ; } + ret =nvme_auth_secure_concat (ctrl, chap); +if (ret) { +dev_warn (ctrl->device, +"%s: qid %d failed to enable secure concatenation\n" , + __func__, chap->qid); + chap->error = ret; + }else + chap->error =0 ; +return ; fail2:if (chap->status ==0 )
@@ -912,6 +1003,12 @@ static void nvme_ctrl_auth_work(struct work_struct *work) "qid 0: authentication failed\n");return ; } +/* +* Only run authentication on the admin queue for +* secure concatenation +*/ +if (ctrl->opts->concat) +return ;for (q =1 ; q < ctrl->queue_count; q++) { ret =nvme_auth_negotiate (ctrl, q);
drivers/nvme/host/fabrics.c +30 -2
@@ -463,8 +463,9 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) result =le32_to_cpu (res.u32); ctrl->cntlid = result &0xFFFF ;if (result & (NVME_CONNECT_AUTHREQ_ATR |NVME_CONNECT_AUTHREQ_ASCR )) { -/* Secure concatenation is not implemented */ -if (result &NVME_CONNECT_AUTHREQ_ASCR ) { +/* Check for secure concatenation */ +if ((result &NVME_CONNECT_AUTHREQ_ASCR ) && + !ctrl->opts->concat) {dev_warn (ctrl->device,"qid 0: secure concatenation is not supported\n" ); ret = -EOPNOTSUPP ;
@@ -679,6 +680,7 @@ static const match_table_t opt_tokens = {#endif #ifdef CONFIG_NVME_TCP_TLS { NVMF_OPT_TLS, "tls" }, + { NVMF_OPT_CONCAT, "concat" }, #endif { NVMF_OPT_ERR, NULL } };
@@ -708,6 +710,7 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts, opts->tls = false; opts->tls_key =NULL ; opts->keyring =NULL ; + opts->concat = false; options = o =kstrdup (buf,GFP_KERNEL );if (!options)
@@ -1026,6 +1029,14 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts, } opts->tls = true;break ; +case NVMF_OPT_CONCAT : +if (!IS_ENABLED (CONFIG_NVME_TCP_TLS )) { +pr_err ("TLS is not supported\n" ); + ret = -EINVAL ; + goto out; + } + opts->concat = true; +break ;default :pr_warn ("unknown parameter or missing value '%s' in ctrl creation request\n" , p);
@@ -1052,6 +1063,23 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,pr_warn ("failfast tmo (%d) larger than controller loss tmo (%d)\n" , opts->fast_io_fail_tmo, ctrl_loss_tmo); } +if (opts->concat) { +if (opts->tls) { +pr_err ("Secure concatenation over TLS is not supported\n" ); + ret = -EINVAL ; + goto out; + } +if (opts->tls_key) { +pr_err ("Cannot specify a TLS key for secure concatenation\n" ); + ret = -EINVAL ; + goto out; + } +if (!opts->dhchap_secret) { +pr_err ("Need to enable DH-CHAP for secure concatenation\n" ); + ret = -EINVAL ; + goto out; + } + } opts->host =nvmf_host_add (hostnqn, &hostid);if (IS_ERR (opts->host)) {
drivers/nvme/host/fabrics.h +3 -0
@@ -66,6 +66,7 @@ enum {NVMF_OPT_TLS =1 <<25 ,NVMF_OPT_KEYRING =1 <<26 ,NVMF_OPT_TLS_KEY =1 <<27 , + NVMF_OPT_CONCAT = 1 << 28, };/**
@@ -101,6 +102,7 @@ enum {* @keyring: Keyring to use for key lookups * @tls_key: TLS key for encrypted connections (TCP) * @tls: Start TLS encrypted connections (TCP) +* @concat: Enabled Secure channel concatenation (TCP) * @disable_sqflow: disable controller sq flow control * @hdr_digest: generate/verify header digest (TCP) * @data_digest: generate/verify data digest (TCP)
@@ -130,6 +132,7 @@ struct nvmf_ctrl_options {struct key *keyring;struct key *tls_key;bool tls; +bool concat;bool disable_sqflow;bool hdr_digest;bool data_digest;
drivers/nvme/host/tcp.c +48 -6
102 lines not on this page · raw message
include/linux/nvme.h +7 -0
@@ -1664,6 +1664,13 @@ enum { NVME_AUTH_DHGROUP_INVALID = 0xff, }; +enum { +NVME_AUTH_SECP_NOSC =0x00 , +NVME_AUTH_SECP_SC =0x01 , +NVME_AUTH_SECP_NEWTLSPSK =0x02 , +NVME_AUTH_SECP_REPLACETLSPSK =0x03 , +}; +union nvmf_auth_protocol {struct nvmf_auth_dhchap_protocol_descriptor dhchap; };
Signature · 1 line
2.35.3
PATCH v4 13/19 nvme-fabrics: reset connection for secure concatenation
In reply to Hannes ReineckeWhen secure concatenation is requested the connection needs to be reset to enable TLS encryption on the new cnnection. That implies that the original connection used for the DH-CHAP negotiation really shouldn't be used, and we should reset as soon as the DH-CHAP negotiation has succeeded on the admin queue. The current implementation does not allow to easily skip connection attempts on the I/O queues, so we connect I/O queues, but disable namespace scanning on these queues. With that no I/O can be issued on these queues, so we can tear them down quickly without having to wait for quiescing etc.
Once that is done we can reset the controller directly after the ->create_ctrl() callback.
--- drivers/nvme/host/core.c | 8 +++++++- drivers/nvme/host/fabrics.c | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-)
drivers/nvme/host/core.c +7 -1
@@ -4541,6 +4541,8 @@ EXPORT_SYMBOL_GPL(nvme_stop_ctrl);void nvme_start_ctrl (struct nvme_ctrl *ctrl) { +bool start_scan = ctrl->queue_count >1 ; +nvme_enable_aen (ctrl);/*
@@ -4553,7 +4555,11 @@ void nvme_start_ctrl(struct nvme_ctrl *ctrl) nvme_discovery_ctrl(ctrl))nvme_change_uevent (ctrl,"NVME_EVENT=rediscover" ); -if (ctrl->queue_count >1 ) { +/* Suppress namespace scanning during setting up secure concatenation */ +if (ctrl->opts && ctrl->opts->concat && !ctrl->tls_pskid) + start_scan = false; + +if (start_scan) {nvme_queue_scan (ctrl);nvme_unquiesce_io_queues (ctrl);nvme_mpath_update (ctrl);
drivers/nvme/host/fabrics.c +6 -0
@@ -1335,6 +1335,12 @@ nvmf_create_ctrl(struct device *dev, const char *buf) goto out_module_put; } +/* Reset controller to start TLS */ +if (opts->concat) { +pr_debug ("resetting for secure concatenation\n" ); +nvme_reset_ctrl (ctrl); + } +module_put (ops->module);return ctrl;
Signature · 1 line
2.35.3
PATCH v4 14/19 nvme-tcp: reset after recovery for secure concatenation
In reply to Hannes ReineckeWith TP8018 a new key will be generated from the DH-HMAC-CHAP protocol after reset or recovery, but we need to start over to establish a new TLS connection with the new keys.
--- drivers/nvme/host/tcp.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
drivers/nvme/host/tcp.c +20 -0
@@ -2222,6 +2222,22 @@ static void nvme_tcp_reconnect_or_remove(struct nvme_ctrl *ctrl, } } +static bool nvme_tcp_reset_for_secure_concat (struct nvme_ctrl *ctrl) +{ +if (!ctrl->opts->concat) +return false; +/* +* If a key has been generated and TLS has not been enabled +* reset the queue to start TLS handshake. +*/ +if (ctrl->opts->tls_key && !ctrl->tls_pskid) { +dev_info (ctrl->device,"Reset to enable TLS with generated PSK\n" ); +nvme_reset_ctrl (ctrl); +return true; + } +return false; +} +static void nvme_tcp_revoke_generated_tls_key (struct nvme_ctrl *ctrl) {if (!ctrl->opts->concat)
@@ -2327,6 +2343,9 @@ static void nvme_tcp_reconnect_ctrl_work(struct work_struct *work)if (ret) goto requeue; +if (nvme_tcp_reset_for_secure_concat (ctrl)) +return ; +dev_info (ctrl->device,"Successfully reconnected (%d attempt)\n" , ctrl->nr_reconnects);
@@ -2404,6 +2423,7 @@ static void nvme_reset_ctrl_work(struct work_struct *work)if (ret) goto out_fail; +nvme_tcp_reset_for_secure_concat (ctrl);return ; out_fail:
Signature · 1 line
2.35.3
PATCH v4 15/19 nvme-tcp: do not start queues when TLS is not enabled for secure concatenation
In reply to Hannes ReineckeFor secure concatenation the TLS PSK is negotiated with DH-HMAC-CHAP, and then the queue is reset to enable TLS. During that state we should not start the I/O queues as the connection will be reset after DH-HMAC-CHAP is run.
--- drivers/nvme/host/tcp.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
drivers/nvme/host/tcp.c +11 -0
@@ -2065,6 +2065,17 @@ static int nvme_tcp_configure_io_queues(struct nvme_ctrl *ctrl, bool new)* queue number might have changed. */ nr_queues =min (ctrl->tagset->nr_hw_queues +1 , ctrl->queue_count); + +/* +* If secure concatenation is enabled don't start queues +* when TLS is not enabled; the connection will be reset +* after DH-HMAC-CHAP is run to enable TLS. +*/ +if (new && ctrl->opts && ctrl->opts->concat && !ctrl->tls_pskid) { + nr_queues =1 ; +dev_dbg (ctrl->device,"restrict I/O queues for secure concatenation\n" ); + } + ret =nvme_tcp_start_io_queues (ctrl,1 , nr_queues);if (ret) goto out_cleanup_connect_q;
Signature · 1 line
2.35.3
PATCH v4 16/19 nvmet-auth: allow to clear DH-HMAC-CHAP keys
In reply to Hannes ReineckeAs we can set DH-HMAC-CHAP keys, we should also be able to unset them.
--- drivers/nvme/target/auth.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/nvme/target/auth.c +12 -0
@@ -25,6 +25,18 @@ int nvmet_auth_set_key(struct nvmet_host *host, const char *secret,unsigned char key_hash;char *dhchap_secret; +if (!strlen (secret)) { +if (set_ctrl) { +kfree (host->dhchap_ctrl_secret); + host->dhchap_ctrl_secret =NULL ; + host->dhchap_ctrl_key_hash =0 ; + }else { +kfree (host->dhchap_secret); + host->dhchap_secret =NULL ; + host->dhchap_key_hash =0 ; + } +return 0 ; + }if (sscanf (secret,"DHHC-1:%hhd:%*s" , &key_hash) !=1 )return -EINVAL ;if (key_hash >3 ) {
Signature · 1 line
2.35.3
PATCH v4 17/19 nvme-target: do not check authentication status for admin commands twice
In reply to Hannes Reineckenvmet_check_ctrl_status() checks the authentication status, so we don't need to do that prior to calling it.
--- drivers/nvme/target/admin-cmd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/nvme/target/admin-cmd.c +1 -2
@@ -1005,8 +1005,7 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)if (nvme_is_fabrics (cmd))return nvmet_parse_fabrics_admin_cmd (req); -if (unlikely (!nvmet_check_auth_status (req))) -return NVME_SC_AUTH_REQUIRED |NVME_SC_DNR ; +if (nvmet_is_disc_subsys (nvmet_req_subsys (req)))return nvmet_parse_discovery_cmd (req);
Signature · 1 line
2.35.3
PATCH v4 18/19 nvme-target: do not check authentication status for I/O commands twice
In reply to Hannes Reineckenvmet_check_ctrl_status() checks the authentication status, so we don't need to do that prior to calling it.
--- drivers/nvme/target/core.c | 3 --- 1 file changed, 3 deletions(-)
drivers/nvme/target/core.c +0 -3
@@ -891,9 +891,6 @@ static u16 nvmet_parse_io_cmd(struct nvmet_req *req)if (nvme_is_fabrics (cmd))return nvmet_parse_fabrics_io_cmd (req); -if (unlikely (!nvmet_check_auth_status (req))) -return NVME_SC_AUTH_REQUIRED |NVME_SC_DNR ; - ret =nvmet_check_ctrl_status (req);if (unlikely (ret))return ret;
Signature · 1 line
2.35.3
PATCH v4 19/19 nvmet-tcp: support secure channel concatenation
In reply to Hannes ReineckeFrom: Hannes Reinecke <[email protected]>
Evaluate the SC_C flag during DH-CHAP-HMAC negotiation and insert the generated PSK once negotiation has finished.
--- drivers/nvme/target/auth.c | 72 +++++++++++++++++++++++++- drivers/nvme/target/fabrics-cmd-auth.c | 46 ++++++++++++++-- drivers/nvme/target/fabrics-cmd.c | 29 ++++++++--- drivers/nvme/target/nvmet.h | 30 ++++++++--- drivers/nvme/target/tcp.c | 25 ++++++++- 5 files changed, 180 insertions(+), 22 deletions(-)
drivers/nvme/target/auth.c +71 -1
@@ -15,6 +15,7 @@#include <linux/ctype.h> #include <linux/random.h> #include <linux/nvme-auth.h> +#include <linux/nvme-keyring.h> #include <asm/unaligned.h> #include "nvmet.h"
@@ -138,7 +139,7 @@ int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, u8 dhgroup_id)return ret; } -u8 nvmet_setup_auth (struct nvmet_ctrl *ctrl) +u8 nvmet_setup_auth (struct nvmet_ctrl *ctrl,struct nvmet_req *req) {int ret =0 ;struct nvmet_host_link *p;
@@ -164,6 +165,11 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl) goto out_unlock; } +if (nvmet_queue_is_tls (req->sq)) { +pr_debug ("host %s tls enabled\n" , ctrl->hostnqn); + goto out_unlock; + } + ret =nvmet_setup_dhgroup (ctrl, host->dhchap_dhgroup_id);if (ret <0 ) {pr_warn ("Failed to setup DH group" );
@@ -232,6 +238,9 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl)void nvmet_auth_sq_free (struct nvmet_sq *sq) {cancel_delayed_work (&sq->auth_expired_work); +#ifdef CONFIG_NVME_TARGET_TCP_TLS + sq->tls_key =0 ; +#endif kfree (sq->dhchap_c1); sq->dhchap_c1 =NULL ;kfree (sq->dhchap_c2);
@@ -260,6 +269,12 @@ void nvmet_destroy_auth(struct nvmet_ctrl *ctrl)nvme_auth_free_key (ctrl->ctrl_key); ctrl->ctrl_key =NULL ; } +#ifdef CONFIG_NVME_TARGET_TCP_TLS +if (ctrl->tls_key) { +key_put (ctrl->tls_key); + ctrl->tls_key =NULL ; + } +#endif }bool nvmet_check_auth_status (struct nvmet_req *req)
@@ -539,3 +554,58 @@ int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,return ret; } + +void nvmet_auth_insert_psk (struct nvmet_sq *sq) +{ +int hash_len =nvme_auth_hmac_hash_len (sq->ctrl->shash_id); +u8 *psk, *digest, *tls_psk; +size_t psk_len; +int ret; +#ifdef CONFIG_NVME_TARGET_TCP_TLS +struct key *tls_key =NULL ; +#endif + + ret =nvme_auth_generate_psk (sq->ctrl->shash_id, + sq->dhchap_skey, + sq->dhchap_skey_len, + sq->dhchap_c1, sq->dhchap_c2, + hash_len, &psk, &psk_len); +if (ret) { +pr_warn ("%s: ctrl %d qid %d failed to generate PSK, error %d\n" , + __func__, sq->ctrl->cntlid, sq->qid, ret); +return ; + } + ret =nvme_auth_generate_digest (sq->ctrl->shash_id, psk, psk_len, + sq->ctrl->subsysnqn, + sq->ctrl->hostnqn, &digest); +if (ret) { +pr_warn ("%s: ctrl %d qid %d failed to generate digest, error %d\n" , + __func__, sq->ctrl->cntlid, sq->qid, ret); + goto out_free_psk; + } + ret =nvme_auth_derive_tls_psk (sq->ctrl->shash_id, psk, psk_len, + digest, &tls_psk); +if (ret) { +pr_warn ("%s: ctrl %d qid %d failed to derive TLS PSK, error %d\n" , + __func__, sq->ctrl->cntlid, sq->qid, ret); + goto out_free_digest; + } +#ifdef CONFIG_NVME_TARGET_TCP_TLS + tls_key =nvme_tls_psk_refresh (NULL , sq->ctrl->hostnqn, sq->ctrl->subsysnqn, + sq->ctrl->shash_id, true, tls_psk, psk_len, digest); +if (IS_ERR (tls_key)) { +pr_warn ("%s: ctrl %d qid %d failed to refresh key, error %ld\n" , + __func__, sq->ctrl->cntlid, sq->qid,PTR_ERR (tls_key)); + tls_key =NULL ; +kfree_sensitive (tls_psk); + } +if (sq->ctrl->tls_key) +key_put (sq->ctrl->tls_key); + sq->ctrl->tls_key = tls_key; +#endif + +out_free_digest: +kfree_sensitive (digest); +out_free_psk: +kfree_sensitive (psk); +}
drivers/nvme/target/fabrics-cmd-auth.c +41 -5
85 lines not on this page · raw message
drivers/nvme/target/fabrics-cmd.c +21 -8
@@ -199,10 +199,20 @@ static u16 nvmet_install_queue(struct nvmet_ctrl *ctrl, struct nvmet_req *req)return ret; } -static u32 nvmet_connect_result (struct nvmet_ctrl *ctrl) +static u32 nvmet_connect_result (struct nvmet_ctrl *ctrl,struct nvmet_req *req) { +bool needs_auth =nvmet_has_auth (ctrl, req); + +/* Do not authenticate I/O queues for secure concatenation */ +if (ctrl->concat && req->sq->qid) + needs_auth = false; + +pr_debug ("%s: ctrl %d qid %d should %sauthenticate, tls psk %08x\n" , + __func__, ctrl->cntlid, req->sq->qid, + needs_auth ?"" :"not " , + req->sq->tls_key ?key_serial (req->sq->tls_key) :0 );return (u32 )ctrl->cntlid | - (nvmet_has_auth (ctrl) ?NVME_CONNECT_AUTHREQ_ATR :0 ); + (needs_auth ?NVME_CONNECT_AUTHREQ_ATR :0 ); }static void nvmet_execute_admin_connect (struct nvmet_req *req)
@@ -254,7 +264,7 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req)uuid_copy (&ctrl->hostid, &d->hostid); - dhchap_status =nvmet_setup_auth (ctrl); + dhchap_status =nvmet_setup_auth (ctrl, req);if (dhchap_status) {pr_err ("Failed to setup authentication, dhchap status %u\n" , dhchap_status);
@@ -272,12 +282,13 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req) goto out; } -pr_info ("creating %s controller %d for subsystem %s for NQN %s%s%s.\n" , +pr_info ("creating %s controller %d for subsystem %s for NQN %s%s%s%s.\n" ,nvmet_is_disc_subsys (ctrl->subsys) ?"discovery" :"nvm" , ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn, - ctrl->pi_support ?" T10-PI is enabled" :"" , -nvmet_has_auth (ctrl) ?" with DH-HMAC-CHAP" :"" ); - req->cqe->result.u32 =cpu_to_le32 (nvmet_connect_result (ctrl)); + ctrl->pi_support ?", T10-PI" :"" , +nvmet_has_auth (ctrl, req) ?", DH-HMAC-CHAP" :"" , +nvmet_queue_is_tls (req->sq) ?", TLS" :"" ); + req->cqe->result.u32 =cpu_to_le32 (nvmet_connect_result (ctrl, req)); out:kfree (d); complete:
@@ -320,6 +331,8 @@ static void nvmet_execute_io_connect(struct nvmet_req *req) ctrl =nvmet_ctrl_find_get (d->subsysnqn, d->hostnqn,le16_to_cpu (d->cntlid), req);if (!ctrl) { +pr_warn ("invalid controller (%x)\n" , +le16_to_cpu (d->cntlid)); status =NVME_SC_CONNECT_INVALID_PARAM |NVME_SC_DNR ; goto out; }
@@ -336,7 +349,7 @@ static void nvmet_execute_io_connect(struct nvmet_req *req) goto out_ctrl_put;pr_debug ("adding queue %d to ctrl %d.\n" , qid, ctrl->cntlid); - req->cqe->result.u32 =cpu_to_le32 (nvmet_connect_result (ctrl)); + req->cqe->result.u32 =cpu_to_le32 (nvmet_connect_result (ctrl, req)); out:kfree (d); complete:
drivers/nvme/target/nvmet.h +24 -6
@@ -121,6 +121,9 @@ struct nvmet_sq {u32 dhchap_s2;u8 *dhchap_skey;int dhchap_skey_len; +#endif +#ifdef CONFIG_NVME_TARGET_TCP_TLS +struct key *tls_key;#endif struct completion free_done;struct completion confirm_done;
@@ -235,6 +238,7 @@ struct nvmet_ctrl {u64 err_counter;struct nvme_error_slot slots[NVMET_ERROR_LOG_SLOTS ];bool pi_support; +bool concat;#ifdef CONFIG_NVME_TARGET_AUTH struct nvme_dhchap_key *host_key;struct nvme_dhchap_key *ctrl_key;
@@ -244,6 +248,9 @@ struct nvmet_ctrl {u8 *dh_key;size_t dh_keysize;#endif +#ifdef CONFIG_NVME_TARGET_TCP_TLS +struct key *tls_key; +#endif };struct nvmet_subsys {
@@ -707,13 +714,21 @@ static inline void nvmet_req_bio_put(struct nvmet_req *req, struct bio *bio)bio_put (bio); } +#ifdef CONFIG_NVME_TARGET_TCP_TLS +static inline bool nvmet_queue_is_tls (struct nvmet_sq *sq) +{ +return !!sq->tls_key; +} +#else +static inline bool nvmet_queue_is_tls (struct nvmet_sq *sq) {return false; } +#endif #ifdef CONFIG_NVME_TARGET_AUTH void nvmet_execute_auth_send (struct nvmet_req *req);void nvmet_execute_auth_receive (struct nvmet_req *req);int nvmet_auth_set_key (struct nvmet_host *host,const char *secret,bool set_ctrl);int nvmet_auth_set_host_hash (struct nvmet_host *host,const char *hash); -u8 nvmet_setup_auth (struct nvmet_ctrl *ctrl); +u8 nvmet_setup_auth (struct nvmet_ctrl *ctrl,struct nvmet_req *req);void nvmet_auth_sq_init (struct nvmet_sq *sq);void nvmet_destroy_auth (struct nvmet_ctrl *ctrl);void nvmet_auth_sq_free (struct nvmet_sq *sq);
@@ -723,16 +738,18 @@ int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response, unsigned int hash_len);int nvmet_auth_ctrl_hash (struct nvmet_req *req,u8 *response,unsigned int hash_len); -static inline bool nvmet_has_auth (struct nvmet_ctrl *ctrl) +static inline bool nvmet_has_auth (struct nvmet_ctrl *ctrl,struct nvmet_req *req) { -return ctrl->host_key !=NULL ; +return ctrl->host_key !=NULL && !nvmet_queue_is_tls (req->sq); }int nvmet_auth_ctrl_exponential (struct nvmet_req *req,u8 *buf,int buf_size);int nvmet_auth_ctrl_sesskey (struct nvmet_req *req,u8 *buf,int buf_size); +void nvmet_auth_insert_psk (struct nvmet_sq *sq);#else -static inline u8 nvmet_setup_auth (struct nvmet_ctrl *ctrl) +static inline u8 nvmet_setup_auth (struct nvmet_ctrl *ctrl, +struct nvmet_req *req) {return 0 ; }
@@ -745,11 +762,12 @@ static inline bool nvmet_check_auth_status(struct nvmet_req *req) {return true; } -static inline bool nvmet_has_auth (struct nvmet_ctrl *ctrl) +static inline bool nvmet_has_auth (struct nvmet_ctrl *ctrl, +struct nvmet_req *req) {return false; }static inline const char *nvmet_dhchap_dhgroup_name (u8 dhgid) {return NULL ; } +static inline void nvmet_auth_insert_psk (struct nvmet_sq *sq) {};#endif -#endif /* _NVMET_H */
drivers/nvme/target/tcp.c +23 -2
@@ -1072,10 +1072,11 @@ static int nvmet_tcp_done_recv_pdu(struct nvmet_tcp_queue *queue)if (unlikely (!nvmet_req_init (req, &queue->nvme_cq, &queue->nvme_sq, &nvmet_tcp_ops))) { -pr_err ("failed cmd %p id %d opcode %d, data_len: %d\n" , +pr_err ("failed cmd %p id %d opcode %d, data_len: %d, status: %04x\n" , req->cmd, req->cmd->common.command_id, req->cmd->common.opcode, -le32_to_cpu (req->cmd->common.dptr.sgl.length)); +le32_to_cpu (req->cmd->common.dptr.sgl.length), +le16_to_cpu (req->cqe->status));nvmet_tcp_handle_req_failure (queue, queue->cmd, req);return 0 ;
@@ -1605,6 +1606,9 @@ static void nvmet_tcp_release_queue_work(struct work_struct *w)/* stop accepting incoming data */ queue->rcv_state =NVMET_TCP_RECV_ERR ; +if (queue->nvme_sq.tls_key) +key_put (queue->nvme_sq.tls_key); +nvmet_tcp_uninit_data_in_cmds (queue);nvmet_sq_destroy (&queue->nvme_sq);cancel_work_sync (&queue->io_work);
@@ -1810,6 +1814,23 @@ static void nvmet_tcp_tls_handshake_done(void *data, int status,spin_unlock_bh (&queue->state_lock);cancel_delayed_work_sync (&queue->tls_handshake_tmo_work); + +if (!status) { +struct key *tls_key =nvme_tls_key_lookup (peerid); + +if (IS_ERR (tls_key)) { +pr_warn ("%s: queue %d failed to lookup key %x\n" , + __func__, queue->idx, peerid); +spin_lock_bh (&queue->state_lock); + queue->state =NVMET_TCP_Q_FAILED ; +spin_unlock_bh (&queue->state_lock); + status =PTR_ERR (tls_key); + }else { +pr_debug ("%s: queue %d using TLS PSK %x\n" , + __func__, queue->idx, peerid); + queue->nvme_sq.tls_key = tls_key; + } + }if (status)nvmet_tcp_schedule_release_queue (queue);else
Signature · 1 line
2.35.3