nvme: In-band authentication support
Subsystems (5)
Hannes Reinecke
maintainercover nvme: In-band authentication support
Hi all,
recent updates to the NVMe spec have added definitions for in-band authentication, and seeing that it provides some real benefit especially for NVMe-TCP here's an attempt to implement it.
Tricky bit here is that the specification orients itself on TLS 1.3, but supports only the FFDHE groups. Which of course the kernel doesn't support. I've been able to come up with a patch for this, but as this is my first attempt to fix anything in the crypto area I would invite people more familiar with these matters to have a look.
Also note that this is just for in-band authentication. Secure concatenation (ie starting TLS with the negotiated parameters) is not implemented; one would need to update the kernel TLS implementation for this, which at this time is beyond scope.
As usual, comments and reviews are welcome.
Hannes Reinecke (11):
crypto: add crypto_has_shash()
crypto: add crypto_has_kpp()
crypto/ffdhe: Finite Field DH Ephemeral Parameters lib/base64: RFC4648-compliant base64 encoding
nvme: add definitions for NVMe In-Band authentication
nvme: Implement In-Band authentication
nvme-auth: augmented challenge support
nvmet: Parse fabrics commands on all queues
nvmet: Implement basic In-Band Authentication
nvmet-auth: implement support for augmented challenge
nvme: add non-standard ECDH and curve25517 algorithms
crypto/Kconfig | 8 + crypto/Makefile | 1 + crypto/ffdhe_helper.c | 877 +++++++++++++++++ crypto/kpp.c | 6 + crypto/shash.c | 6 + drivers/nvme/host/Kconfig | 11 + drivers/nvme/host/Makefile | 1 + drivers/nvme/host/auth.c | 1188 ++++++++++++++++++++++++ drivers/nvme/host/auth.h | 23 + drivers/nvme/host/core.c | 77 +- drivers/nvme/host/fabrics.c | 65 +- drivers/nvme/host/fabrics.h | 8 + drivers/nvme/host/nvme.h | 15 + drivers/nvme/host/trace.c | 32 + drivers/nvme/target/Kconfig | 10 + drivers/nvme/target/Makefile | 1 + drivers/nvme/target/admin-cmd.c | 4 + drivers/nvme/target/auth.c | 608 ++++++++++++ drivers/nvme/target/configfs.c | 102 +- drivers/nvme/target/core.c | 10 + drivers/nvme/target/fabrics-cmd-auth.c | 472 ++++++++++ drivers/nvme/target/fabrics-cmd.c | 30 +- drivers/nvme/target/nvmet.h | 71 ++ include/crypto/ffdhe.h | 24 + include/crypto/hash.h | 2 + include/crypto/kpp.h | 2 + include/linux/base64.h | 16 + include/linux/nvme.h | 187 +++- lib/Makefile | 2 +- lib/base64.c | 111 +++ 30 files changed, 3961 insertions(+), 9 deletions(-) create mode 100644 crypto/ffdhe_helper.c create mode 100644 drivers/nvme/host/auth.c create mode 100644 drivers/nvme/host/auth.h create mode 100644 drivers/nvme/target/auth.c create mode 100644 drivers/nvme/target/fabrics-cmd-auth.c create mode 100644 include/crypto/ffdhe.h create mode 100644 include/linux/base64.h create mode 100644 lib/base64.c
Signature · 1 line
2.29.2
Hannes Reinecke
maintainerPATCH 1/11 crypto: add crypto_has_shash()
Add helper function to determine if a given synchronous hash is supported.
--- crypto/shash.c | 6 ++++++ include/crypto/hash.h | 2 ++ 2 files changed, 8 insertions(+)
crypto/shash.c +6 -0
@@ -521,6 +521,12 @@ struct crypto_shash *crypto_alloc_shash(const char *alg_name, u32 type, }EXPORT_SYMBOL_GPL (crypto_alloc_shash); +int crypto_has_shash (const char *alg_name,u32 type,u32 mask) +{ +return crypto_type_has_alg (alg_name, &crypto_shash_type, type, mask); +} +EXPORT_SYMBOL_GPL (crypto_has_shash); +static int shash_prepare_alg (struct shash_alg *alg) {struct crypto_alg *base = &alg->base;
include/crypto/hash.h +2 -0
@@ -718,6 +718,8 @@ static inline void ahash_request_set_crypt(struct ahash_request *req,struct crypto_shash *crypto_alloc_shash (const char *alg_name,u32 type,u32 mask); +int crypto_has_shash (const char *alg_name,u32 type,u32 mask); +static inline struct crypto_tfm *crypto_shash_tfm (struct crypto_shash *tfm) {return &tfm->base;
Signature · 1 line
2.29.2
Sagi Grimberg
maintainerRe: [PATCH 01/11] crypto: add crypto_has_shash()
Quoted 1 line
Add helper function to determine if a given synchronous hash is supported.
Can you add more info in the change log? Who is the consumer and why is this needed.
Hannes Reinecke
maintainerPATCH 2/11 crypto: add crypto_has_kpp()
In reply to Hannes ReineckeAdd helper function to determine if a given key-agreement protocol primitive is supported.
--- crypto/kpp.c | 6 ++++++ include/crypto/kpp.h | 2 ++ 2 files changed, 8 insertions(+)
crypto/kpp.c +6 -0
@@ -87,6 +87,12 @@ struct crypto_kpp *crypto_alloc_kpp(const char *alg_name, u32 type, u32 mask) }EXPORT_SYMBOL_GPL (crypto_alloc_kpp); +int crypto_has_kpp (const char *alg_name,u32 type,u32 mask) +{ +return crypto_type_has_alg (alg_name, &crypto_kpp_type, type, mask); +} +EXPORT_SYMBOL_GPL (crypto_has_kpp); +static void kpp_prepare_alg (struct kpp_alg *alg) {struct crypto_alg *base = &alg->base;
include/crypto/kpp.h +2 -0
@@ -104,6 +104,8 @@ struct kpp_alg {*/ struct crypto_kpp *crypto_alloc_kpp (const char *alg_name,u32 type,u32 mask); +int crypto_has_kpp (const char *alg_name,u32 type,u32 mask); +static inline struct crypto_tfm *crypto_kpp_tfm (struct crypto_kpp *tfm) {return &tfm->base;
Signature · 1 line
2.29.2
Sagi Grimberg
maintainerRe: [PATCH 02/11] crypto: add crypto_has_kpp()
Quoted 1 line
Add helper function to determine if a given key-agreement protocol primitive is supported.
Same comment.
Hannes Reinecke
maintainerPATCH 3/11 crypto/ffdhe: Finite Field DH Ephemeral Parameters
In reply to Hannes ReineckeAdd helper functions to generaten Finite Field DH Ephemeral Parameters as specified in RFC 7919.
--- crypto/Kconfig | 8 + crypto/Makefile | 1 + crypto/ffdhe_helper.c | 877 +++++++++++++++++++++++++++++++++++++++++ include/crypto/ffdhe.h | 24 ++ 4 files changed, 910 insertions(+) create mode 100644 crypto/ffdhe_helper.c create mode 100644 include/crypto/ffdhe.h
crypto/Kconfig +8 -0
@@ -231,6 +231,14 @@ config CRYPTO_DH help Generic implementation of the Diffie-Hellman algorithm. +config CRYPTO_FFDHE + tristate "Finite Field DH (RFC 7919) ephemeral parameters" + select CRYPTO_DH + select CRYPTO_KPP + select CRYPTO_RNG_DEFAULT + help + Generic implementation of the Finite Field DH algorithm + config CRYPTO_ECC tristate
crypto/Makefile +1 -0
@@ -177,6 +177,7 @@ obj-$(CONFIG_CRYPTO_OFB) += ofb.o obj-$(CONFIG_CRYPTO_ECC) += ecc.o obj-$(CONFIG_CRYPTO_ESSIV) += essiv.o obj-$(CONFIG_CRYPTO_CURVE25519) += curve25519-generic.o +obj-$(CONFIG_CRYPTO_FFDHE) += ffdhe_helper.o ecdh_generic-y += ecdh.o ecdh_generic-y += ecdh_helper.o
crypto/ffdhe_helper.c new file +877 -0
877 lines not on this page · raw message
include/crypto/ffdhe.h new file +24 -0
@@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* +* Finite-Field Diffie-Hellman definition according to RFC 7919 +* +* Copyright (c) 2021, SUSE Software Products +* Authors: Hannes Reinecke <[email protected]> +*/ +#ifndef _CRYPTO_FFDHE_ +#define _CRYPTO_FFDHE_ + +/** +* crypto_ffdhe_params() - Generate FFDHE params +* @params: DH params +* @bits: Bitsize of the FFDHE parameters +* +* This functions sets the FFDHE parameter for @bits in @params. +* Valid bit sizes are 2048, 3072, 4096, 6144, or 8194. +* +* Returns: 0 on success, errno on failure. +*/ + +int crypto_ffdhe_params (struct dh *p,int bits); + +#endif /* _CRYPTO_FFDHE_H */
Signature · 1 line
2.29.2
Sagi Grimberg
maintainerRe: [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters
Quoted 18 lines
Add helper functions to generaten Finite Field DH Ephemeral Parameters as specified in RFC 7919.
Signed-off-by Hannes Reinecke <[email protected]>--- crypto/Kconfig | 8 + crypto/Makefile | 1 + crypto/ffdhe_helper.c | 877 +++++++++++++++++++++++++++++++++++++++++ include/crypto/ffdhe.h | 24 ++ 4 files changed, 910 insertions(+)create mode 100644
crypto/ffdhe_helper.c
create mode 100644include/crypto/ffdhe.hcrypto/Kconfig +0
-0
crypto/Kconfig config CRYPTO_DH · line 235
Quoted 5 lines
crypto/Kconfig +2
-0@@ -231,6 +231,14 @@ config CRYPTO_DH help Generic implementation of the Diffie-Hellman algorithm. +config CRYPTO_FFDHE + tristate "Finite Field DH (RFC 7919) ephemeral parameters"
I'd stick with "Diffie-Hellman" in the tristate.
crypto/Kconfig config CRYPTO_DH · line 240
Quoted 5 lines
crypto/Kconfig +5
-0+ select CRYPTO_DH + select CRYPTO_KPP + select CRYPTO_RNG_DEFAULT + help + Generic implementation of the Finite Field DH algorithm
936 lines not shown · raw message
Part of this message is not shown here. View the raw message.
Hannes Reinecke
maintainerRe: [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters
Quoted 13 lines · Sagi Grimberg
Quoted 13 lines
Add helper functions to generaten Finite Field DH Ephemeral Parameters as specified in RFC 7919.
Signed-off-by Hannes Reinecke <[email protected]>---
crypto/Kconfig | 8 +
crypto/Makefile | 1 +
crypto/ffdhe_helper.c | 877 +++++++++++++++++++++++++++++++++++++++++include/crypto/ffdhe.h| 24 ++4 files changed, 910 insertions(+)create mode 100644
crypto/ffdhe_helper.c
create mode 100644include/crypto/ffdhe.h
Quoted 12 lines
Quoted 9 lines
crypto/Kconfig +0
-0@@ -231,6 +231,14 @@ config CRYPTO_DHhelp
Generic implementation of the Diffie-Hellman algorithm. +configCRYPTO_FFDHE
+ tristate "Finite Field DH (RFC 7919) ephemeral parameters"I'd stick with "Diffie-Hellman" in the tristate.
Ok.
Quoted 11 lines
Quoted 5 lines
+ select
CRYPTO_DH
+ selectCRYPTO_KPP
+ selectCRYPTO_RNG_DEFAULT
+ help
+ Generic implementation of the Finite Field DH algorithmDiffie-Hellman algorithm
And not sure I'd call it algorithm implementation, but rather a helper but maybe something like:
Finite Field Diffie-Hellman ephemeral parameters helper implementation
Wasn't sure how to call it myself; as stated I'm not a security expert.
Quoted 31 lines
Quoted 31 lines
+
configCRYPTO_ECC
tristatecrypto/Makefile +0
-0@@ -177,6 +177,7 @@ obj-$(CONFIG_CRYPTO_OFB) += ofb.oobj-$(CONFIG_CRYPTO_ECC) += ecc.o obj-$(CONFIG_CRYPTO_ESSIV) += essiv.o obj-$(CONFIG_CRYPTO_CURVE25519) += curve25519-generic.o +obj-$(CONFIG_CRYPTO_FFDHE) += ffdhe_helper.o ecdh_generic-y += ecdh.o
ecdh_generic-y += ecdh_helper.ocrypto/ffdhe_helper.c new file +11
-0@@ -0,0 +1,877 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* +* Finite Field DH Ephemeral Parameters (RFC 7919) +* +* Copyright (c) 2021, Hannes Reinecke, SUSE Software Products +* +*/ + +#include <linux/module.h> +#include <crypto/internal/kpp.h> +#include <crypto/kpp.h>
Quoted 9 lines
crypto/ffdhe_helper.c line 16
Quoted 5 lines
crypto/ffdhe_helper.c new file +5
-0+#include <crypto/dh.h> +#include <linux/mpi.h> + +/* +* ffdhe2048 generator (g), modulus (p) and group size (q) Maybe worth to refer exactly the source of these parameters in the comment body (rfc section/appendix).
Sure. These actually are copies from RFC 7919, so will be adding a reference to it.
Cheers,
Hannes
Signature · 4 lines
Dr. Hannes Reinecke Kernel Storage Architect [email protected] +49 911 74053 688 SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
Re: [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters
In reply to Hannes ReineckeAm Freitag, 16. Juli 2021, 13:04:20 CEST schrieb Hannes Reinecke:
Hi Hannes,
crypto/ffdhe_helper.c line 18
Quoted 10 lines
crypto/ffdhe_helper.c new file +10
-0+#include <linux/module.h> +#include <crypto/internal/kpp.h> +#include <crypto/kpp.h> +#include <crypto/dh.h> +#include <linux/mpi.h> + +/* +* ffdhe2048 generator (g), modulus (p) and group size (q) +*/ +const u8 ffdhe2048_g[] = {0x02 };
What about using static const here (and for all the following groups)?
Ciao
Stephan
Hannes Reinecke
maintainerRe: [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters
Quoted 4 lines · Stephan Müller
Am Freitag, 16. Juli 2021, 13:04:20 CEST schrieb Hannes Reinecke:
Hi Hannes,
Quoted 13 lines
crypto/ffdhe_helper.c line 18
Quoted 10 lines
crypto/ffdhe_helper.c new file +10
-0+#include <linux/module.h> +#include <crypto/internal/kpp.h> +#include <crypto/kpp.h> +#include <crypto/dh.h> +#include <linux/mpi.h> + +/* +* ffdhe2048 generator (g), modulus (p) and group size (q) +*/ +const u8 ffdhe2048_g[] = {0x02 };What about using static const here (and for all the following groups)?
Yes, of course. Will be fixing it.
Cheers,
Hannes
Signature · 4 lines
Dr. Hannes Reinecke Kernel Storage Architect [email protected] +49 911 74053 688 SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
Hannes Reinecke
maintainerPATCH 4/11 lib/base64: RFC4648-compliant base64 encoding
In reply to Hannes ReineckeAdd RFC4648-compliant base64 encoding and decoding routines.
--- include/linux/base64.h | 16 ++++++ lib/Makefile | 2 +- lib/base64.c | 111 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 include/linux/base64.h create mode 100644 lib/base64.c
include/linux/base64.h new file +16 -0
@@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* base64 encoding, lifted from fs/crypto/fname.c. +*/ + +#ifndef _LINUX_BASE64_H +#define _LINUX_BASE64_H + +#include <linux/types.h> + +#define BASE64_CHARS (nbytes) DIV_ROUND_UP((nbytes) * 4, 3) + +int base64_encode (const u8 *src,int len,char *dst); +int base64_decode (const char *src,int len,u8 *dst); + +#endif /* _LINUX_BASE64_H */
lib/Makefile +1 -1
@@ -46,7 +46,7 @@ obj-y += bcd.o sort.o parser.o debug_locks.o random32.o \ bust_spinlocks.o kasprintf.o bitmap.o scatterlist.o \ list_sort.o uuid.o iov_iter.o clz_ctz.o \ bsearch.o find_bit.o llist.o memweight.o kfifo.o \ - percpu-refcount.o rhashtable.o \ + percpu-refcount.o rhashtable.o base64.o \ once.o refcount.o usercopy.o errseq.o bucket_locks.o \ generic-radix-tree.o obj-$(CONFIG_STRING_SELFTEST) += test_string.o
lib/base64.c new file +111 -0
111 lines not on this page · raw message
Signature · 1 line
2.29.2
Sagi Grimberg
maintainerRe: [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding
Quoted 2 lines
Add RFC4648-compliant base64 encoding and decoding routines.
Looks good to me (although didn't look in the logic itself). Can you maybe mention where was this taken from?
Hannes Reinecke
maintainerRe: [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding
Quoted 5 lines · Sagi Grimberg
Quoted 2 lines
Add RFC4648-compliant base64 encoding and decoding routines.
Looks good to me (although didn't look in the logic itself). Can you maybe mention where was this taken from?
Umm ... yeah, I guess I can; I _think_ I've copied it from base64 routines in fs/crypto/fname.c, but I'll check.
Cheers,
Hannes
Signature · 4 lines
Dr. Hannes Reinecke Kernel Storage Architect [email protected] +49 911 74053 688 SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
Re: [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding
Quoted 10 lines · Hannes Reinecke
Quoted 5 lines · Sagi Grimberg
Quoted 2 lines
Add RFC4648-compliant base64 encoding and decoding routines.
Looks good to me (although didn't look in the logic itself). Can you maybe mention where was this taken from?
Umm ... yeah, I guess I can; I _think_ I've copied it from base64 routines in
fs/crypto/fname.c, but I'll check.
Note that it wasn't simply a copy, as you changed the variant of base64 that is implemented. So please make sure that you are very clear about which variant of base64 it is, and update all the comments accordingly.
- Eric
Re: [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding
In reply to Hannes ReineckeQuoted 37 lines · Hannes Reinecke
lib/base64.c new file +37
-0+/** +* base64_decode() - base64-decode some bytes +* @src: the base64-encoded string to decode +* @len: number of bytes to decode +* @dst: (output) the decoded bytes. +* +* Decodes the base64-encoded bytes @src according to RFC 4648. +* +* Return: number of decoded bytes +*/ +int base64_decode (const char *src,int len,u8 *dst) +{ +int i, bits =0 , pad =0 ; +u32 ac =0 ; +size_t dst_len =0 ; + +for (i =0 ; i < len; i++) { +int c, p =-1 ; + +if (src[i] =='=' ) { + pad++; +if (i +1 < len && src[i +1 ] =='=' ) + pad++; +break ; + } +for (c =0 ; c <strlen (lookup_table); c++) { +if (src[i] == lookup_table[c]) { + p = c; +break ; + } + } +if (p <0 ) +break ; + ac = (ac <<6 ) | p; + bits +=6 ; +if (bits <24 ) +continue ;
lib/base64.c line 111
Quoted 10 lines
lib/base64.c new file +10
-0+while (bits) { + bits -=8 ; + dst[dst_len++] = (ac >> bits) &0xff ; + } + ac =0 ; + } + dst_len -= pad; +return dst_len; +} +EXPORT_SYMBOL_GPL (base64_decode);
This should return an error if the input isn't valid base64.
- Eric
Hannes Reinecke
maintainerPATCH 5/11 nvme: add definitions for NVMe In-Band authentication
In reply to Hannes Reinecke--- include/linux/nvme.h | 185 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 184 insertions(+), 1 deletion(-)
include/linux/nvme.h +184 -1
@@ -19,6 +19,7 @@#define NVMF_TRSVCID_SIZE 32#define NVMF_TRADDR_SIZE 256#define NVMF_TSAS_SIZE 256 +#define NVMF_AUTH_HASH_LEN 64#define NVME_DISC_SUBSYS_NAME "nqn.2014-08.org.nvmexpress.discovery"
@@ -1263,6 +1264,8 @@ enum nvmf_capsule_command { nvme_fabrics_type_property_set =0x00 , nvme_fabrics_type_connect =0x01 , nvme_fabrics_type_property_get =0x04 , + nvme_fabrics_type_auth_send =0x05 , + nvme_fabrics_type_auth_receive = 0x06, };#define nvme_fabrics_type_name (type) { type, #type }
@@ -1270,7 +1273,9 @@ enum nvmf_capsule_command {__print_symbolic (type, \nvme_fabrics_type_name (nvme_fabrics_type_property_set), \nvme_fabrics_type_name (nvme_fabrics_type_connect), \ -nvme_fabrics_type_name (nvme_fabrics_type_property_get)) +nvme_fabrics_type_name (nvme_fabrics_type_property_get), \ +nvme_fabrics_type_name (nvme_fabrics_type_auth_send), \ +nvme_fabrics_type_name (nvme_fabrics_type_auth_receive))/* * If not fabrics command, fctype will be ignored.
@@ -1393,6 +1398,182 @@ struct nvmf_property_get_command {__u8 resv4[16 ]; }; +struct nvmf_auth_send_command { +__u8 opcode; +__u8 resv1; +__u16 command_id; +__u8 fctype; +__u8 resv2[19 ]; +union nvme_data_ptr dptr; +__u8 resv3; +__u8 spsp0; +__u8 spsp1; +__u8 secp; +__le32 tl; +__u8 resv4[12 ]; + +}; + +struct nvmf_auth_receive_command { +__u8 opcode; +__u8 resv1; +__u16 command_id; +__u8 fctype; +__u8 resv2[19 ]; +union nvme_data_ptr dptr; +__u8 resv3; +__u8 spsp0; +__u8 spsp1; +__u8 secp; +__le32 al; +__u8 resv4[12 ]; +}; + +/* Value for secp */ +enum { +NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER =0xe9 , +}; + +/* Defined value for auth_type */ +enum { +NVME_AUTH_COMMON_MESSAGES =0x00 , +NVME_AUTH_DHCHAP_MESSAGES =0x01 , +}; + +/* Defined messages for auth_id */ +enum { +NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE =0x00 , +NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE =0x01 , +NVME_AUTH_DHCHAP_MESSAGE_REPLY =0x02 , +NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1 =0x03 , +NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 =0x04 , +NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 =0xf0 , +NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 =0xf1 , +}; + +struct nvmf_auth_dhchap_protocol_descriptor { +__u8 authid; +__u8 rsvd; +__u8 halen; +__u8 dhlen; +__u8 idlist[60 ]; +}; + +enum { +NVME_AUTH_DHCHAP_AUTH_ID =0x01 , +}; + +enum { +NVME_AUTH_DHCHAP_HASH_SHA256 =0x01 , +NVME_AUTH_DHCHAP_HASH_SHA384 =0x02 , +NVME_AUTH_DHCHAP_HASH_SHA512 =0x03 , +}; + +enum { +NVME_AUTH_DHCHAP_DHGROUP_NULL =0x00 , +NVME_AUTH_DHCHAP_DHGROUP_2048 =0x01 , +NVME_AUTH_DHCHAP_DHGROUP_3072 =0x02 , +NVME_AUTH_DHCHAP_DHGROUP_4096 =0x03 , +NVME_AUTH_DHCHAP_DHGROUP_6144 =0x04 , +NVME_AUTH_DHCHAP_DHGROUP_8192 =0x05 , +}; + +union nvmf_auth_protocol { +struct nvmf_auth_dhchap_protocol_descriptor dhchap; +}; + +struct nvmf_auth_dhchap_negotiate_data { +__u8 auth_type; +__u8 auth_id; +__u8 rsvd[2 ]; +__le16 t_id; +__u8 sc_c; +__u8 napd; +union nvmf_auth_protocol auth_protocol[]; +}; + +struct nvmf_auth_dhchap_challenge_data { +__u8 auth_type; +__u8 auth_id; +__u8 rsvd1[2 ]; +__le16 t_id; +__u8 hl; +__u8 rsvd2; +__u8 hashid; +__u8 dhgid; +__le16 dhvlen; +__le32 seqnum; +/* 'hl' bytes of challenge value */ +__u8 cval[]; +/* followed by 'dhvlen' bytes of DH value */ +}; + +struct nvmf_auth_dhchap_reply_data { +__u8 auth_type; +__u8 auth_id; +__u8 rsvd1[2 ]; +__le16 t_id; +__u8 hl; +__u8 rsvd2; +__u8 cvalid; +__u8 rsvd3; +__le16 dhvlen; +__le32 seqnum; +/* 'hl' bytes of response data */ +__u8 rval[]; +/* followed by 'hl' bytes of Challenge value */ +/* followed by 'dhvlen' bytes of DH value */ +}; + +enum { +NVME_AUTH_DHCHAP_RESPONSE_VALID = (1 <<0 ), +}; + +struct nvmf_auth_dhchap_success1_data { +__u8 auth_type; +__u8 auth_id; +__u8 rsvd1[2 ]; +__le16 t_id; +__u8 hl; +__u8 rsvd2; +__u8 rvalid; +__u8 rsvd3[7 ]; +/* 'hl' bytes of response value if 'rvalid' is set */ +__u8 rval[]; +}; + +struct nvmf_auth_dhchap_success2_data { +__u8 auth_type; +__u8 auth_id; +__u8 rsvd1[2 ]; +__le16 t_id; +__u8 rsvd2[10 ]; +}; + +struct nvmf_auth_dhchap_failure_data { +__u8 auth_type; +__u8 auth_id; +__u8 rsvd1[2 ]; +__le16 t_id; +__u8 reason_code; +__u8 reason_code_explanation; +}; + +enum { +NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED =0x01 , +}; + +enum { +NVME_AUTH_DHCHAP_FAILURE_FAILED =0x01 , +NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE =0x02 , +NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH =0x03 , +NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE =0x04 , +NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE =0x05 , +NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD =0x06 , +NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE =0x07 , +}; + +struct nvme_dbbuf {__u8 opcode;__u8 flags;
@@ -1436,6 +1617,8 @@ struct nvme_command {struct nvmf_connect_command connect;struct nvmf_property_set_command prop_set;struct nvmf_property_get_command prop_get; +struct nvmf_auth_send_command auth_send; +struct nvmf_auth_receive_command auth_receive;struct nvme_dbbuf dbbuf;struct nvme_directive_cmd directive; };
Signature · 1 line
2.29.2
Sagi Grimberg
maintainerRe: [PATCH 05/11] nvme: add definitions for NVMe In-Band authentication
Quoted 44 lines · Hannes Reinecke
Signed-off-by Hannes Reinecke <[email protected]>--- include/linux/nvme.h | 185 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 184 insertions(+), 1 deletion(-)include/linux/nvme.h +9
-1@@ -19,6 +19,7 @@#define NVMF_TRSVCID_SIZE 32#define NVMF_TRADDR_SIZE 256#define NVMF_TSAS_SIZE 256 +#define NVMF_AUTH_HASH_LEN 64#define NVME_DISC_SUBSYS_NAME "nqn.2014-08.org.nvmexpress.discovery"@@ -1263,6 +1264,8 @@ enum nvmf_capsule_command { nvme_fabrics_type_property_set =0x00 , nvme_fabrics_type_connect =0x01 , nvme_fabrics_type_property_get =0x04 , + nvme_fabrics_type_auth_send =0x05 , + nvme_fabrics_type_auth_receive = 0x06, };#define nvme_fabrics_type_name (type) { type, #type }@@ -1270,7 +1273,9 @@ enum nvmf_capsule_command {__print_symbolic (type, \nvme_fabrics_type_name (nvme_fabrics_type_property_set), \nvme_fabrics_type_name (nvme_fabrics_type_connect), \ -nvme_fabrics_type_name (nvme_fabrics_type_property_get)) +nvme_fabrics_type_name (nvme_fabrics_type_property_get), \ +nvme_fabrics_type_name (nvme_fabrics_type_auth_send), \ +nvme_fabrics_type_name (nvme_fabrics_type_auth_receive))/* * If not fabrics command, fctype will be ignored. @@ -1393,6 +1398,182 @@ struct nvmf_property_get_command {__u8 resv4[16 ]; }; +struct nvmf_auth_send_command { +__u8 opcode; +__u8 resv1;
include/linux/nvme.h struct nvmf_property_get_command { · line 1413
Quoted 10 lines
include/linux/nvme.h +10
-0+__u16 command_id; +__u8 fctype; +__u8 resv2[19 ]; +union nvme_data_ptr dptr; +__u8 resv3; +__u8 spsp0; +__u8 spsp1; +__u8 secp; +__le32 tl; +__u8 resv4[12 ];
Isn't that 16 bytes?
You should add these to the compile time checkers in _nvme_check_size.
Quoted 44 lines
include/linux/nvme.h +44
-0+ +}; + +struct nvmf_auth_receive_command { +__u8 opcode; +__u8 resv1; +__u16 command_id; +__u8 fctype; +__u8 resv2[19 ]; +union nvme_data_ptr dptr; +__u8 resv3; +__u8 spsp0; +__u8 spsp1; +__u8 secp; +__le32 al; +__u8 resv4[12 ]; +}; + +/* Value for secp */ +enum { +NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER =0xe9 , +}; + +/* Defined value for auth_type */ +enum { +NVME_AUTH_COMMON_MESSAGES =0x00 , +NVME_AUTH_DHCHAP_MESSAGES =0x01 , +}; + +/* Defined messages for auth_id */ +enum { +NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE =0x00 , +NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE =0x01 , +NVME_AUTH_DHCHAP_MESSAGE_REPLY =0x02 , +NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1 =0x03 , +NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 =0x04 , +NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 =0xf0 , +NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 =0xf1 , +}; + +struct nvmf_auth_dhchap_protocol_descriptor { +__u8 authid; +__u8 rsvd; +__u8 halen;
include/linux/nvme.h struct nvmf_property_get_command { · line 1467
Quoted 10 lines
include/linux/nvme.h +10
-0+__u8 dhlen; +__u8 idlist[60 ]; +}; + +enum { +NVME_AUTH_DHCHAP_AUTH_ID =0x01 , +}; + +enum { +NVME_AUTH_DHCHAP_HASH_SHA256 =0x01 ,
Maybe s/HASH/HF/ (stands for hash function, which is a better description).
Quoted 37 lines
include/linux/nvme.h +37
-0+NVME_AUTH_DHCHAP_HASH_SHA384 =0x02 , + NVME_AUTH_DHCHAP_HASH_SHA512 = 0x03, +}; + +enum { +NVME_AUTH_DHCHAP_DHGROUP_NULL =0x00 , +NVME_AUTH_DHCHAP_DHGROUP_2048 =0x01 , +NVME_AUTH_DHCHAP_DHGROUP_3072 =0x02 , +NVME_AUTH_DHCHAP_DHGROUP_4096 =0x03 , +NVME_AUTH_DHCHAP_DHGROUP_6144 =0x04 , +NVME_AUTH_DHCHAP_DHGROUP_8192 =0x05 , +}; + +union nvmf_auth_protocol { +struct nvmf_auth_dhchap_protocol_descriptor dhchap; +}; + +struct nvmf_auth_dhchap_negotiate_data { +__u8 auth_type; +__u8 auth_id; +__u8 rsvd[2 ]; +__le16 t_id; +__u8 sc_c; +__u8 napd; +union nvmf_auth_protocol auth_protocol[]; +}; + +struct nvmf_auth_dhchap_challenge_data { +__u8 auth_type; +__u8 auth_id; +__u8 rsvd1[2 ]; +__le16 t_id; +__u8 hl; +__u8 rsvd2; +__u8 hashid; +__u8 dhgid; +__le16 dhvlen;
include/linux/nvme.h struct nvmf_property_get_command { · line 1514
Quoted 10 lines
include/linux/nvme.h +10
-0+__le32 seqnum; +/* 'hl' bytes of challenge value */ +__u8 cval[]; +/* followed by 'dhvlen' bytes of DH value */ +}; + +struct nvmf_auth_dhchap_reply_data { +__u8 auth_type; +__u8 auth_id; +__u8 rsvd1[2 ];
Maybe __u32 rsvd1? Usually its done this way in the other headers...
Quoted 18 lines
include/linux/nvme.h +18
-0+__le16 t_id; +__u8 hl; +__u8 rsvd2; +__u8 cvalid; +__u8 rsvd3; +__le16 dhvlen; +__le32 seqnum; +/* 'hl' bytes of response data */ +__u8 rval[]; +/* followed by 'hl' bytes of Challenge value */ +/* followed by 'dhvlen' bytes of DH value */ +}; + +enum { +NVME_AUTH_DHCHAP_RESPONSE_VALID = (1 <<0 ), +}; + +struct nvmf_auth_dhchap_success1_data {
include/linux/nvme.h struct nvmf_property_get_command { · line 1542
Quoted 10 lines
include/linux/nvme.h +10
-0+__u8 auth_type; +__u8 auth_id; +__u8 rsvd1[2 ]; +__le16 t_id; +__u8 hl; +__u8 rsvd2; +__u8 rvalid; +__u8 rsvd3[7 ]; +/* 'hl' bytes of response value if 'rvalid' is set */ +__u8 rval[];
It really sucks that we have zero-length pointers in a wire-format struct... but anyways, it is what it is...
Quoted 7 lines
include/linux/nvme.h +7
-0+}; + +struct nvmf_auth_dhchap_success2_data { +__u8 auth_type; +__u8 auth_id; +__u8 rsvd1[2 ]; +__le16 t_id;
include/linux/nvme.h struct nvmf_property_get_command { · line 1559
Quoted 10 lines
include/linux/nvme.h +10
-0+__u8 rsvd2[10 ]; +}; + +struct nvmf_auth_dhchap_failure_data { +__u8 auth_type; +__u8 auth_id; +__u8 rsvd1[2 ]; +__le16 t_id; +__u8 reason_code; +__u8 reason_code_explanation;
I'd maybe do those shorter;
rescoderescode_exp
Quoted 4 lines
include/linux/nvme.h +4
-0+}; + +enum { +NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED =0x01 ,
include/linux/nvme.h struct nvmf_property_get_command { · line 1573
Quoted 10 lines
include/linux/nvme.h +10
-0+}; + +enum { +NVME_AUTH_DHCHAP_FAILURE_FAILED =0x01 , +NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE =0x02 , +NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH =0x03 , +NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE =0x04 , +NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE =0x05 , +NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD =0x06 , +NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE =0x07 ,
I think the language in the spec is "incorrect", why not stick with that instead of "invalid"?
Quoted 16 lines
include/linux/nvme.h +5
-0+}; + +struct nvme_dbbuf {__u8 opcode;__u8 flags;@@ -1436,6 +1617,8 @@ struct nvme_command {struct nvmf_connect_command connect;struct nvmf_property_set_command prop_set;struct nvmf_property_get_command prop_get; +struct nvmf_auth_send_command auth_send; +struct nvmf_auth_receive_command auth_receive;struct nvme_dbbuf dbbuf;struct nvme_directive_cmd directive; };
Hannes Reinecke
maintainerRe: [PATCH 05/11] nvme: add definitions for NVMe In-Band authentication
Quoted 48 lines · Sagi Grimberg
Quoted 45 lines · Hannes Reinecke
Signed-off-by Hannes Reinecke <[email protected]>---
include/linux/nvme.h| 185 ++++++++++++++++++++++++++++++++++++++++++-1 file changed, 184 insertions(+), 1 deletion(-)include/linux/nvme.h +15
-1@@ -19,6 +19,7 @@ #define NVMF_TRSVCID_SIZE 32 #define NVMF_TRADDR_SIZE 256 #define NVMF_TSAS_SIZE 256 +#define NVMF_AUTH_HASH_LEN 64 #define NVME_DISC_SUBSYS_NAME "nqn.2014-08.org.nvmexpress.discovery"@@ -1263,6 +1264,8 @@ enum nvmf_capsule_command { nvme_fabrics_type_property_set = 0x00, nvme_fabrics_type_connect = 0x01, nvme_fabrics_type_property_get = 0x04, + nvme_fabrics_type_auth_send = 0x05, + nvme_fabrics_type_auth_receive = 0x06, }; #define nvme_fabrics_type_name(type) { type, #type }@@ -1270,7 +1273,9 @@ enum nvmf_capsule_command { __print_symbolic(type, \ nvme_fabrics_type_name(nvme_fabrics_type_property_set), \ nvme_fabrics_type_name(nvme_fabrics_type_connect), \ - nvme_fabrics_type_name(nvme_fabrics_type_property_get)) + nvme_fabrics_type_name(nvme_fabrics_type_property_get), \ + nvme_fabrics_type_name(nvme_fabrics_type_auth_send), \ + nvme_fabrics_type_name(nvme_fabrics_type_auth_receive)) /** If not fabrics command, fctype will be ignored. @@ -1393,6 +1398,182 @@ struct nvmf_property_get_command { __u8 resv4[16]; }; +struct nvmf_auth_send_command { + __u8 opcode; + __u8 resv1; + __u16 command_id; + __u8 fctype; + __u8 resv2[19]; + union nvme_data_ptr dptr; + __u8 resv3; + __u8 spsp0;
Quoted 9 lines
include/linux/nvme.h struct nvmf_property_get_command { · line 1413
Quoted 4 lines
include/linux/nvme.h +4
-0+ __u8 spsp1; + __u8 secp; + __le32 tl; + __u8 resv4[12];Isn't that 16 bytes?
You should add these to the compile time checkers in_nvme_check_size.
If you say so ... I'll cross-check.
Quoted 49 lines
Quoted 49 lines
include/linux/nvme.h +49
-0+ +}; + +struct nvmf_auth_receive_command { + __u8 opcode; + __u8 resv1; + __u16 command_id; + __u8 fctype; + __u8 resv2[19]; + union nvme_data_ptr dptr; + __u8 resv3; + __u8 spsp0; + __u8 spsp1; + __u8 secp; + __le32 al; + __u8 resv4[12]; +}; + +/* Value for secp */ +enum { + NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER = 0xe9, +}; + +/* Defined value for auth_type */ +enum { + NVME_AUTH_COMMON_MESSAGES = 0x00, + NVME_AUTH_DHCHAP_MESSAGES = 0x01, +}; + +/* Defined messages for auth_id */ +enum { + NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE = 0x00, + NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE = 0x01, + NVME_AUTH_DHCHAP_MESSAGE_REPLY = 0x02, + NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1 = 0x03, + NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 = 0x04, + NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 = 0xf0, + NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 = 0xf1, +}; + +struct nvmf_auth_dhchap_protocol_descriptor { + __u8 authid; + __u8 rsvd; + __u8 halen; + __u8 dhlen; + __u8 idlist[60]; +}; + +enum {
Quoted 9 lines
include/linux/nvme.h struct nvmf_property_get_command { · line 1467
Quoted 5 lines
include/linux/nvme.h +5
-0+ NVME_AUTH_DHCHAP_AUTH_ID = 0x01, +}; + +enum { + NVME_AUTH_DHCHAP_HASH_SHA256 = 0x01,Maybe s/HASH/HF/ (stands for hash function, which is a better description).
Or HMAC, as this is what it's used for...
Quoted 42 lines
Quoted 42 lines
include/linux/nvme.h +42
-0+ NVME_AUTH_DHCHAP_HASH_SHA384 = 0x02, + NVME_AUTH_DHCHAP_HASH_SHA512 = 0x03, +}; + +enum { + NVME_AUTH_DHCHAP_DHGROUP_NULL = 0x00, + NVME_AUTH_DHCHAP_DHGROUP_2048 = 0x01, + NVME_AUTH_DHCHAP_DHGROUP_3072 = 0x02, + NVME_AUTH_DHCHAP_DHGROUP_4096 = 0x03, + NVME_AUTH_DHCHAP_DHGROUP_6144 = 0x04, + NVME_AUTH_DHCHAP_DHGROUP_8192 = 0x05, +}; + +union nvmf_auth_protocol { + struct nvmf_auth_dhchap_protocol_descriptor dhchap; +}; + +struct nvmf_auth_dhchap_negotiate_data { + __u8 auth_type; + __u8 auth_id; + __u8 rsvd[2]; + __le16 t_id; + __u8 sc_c; + __u8 napd; + union nvmf_auth_protocol auth_protocol[]; +}; + +struct nvmf_auth_dhchap_challenge_data { + __u8 auth_type; + __u8 auth_id; + __u8 rsvd1[2]; + __le16 t_id; + __u8 hl; + __u8 rsvd2; + __u8 hashid; + __u8 dhgid; + __le16 dhvlen; + __le32 seqnum; + /* 'hl' bytes of challenge value */ + __u8 cval[]; + /* followed by 'dhvlen' bytes of DH value */ +};
Quoted 9 lines
include/linux/nvme.h struct nvmf_property_get_command { · line 1514
Quoted 5 lines
include/linux/nvme.h +5
-0+ +struct nvmf_auth_dhchap_reply_data { + __u8 auth_type; + __u8 auth_id; + __u8 rsvd1[2];Maybe
__u32rsvd1? Usually its done this way in the other headers...
Ah. Right, will fix.
Quoted 23 lines
Quoted 23 lines
include/linux/nvme.h +23
-0+ __le16 t_id; + __u8 hl; + __u8 rsvd2; + __u8 cvalid; + __u8 rsvd3; + __le16 dhvlen; + __le32 seqnum; + /* 'hl' bytes of response data */ + __u8 rval[]; + /* followed by 'hl' bytes of Challenge value */ + /* followed by 'dhvlen' bytes of DH value */ +}; + +enum { + NVME_AUTH_DHCHAP_RESPONSE_VALID = (1 << 0), +}; + +struct nvmf_auth_dhchap_success1_data { + __u8 auth_type; + __u8 auth_id; + __u8 rsvd1[2]; + __le16 t_id; + __u8 hl;
Quoted 9 lines
include/linux/nvme.h struct nvmf_property_get_command { · line 1542
Quoted 5 lines
include/linux/nvme.h +5
-0+ __u8 rsvd2; + __u8 rvalid; + __u8 rsvd3[7]; + /* 'hl' bytes of response value if 'rvalid' is set */ + __u8 rval[];It really sucks that we have zero-length pointers in a wire-format struct... but anyways, it is what it is...
Yeah, and that's not the worst of it; the 'reply' structure has _three_ zero-length pointers. Makes me wonder if I should drop them completely...
Quoted 13 lines
Quoted 13 lines
include/linux/nvme.h +13
-0+}; + +struct nvmf_auth_dhchap_success2_data { + __u8 auth_type; + __u8 auth_id; + __u8 rsvd1[2]; + __le16 t_id; + __u8 rsvd2[10]; +}; + +struct nvmf_auth_dhchap_failure_data { + __u8 auth_type; + __u8 auth_id;
Quoted 9 lines
include/linux/nvme.h struct nvmf_property_get_command { · line 1559
Quoted 4 lines
include/linux/nvme.h +4
-0+ __u8 rsvd1[2]; + __le16 t_id; + __u8 reason_code; + __u8 reason_code_explanation;I'd maybe do those shorter;
rescoderescode_exp
Ok.
Quoted 9 lines
Quoted 9 lines
include/linux/nvme.h +9
-0+}; + +enum { + NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED = 0x01, +}; + +enum { + NVME_AUTH_DHCHAP_FAILURE_FAILED = 0x01, + NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE = 0x02,
Quoted 9 lines
include/linux/nvme.h struct nvmf_property_get_command { · line 1573
Quoted 5 lines
include/linux/nvme.h +5
-0+ NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH = 0x03, + NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE = 0x04, + NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE = 0x05, + NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD = 0x06, + NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE = 0x07,I think the language in the spec is "incorrect", why not stick with that instead of "invalid"?
Ah. Things got changed so much during development of the spec that I seem to have lost track. Will change it.
Quoted 8 lines
Quoted 8 lines
include/linux/nvme.h +3
-0+}; + + struct nvme_dbbuf { __u8 opcode; __u8 flags;@@ -1436,6 +1617,8 @@ struct nvme_command { struct nvmf_connect_command connect;
Quoted 8 lines
include/linux/nvme.h struct nvme_command { · line 1624
Quoted 8 lines
include/linux/nvme.h +2
-0struct nvmf_property_set_command prop_set; struct nvmf_property_get_command prop_get; + struct nvmf_auth_send_command auth_send; + struct nvmf_auth_receive_command auth_receive; struct nvme_dbbuf dbbuf; struct nvme_directive_cmd directive; };
Cheers,
Hannes
Signature · 4 lines
Dr. Hannes Reinecke Kernel Storage Architect [email protected] +49 911 74053 688 SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
Re: [PATCH 05/11] nvme: add definitions for NVMe In-Band authentication
Quoted 1 line · Hannes Reinecke
On 7/17/21 8:30 AM, Sagi Grimberg wrote:
[...]
Quoted 8 lines
Quoted 6 lines
include/linux/nvme.h struct nvmf_property_get_command { · line 1467
Quoted 3 lines
include/linux/nvme.h +3
-0+ +enum { + NVME_AUTH_DHCHAP_HASH_SHA256 = 0x01,Maybe s/HASH/HF/ (stands for hash function, which is a better description).
Or HMAC, as this is what it's used for...
I would better do just NVME_AUTH_DHCHAP_SHA256. "HASH" is redundant here. Better to keep it shorter.
Vlad
_______________________________________________
Linux-nvme mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-nvme
Hannes Reinecke
maintainerPATCH 6/11 nvme: Implement In-Band authentication
In reply to Hannes ReineckeImplement NVMe-oF In-Band authentication. This patch adds two new fabric options 'dhchap_key' to specify the PSK and 'dhchap_authenticate' to request bi-directional authentication of both the host and the controller.
--- drivers/nvme/host/Kconfig | 11 + drivers/nvme/host/Makefile | 1 + drivers/nvme/host/auth.c | 813 ++++++++++++++++++++++++++++++++++++ drivers/nvme/host/auth.h | 23 + drivers/nvme/host/core.c | 77 +++- drivers/nvme/host/fabrics.c | 65 ++- drivers/nvme/host/fabrics.h | 8 + drivers/nvme/host/nvme.h | 15 + drivers/nvme/host/trace.c | 32 ++ 9 files changed, 1041 insertions(+), 4 deletions(-) create mode 100644 drivers/nvme/host/auth.c create mode 100644 drivers/nvme/host/auth.h
drivers/nvme/host/Kconfig +11 -0
@@ -85,3 +85,14 @@ config NVME_TCP from https://github.com/linux-nvme/nvme-cli. If unsure, say N. + +config NVME_AUTH + bool "NVM Express over Fabrics In-Band Authentication" + depends on NVME_TCP + select CRYPTO_SHA256 + select CRYPTO_SHA512 + help + This provides support for NVMe over Fabrics In-Band Authentication + for the NVMe over TCP transport. + + If unsure, say N.
drivers/nvme/host/Makefile +1 -0
@@ -20,6 +20,7 @@ nvme-core-$(CONFIG_NVME_HWMON) += hwmon.o nvme-y += pci.o nvme-fabrics-y += fabrics.o +nvme-fabrics-$(CONFIG_NVME_AUTH) += auth.o nvme-rdma-y += rdma.o
drivers/nvme/host/auth.c new file +813 -0
813 lines not on this page · raw message
drivers/nvme/host/auth.h new file +23 -0
@@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions +*/ + +#ifndef _NVME_AUTH_H +#define _NVME_AUTH_H + +const char *nvme_auth_dhgroup_name (int dhgroup_id); +int nvme_auth_dhgroup_pubkey_size (int dhgroup_id); +int nvme_auth_dhgroup_privkey_size (int dhgroup_id); +const char *nvme_auth_dhgroup_kpp (int dhgroup_id); +int nvme_auth_dhgroup_id (const char *dhgroup_name); + +const char *nvme_auth_hmac_name (int hmac_id); +const char *nvme_auth_digest_name (int hmac_id); +int nvme_auth_hmac_id (const char *hmac_name); +int nvme_auth_hmac_len (int hmac_len); + +unsigned char *nvme_auth_extract_secret (unsigned char *dhchap_secret, +size_t *dhchap_key_len); + +#endif /* _NVME_AUTH_H */
drivers/nvme/host/core.c +76 -1
107 lines not on this page · raw message
drivers/nvme/host/fabrics.c +62 -3
113 lines not on this page · raw message
drivers/nvme/host/fabrics.h +8 -0
@@ -67,6 +67,9 @@ enum {NVMF_OPT_TOS =1 <<19 ,NVMF_OPT_FAIL_FAST_TMO =1 <<20 ,NVMF_OPT_HOST_IFACE =1 <<21 , +NVMF_OPT_DHCHAP_SECRET =1 <<22 , +NVMF_OPT_DHCHAP_AUTH =1 <<23 , + NVMF_OPT_DHCHAP_GROUP = 1 << 24, };/**
@@ -96,6 +99,8 @@ enum {* @max_reconnects: maximum number of allowed reconnect attempts before removing * the controller, (-1) means reconnect forever, zero means remove * immediately; +* @dhchap_secret: DH-HMAC-CHAP secret +* @dhchap_auth: DH-HMAC-CHAP authenticate controller * @disable_sqflow: disable controller sq flow control * @hdr_digest: generate/verify header digest (TCP) * @data_digest: generate/verify data digest (TCP)
@@ -120,6 +125,9 @@ struct nvmf_ctrl_options {unsigned int kato;struct nvmf_host *host;int max_reconnects; +char *dhchap_secret; +int dhchap_group; +bool dhchap_auth;bool disable_sqflow;bool hdr_digest;bool data_digest;
drivers/nvme/host/nvme.h +15 -0
@@ -328,6 +328,12 @@ struct nvme_ctrl {struct work_struct ana_work;#endif +#ifdef CONFIG_NVME_AUTH +u16 transaction; +u8 dhchap_hash; +u8 dhchap_dhgroup; +#endif +/* Power saving configuration */ u64 ps_max_latency_us;bool apst_enabled;
@@ -874,6 +880,15 @@ static inline bool nvme_ctrl_sgl_supported(struct nvme_ctrl *ctrl)return ctrl->sgls & ((1 <<0 ) | (1 <<1 )); } +#ifdef CONFIG_NVME_AUTH +int nvme_auth_negotiate (struct nvme_ctrl *ctrl,int qid); +#else +static inline int nvme_auth_negotiate (struct nvme_ctrl *ctrl,int qid) +{ +return -EPROTONOSUPPORT ; +} +#endif +u32 nvme_command_effects (struct nvme_ctrl *ctrl,struct nvme_ns *ns,u8 opcode);int nvme_execute_passthru_rq (struct request *rq);
drivers/nvme/host/trace.c +32 -0
@@ -271,6 +271,34 @@ static const char *nvme_trace_fabrics_property_get(struct trace_seq *p, u8 *spc)return ret; } +static const char *nvme_trace_fabrics_auth_send (struct trace_seq *p,u8 *spc) +{ +const char *ret =trace_seq_buffer_ptr (p); +u8 spsp0 = spc[1 ]; +u8 spsp1 = spc[2 ]; +u8 secp = spc[3 ]; +u32 tl =get_unaligned_le32 (spc +4 ); + +trace_seq_printf (p,"spsp0=%02x, spsp1=%02x, secp=%02x, tl=%u" , + spsp0, spsp1, secp, tl); +trace_seq_putc (p,0 ); +return ret; +} + +static const char *nvme_trace_fabrics_auth_receive (struct trace_seq *p,u8 *spc) +{ +const char *ret =trace_seq_buffer_ptr (p); +u8 spsp0 = spc[1 ]; +u8 spsp1 = spc[2 ]; +u8 secp = spc[3 ]; +u32 al =get_unaligned_le32 (spc +4 ); + +trace_seq_printf (p,"spsp0=%02x, spsp1=%02x, secp=%02x, al=%u" , + spsp0, spsp1, secp, al); +trace_seq_putc (p,0 ); +return ret; +} +static const char *nvme_trace_fabrics_common (struct trace_seq *p,u8 *spc) {const char *ret =trace_seq_buffer_ptr (p);
@@ -290,6 +318,10 @@ const char *nvme_trace_parse_fabrics_cmd(struct trace_seq *p,return nvme_trace_fabrics_connect (p, spc);case nvme_fabrics_type_property_get:return nvme_trace_fabrics_property_get (p, spc); +case nvme_fabrics_type_auth_send: +return nvme_trace_fabrics_auth_send (p, spc); +case nvme_fabrics_type_auth_receive: +return nvme_trace_fabrics_auth_receive (p, spc);default :return nvme_trace_fabrics_common (p, spc); }
Signature · 1 line
2.29.2
Sagi Grimberg
maintainerRe: [PATCH 06/11] nvme: Implement In-Band authentication
Quoted 2 lines
Implement NVMe-oF In-Band authentication. This patch adds two new fabric options '
dhchap_key' to specify the PSK
pre-shared-key.
Also, we need a sysfs knob to rotate the key that will trigger re-authentication or even a simple controller(s-plural) reset, so this should go beyond just the connection string.
P.S. can you add also the nvme-cli code in the next go?
Quoted 2 lines
and '
dhchap_authenticate'
to request bi-directional authentication of both the host and the controller.
bidirectional? not uni-directional?
Quoted 23 lines
Signed-off-by Hannes Reinecke <[email protected]>--- drivers/nvme/host/Kconfig | 11 + drivers/nvme/host/Makefile | 1 + drivers/nvme/host/auth.c | 813 ++++++++++++++++++++++++++++++++++++ drivers/nvme/host/auth.h | 23 + drivers/nvme/host/core.c | 77 +++- drivers/nvme/host/fabrics.c | 65 ++- drivers/nvme/host/fabrics.h | 8 + drivers/nvme/host/nvme.h | 15 + drivers/nvme/host/trace.c | 32 ++ 9 files changed, 1041 insertions(+), 4 deletions(-)create mode 100644
drivers/nvme/host/auth.c
create mode 100644drivers/nvme/host/auth.hdrivers/nvme/host/Kconfig +0
-0@@ -85,3 +85,14 @@ config NVME_TCP from https://github.com/linux-nvme/nvme-cli.
drivers/nvme/host/Kconfig config NVME_TCP · line 96
Quoted 10 lines
drivers/nvme/host/Kconfig +9
-0If unsure, say N. + +config NVME_AUTH + bool "NVM Express over Fabrics In-Band Authentication" + depends on NVME_TCP + select CRYPTO_SHA256 + select CRYPTO_SHA512 + help + This provides support for NVMe over Fabrics In-Band Authentication + for the NVMe over TCP transport.
In this form, nothing is specific to nvme-tcp here afaict.
Quoted 27 lines
drivers/nvme/host/Kconfig +2
-0+ + If unsure, say N.drivers/nvme/host/Makefile +1
-0@@ -20,6 +20,7 @@ nvme-core-$(CONFIG_NVME_HWMON) += hwmon.o nvme-y += pci.o nvme-fabrics-y += fabrics.o +nvme-fabrics-$(CONFIG_NVME_AUTH) += auth.o nvme-rdma-y += rdma.odrivers/nvme/host/auth.c new file +7
-0@@ -0,0 +1,813 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2020 Hannes Reinecke, SUSE Linux +*/ + +#include <linux/crc32.h> +#include <linux/base64.h>
drivers/nvme/host/auth.c line 17
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+#include <asm/unaligned.h> +#include <crypto/hash.h> +#include <crypto/kpp.h> +#include "nvme.h" +#include "fabrics.h" +#include "auth.h" + +static u32 nvme_dhchap_seqnum; + +struct nvme_dhchap_context {
Maybe nvme_dhchap_queue_context ?
I'm thinking that we should perhaps split
it to host-wide, subsys-wide and queue specific
auth contexts?
Let's see...
Quoted 12 lines
drivers/nvme/host/auth.c new file +12
-0+struct crypto_shash *shash_tfm; +unsigned char *key; +size_t key_len; +int qid; +u32 s1; +u32 s2; +u16 transaction; +u8 status; +u8 hash_id; +u8 hash_len; +u8 c1[64 ]; +u8 c2[64 ];
drivers/nvme/host/auth.c line 39
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+u8 response[64 ]; +u8 *ctrl_key; +int ctrl_key_len; +u8 *host_key; +int host_key_len; +u8 *sess_key; +int sess_key_len; +}; + +struct nvmet_dhchap_hash_map {
nvmet?
Quoted 7 lines
drivers/nvme/host/auth.c new file +7
-0+int id; +int hash_len; +const char hmac[15 ]; +const char digest[15 ]; +} hash_map[] = { + {.id = NVME_AUTH_DHCHAP_HASH_SHA256, + .hash_len = 32,
drivers/nvme/host/auth.c line 56
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ .hmac = "hmac(sha256)", .digest = "sha256" }, + {.id = NVME_AUTH_DHCHAP_HASH_SHA384, + .hash_len = 48, + .hmac = "hmac(sha384)", .digest = "sha384" }, + {.id = NVME_AUTH_DHCHAP_HASH_SHA512, + .hash_len = 64, + .hmac = "hmac(sha512)", .digest = "sha512" }, +}; + +const char *nvme_auth_hmac_name (int hmac_id)
Should these arrays be static?
Quoted 47 lines
drivers/nvme/host/auth.c new file +47
-0+{ +int i; + +for (i =0 ; i <ARRAY_SIZE (hash_map); i++) { +if (hash_map[i].id == hmac_id) +return hash_map[i].hmac; + } +return NULL ; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_name); + +const char *nvme_auth_digest_name (int hmac_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (hash_map); i++) { +if (hash_map[i].id == hmac_id) +return hash_map[i].digest; + } +return NULL ; +} +EXPORT_SYMBOL_GPL (nvme_auth_digest_name); + +int nvme_auth_hmac_len (int hmac_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (hash_map); i++) { +if (hash_map[i].id == hmac_id) +return hash_map[i].hash_len; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_len); + +int nvme_auth_hmac_id (const char *hmac_name) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (hash_map); i++) { +if (!strncmp (hash_map[i].hmac, hmac_name, +strlen (hash_map[i].hmac))) +return hash_map[i].id; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_id);
drivers/nvme/host/auth.c line 113
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ +unsigned char *nvme_auth_extract_secret (unsigned char *dhchap_secret, +size_t *dhchap_key_len) +{ +unsigned char *dhchap_key; +u32 crc; +int key_len; +size_t allocated_len; + + allocated_len =strlen (dhchap_secret) -10 ;
the 10 feels like a magic here, should at least note this is the "DHHC-1:..." prefix.
Quoted 5 lines
drivers/nvme/host/auth.c new file +5
-0+ dhchap_key =kzalloc (allocated_len,GFP_KERNEL ); +if (!dhchap_key) +return ERR_PTR (-ENOMEM ); + + key_len = base64_decode(dhchap_secret + 10,
drivers/nvme/host/auth.c line 128
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ allocated_len, dhchap_key); +if (key_len !=36 && key_len !=52 && + key_len !=68 ) { +pr_debug ("Invalid DH-HMAC-CHAP key len %d\n" , + key_len); +kfree (dhchap_key); +return ERR_PTR (-EINVAL ); + } +pr_debug ("DH-HMAC-CHAP Key: %*ph\n" , + (int )key_len, dhchap_key);
One can argue if even printing this is problematic..
Quoted 32 lines
drivers/nvme/host/auth.c new file +32
-0+ +/* The last four bytes is the CRC in little-endian format */ + key_len -=4 ; +/* +* The linux implementation doesn't do pre- and post-increments, +* so we have to do it manually. +*/ + crc = ~crc32 (~0 , dhchap_key, key_len); + +if (get_unaligned_le32 (dhchap_key + key_len) != crc) { +pr_debug ("DH-HMAC-CHAP crc mismatch (key %08x, crc %08x)\n" , +get_unaligned_le32 (dhchap_key + key_len), crc); +kfree (dhchap_key); +return ERR_PTR (-EKEYREJECTED ); + } + *dhchap_key_len = key_len; +return dhchap_key; +} +EXPORT_SYMBOL_GPL (nvme_auth_extract_secret); + +static int nvme_auth_send (struct nvme_ctrl *ctrl,int qid, +void *data,size_t tl) +{ +struct nvme_command cmd = {}; +blk_mq_req_flags_t flags = qid ==NVME_QID_ANY ? +0 :BLK_MQ_REQ_NOWAIT |BLK_MQ_REQ_RESERVED ; +struct request_queue *q = qid ==NVME_QID_ANY ? + ctrl->fabrics_q : ctrl->connect_q; +int ret; + + cmd.auth_send.opcode = nvme_fabrics_command; + cmd.auth_send.fctype = nvme_fabrics_type_auth_send;
drivers/nvme/host/auth.c line 170
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ cmd.auth_send.secp =NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER ; + cmd.auth_send.spsp0 =0x01 ; + cmd.auth_send.spsp1 =0x01 ; + cmd.auth_send.tl = tl; + + ret =__nvme_submit_sync_cmd (q, &cmd,NULL , data, tl,0 , qid, +0 , flags); +if (ret) +dev_dbg (ctrl->device, +"%s: qid %d error %d\n" , __func__, qid, ret);
Maybe a little more informative print rather than __func__ ?
Quoted 50 lines
drivers/nvme/host/auth.c new file +50
-0+return ret; +} + +static int nvme_auth_receive (struct nvme_ctrl *ctrl,int qid, +void *buf,size_t al, +u16 transaction,u8 expected_msg ) +{ +struct nvme_command cmd = {}; +struct nvmf_auth_dhchap_failure_data *data = buf; +blk_mq_req_flags_t flags = qid ==NVME_QID_ANY ? +0 :BLK_MQ_REQ_NOWAIT |BLK_MQ_REQ_RESERVED ; +struct request_queue *q = qid ==NVME_QID_ANY ? + ctrl->fabrics_q : ctrl->connect_q; +int ret; + + cmd.auth_receive.opcode = nvme_fabrics_command; + cmd.auth_receive.fctype = nvme_fabrics_type_auth_receive; + cmd.auth_receive.secp =NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER ; + cmd.auth_receive.spsp0 =0x01 ; + cmd.auth_receive.spsp1 =0x01 ; + cmd.auth_receive.al = al; + + ret =__nvme_submit_sync_cmd (q, &cmd,NULL , buf, al,0 , qid, +0 , flags); +if (ret >0 ) { +dev_dbg (ctrl->device,"%s: qid %d nvme status %x\n" , + __func__, qid, ret); + ret = -EIO ; + } +if (ret <0 ) { +dev_dbg (ctrl->device,"%s: qid %d error %d\n" , + __func__, qid, ret); +return ret; + } +dev_dbg (ctrl->device,"%s: qid %d auth_type %d auth_id %x\n" , + __func__, qid, data->auth_type, data->auth_id); +if (data->auth_type ==NVME_AUTH_COMMON_MESSAGES && + data->auth_id ==NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ) { +return data->reason_code_explanation; + } +if (data->auth_type !=NVME_AUTH_DHCHAP_MESSAGES || + data->auth_id != expected_msg) { +dev_warn (ctrl->device, +"qid %d invalid message %02x/%02x\n" , + qid, data->auth_type, data->auth_id); +return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; + } +if (le16_to_cpu (data->t_id) != transaction) { + dev_warn(ctrl->device, + "qid %d invalid transaction ID %d\n",
drivers/nvme/host/auth.c line 230
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ qid, le16_to_cpu(data->t_id)); +return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; + } + +return 0 ; +} + +static int nvme_auth_dhchap_negotiate (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size)
Maybe nvme_auth_set_dhchap_negotiate_data ?
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+{ +struct nvmf_auth_dhchap_negotiate_data *data = buf; +size_t size =sizeof (*data) +sizeof (union nvmf_auth_protocol ); + +if (buf_size < size) +return -EINVAL ; + +memset ((u8 *)buf,0 , size); + data->auth_type =NVME_AUTH_COMMON_MESSAGES ; + data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE ;
drivers/nvme/host/auth.c line 250
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ data->t_id =cpu_to_le16 (chap->transaction); + data->sc_c =0 ;/* No secure channel concatenation */ + data->napd =1 ; + data->auth_protocol[0 ].dhchap.authid =NVME_AUTH_DHCHAP_AUTH_ID ; + data->auth_protocol[0 ].dhchap.halen =3 ; + data->auth_protocol[0 ].dhchap.dhlen =1 ; + data->auth_protocol[0 ].dhchap.idlist[0 ] =NVME_AUTH_DHCHAP_HASH_SHA256 ; + data->auth_protocol[0 ].dhchap.idlist[1 ] =NVME_AUTH_DHCHAP_HASH_SHA384 ; + data->auth_protocol[0 ].dhchap.idlist[2 ] =NVME_AUTH_DHCHAP_HASH_SHA512 ; + data->auth_protocol[0 ].dhchap.idlist[3 ] =NVME_AUTH_DHCHAP_DHGROUP_NULL ;
You should comment that this routine expects buf to have enough room for both negotiate and auth_proto structures.
drivers/nvme/host/auth.c line 257
Quoted 7 lines
drivers/nvme/host/auth.c new file +7
-0+ +return size; +} + +static int nvme_auth_dhchap_challenge (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size)
Maybe nvme_auth_process_dhchap_challange ?
Quoted 24 lines
drivers/nvme/host/auth.c new file +24
-0+{ +struct nvmf_auth_dhchap_challenge_data *data = buf; +size_t size =sizeof (*data) + data->hl + data->dhvlen; +const char *gid_name; + +if (buf_size < size) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; +return -ENOMSG ; + } + +if (data->hashid !=NVME_AUTH_DHCHAP_HASH_SHA256 && + data->hashid !=NVME_AUTH_DHCHAP_HASH_SHA384 && + data->hashid !=NVME_AUTH_DHCHAP_HASH_SHA512 ) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: invalid HASH ID %d\n" , + chap->qid, data->hashid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ; +return -EPROTO ; + } +switch (data->dhgid) { +case NVME_AUTH_DHCHAP_DHGROUP_NULL : + gid_name ="null" ; +break ; +default :
drivers/nvme/host/auth.c line 291
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ gid_name =NULL ; +break ; + } +if (!gid_name) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: invalid DH group id %d\n" , + chap->qid, data->dhgid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; +return -EPROTO ; + }
Maybe some spaces between condition blocks?
Quoted 21 lines
drivers/nvme/host/auth.c new file +21
-0+if (data->dhgid !=NVME_AUTH_DHCHAP_DHGROUP_NULL ) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; +return -EPROTO ; + } +if (data->dhgid ==NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen !=0 ) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; +return -EPROTO ; + } +dev_dbg (ctrl->device,"%s: qid %d requested hash id %d\n" , + __func__, chap->qid, data->hashid); +if (nvme_auth_hmac_len (data->hashid) != data->hl) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: invalid hash length\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ; +return -EPROTO ; + } + chap->hash_id = data->hashid;
drivers/nvme/host/auth.c line 322
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ chap->hash_len = data->hl; + chap->s1 =le32_to_cpu (data->seqnum); +memcpy (chap->c1, data->cval, chap->hash_len); + +return 0 ; +} + +static int nvme_auth_dhchap_reply (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size)
nvme_auth_set_dhchap_reply
drivers/nvme/host/auth.c line 328
Quoted 6 lines
drivers/nvme/host/auth.c new file +6
-0+{ +struct nvmf_auth_dhchap_reply_data *data = buf; +size_t size =sizeof (*data); + + size +=2 * chap->hash_len; +if (ctrl->opts->dhchap_auth) {
The ctrl opts is not clear to me. what is dhchap_auth mean?
Also shouldn't these params be lifted to the subsys?
Quoted 27 lines
drivers/nvme/host/auth.c new file +27
-0+get_random_bytes (chap->c2, chap->hash_len); + chap->s2 = nvme_dhchap_seqnum++; + }else +memset (chap->c2,0 , chap->hash_len); + +if (chap->host_key_len) + size += chap->host_key_len; + +if (buf_size < size) +return -EINVAL ; + +memset (buf,0 , size); + data->auth_type =NVME_AUTH_DHCHAP_MESSAGES ; + data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_REPLY ; + data->t_id =cpu_to_le16 (chap->transaction); + data->hl = chap->hash_len; + data->dhvlen = chap->host_key_len; + data->seqnum =cpu_to_le32 (chap->s2); +memcpy (data->rval, chap->response, chap->hash_len); +if (ctrl->opts->dhchap_auth) { +dev_dbg (ctrl->device,"%s: qid %d ctrl challenge %*ph\n" , + __func__, chap->qid, + chap->hash_len, chap->c2); + data->cvalid =1 ; +memcpy (data->rval + chap->hash_len, chap->c2, + chap->hash_len); + }
drivers/nvme/host/auth.c line 365
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+if (chap->host_key_len) +memcpy (data->rval +2 * chap->hash_len, chap->host_key, + chap->host_key_len); + +return size; +} + +static int nvme_auth_dhchap_success1 (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size)
nvme_auth_process_dhchap_success1
Quoted 35 lines
drivers/nvme/host/auth.c new file +35
-0+{ +struct nvmf_auth_dhchap_success1_data *data = buf; +size_t size =sizeof (*data); + +if (ctrl->opts->dhchap_auth) + size += chap->hash_len; + + +if (buf_size < size) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; +return -ENOMSG ; + } + +if (data->hl != chap->hash_len) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: invalid hash length %d\n" , + chap->qid, data->hl); + chap->status =NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ; +return -EPROTO ; + } + +if (!data->rvalid) +return 0 ; + +/* Validate controller response */ +if (memcmp (chap->response, data->rval, data->hl)) { +dev_dbg (ctrl->device,"%s: qid %d ctrl response %*ph\n" , + __func__, chap->qid, chap->hash_len, data->rval); +dev_dbg (ctrl->device,"%s: qid %d host response %*ph\n" , + __func__, chap->qid, chap->hash_len, chap->response); +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: controller authentication failed\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; +return -EPROTO ;
drivers/nvme/host/auth.c line 410
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ } +dev_info (ctrl->device, +"qid %d: DH-HMAC-CHAP: controller authenticated\n" , + chap->qid); +return 0 ; +} + +static int nvme_auth_dhchap_success2 (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size)
same
Quoted 5 lines
drivers/nvme/host/auth.c new file +5
-0+{ +struct nvmf_auth_dhchap_success2_data *data = buf; +size_t size =sizeof (*data); + +memset (buf,0 , size);
drivers/nvme/host/auth.c line 425
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ data->auth_type =NVME_AUTH_DHCHAP_MESSAGES ; + data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 ; + data->t_id =cpu_to_le16 (chap->transaction); + +return size; +} + +static int nvme_auth_dhchap_failure2 (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size)
same
Quoted 6 lines
drivers/nvme/host/auth.c new file +6
-0+{ +struct nvmf_auth_dhchap_failure_data *data = buf; +size_t size =sizeof (*data); + +memset (buf,0 , size); + data->auth_type =NVME_AUTH_DHCHAP_MESSAGES ;
drivers/nvme/host/auth.c line 441
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 ; + data->t_id =cpu_to_le16 (chap->transaction); + data->reason_code =1 ; + data->reason_code_explanation = chap->status; + +return size; +} + +int nvme_auth_select_hash (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap)
Maybe _select_hf (hash function)? not a must, just sticks to the spec language.
Quoted 24 lines
drivers/nvme/host/auth.c new file +24
-0+{ +char *hash_name; +int ret; + +switch (chap->hash_id) { +case NVME_AUTH_DHCHAP_HASH_SHA256 : + hash_name ="hmac(sha256)" ; +break ; +case NVME_AUTH_DHCHAP_HASH_SHA384 : + hash_name ="hmac(sha384)" ; +break ; +case NVME_AUTH_DHCHAP_HASH_SHA512 : + hash_name ="hmac(sha512)" ; +break ; +default : + hash_name =NULL ; +break ; + } +if (!hash_name) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +return -EPROTO ; + } + chap->shash_tfm =crypto_alloc_shash (hash_name,0 , +CRYPTO_ALG_ALLOCATES_MEMORY );
drivers/nvme/host/auth.c line 475
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+if (IS_ERR (chap->shash_tfm)) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; + chap->shash_tfm =NULL ; +return -EPROTO ; + } +if (!chap->key) { +dev_warn (ctrl->device,"qid %d: cannot select hash, no key\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->shash_tfm);
Wouldn't it better to check this before allocating the tfm?
Quoted 34 lines
drivers/nvme/host/auth.c new file +34
-0+ chap->shash_tfm =NULL ; +return -EINVAL ; + } + ret =crypto_shash_setkey (chap->shash_tfm, chap->key, chap->key_len); +if (ret) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->shash_tfm); + chap->shash_tfm =NULL ; +return ret; + } +dev_info (ctrl->device,"qid %d: DH-HMAC_CHAP: selected hash %s\n" , + chap->qid, hash_name); +return 0 ; +} + +static int nvme_auth_dhchap_host_response (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap) +{ +SHASH_DESC_ON_STACK (shash, chap->shash_tfm); +u8 buf[4 ], *challenge = chap->c1; +int ret; + +dev_dbg (ctrl->device,"%s: qid %d host response seq %d transaction %d\n" , + __func__, chap->qid, chap->s1, chap->transaction); + shash->tfm = chap->shash_tfm; + ret =crypto_shash_init (shash); +if (ret) + goto out; + ret =crypto_shash_update (shash, challenge, chap->hash_len); +if (ret) + goto out; +put_unaligned_le32 (chap->s1, buf); + ret =crypto_shash_update (shash, buf,4 ); +if (ret)
drivers/nvme/host/auth.c line 519
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ goto out; +put_unaligned_le16 (chap->transaction, buf); + ret =crypto_shash_update (shash, buf,2 ); +if (ret) + goto out; +memset (buf,0 ,sizeof (buf)); + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash,"HostHost" ,8 );
HostHost ? Can you refer me to the specific section that talks about this?
Would be good to have a comment on the format fed to the shash.
Quoted 72 lines
drivers/nvme/host/auth.c new file +72
-0+if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->opts->host->nqn, +strlen (ctrl->opts->host->nqn)); +if (ret) + goto out; + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->opts->subsysnqn, +strlen (ctrl->opts->subsysnqn)); +if (ret) + goto out; + ret =crypto_shash_final (shash, chap->response); +out: +return ret; +} + +static int nvme_auth_dhchap_ctrl_response (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap) +{ +SHASH_DESC_ON_STACK (shash, chap->shash_tfm); +u8 buf[4 ], *challenge = chap->c2; +int ret; + +dev_dbg (ctrl->device,"%s: qid %d host response seq %d transaction %d\n" , + __func__, chap->qid, chap->s2, chap->transaction); +dev_dbg (ctrl->device,"%s: qid %d challenge %*ph\n" , + __func__, chap->qid, chap->hash_len, challenge); +dev_dbg (ctrl->device,"%s: qid %d subsysnqn %s\n" , + __func__, chap->qid, ctrl->opts->subsysnqn); +dev_dbg (ctrl->device,"%s: qid %d hostnqn %s\n" , + __func__, chap->qid, ctrl->opts->host->nqn); + shash->tfm = chap->shash_tfm; + ret =crypto_shash_init (shash); +if (ret) + goto out; + ret =crypto_shash_update (shash, challenge, chap->hash_len); +if (ret) + goto out; +put_unaligned_le32 (chap->s2, buf); + ret =crypto_shash_update (shash, buf,4 ); +if (ret) + goto out; +put_unaligned_le16 (chap->transaction, buf); + ret =crypto_shash_update (shash, buf,2 ); +if (ret) + goto out; +memset (buf,0 ,4 ); + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash,"Controller" ,10 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->opts->subsysnqn, +strlen (ctrl->opts->subsysnqn)); +if (ret) + goto out; + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->opts->host->nqn, +strlen (ctrl->opts->host->nqn)); +if (ret) + goto out; + ret =crypto_shash_final (shash, chap->response); +out: +return ret; +} + +int nvme_auth_generate_key(struct nvme_ctrl *ctrl,
drivers/nvme/host/auth.c line 601
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ struct nvme_dhchap_context *chap) +{ +int ret; +u8 key_hash; +const char *hmac_name; +struct crypto_shash *key_tfm; + +if (sscanf (ctrl->opts->dhchap_secret,"DHHC-1:%hhd:%*s:" , + &key_hash) !=1 ) +return -EINVAL ;
I'd expect that the user will pass in a secret key (as binary) the the driver will build the spec compliant formatted string no?
Am I not reading this correctly?
Quoted 7 lines
drivers/nvme/host/auth.c new file +7
-0+ + chap->key =nvme_auth_extract_secret (ctrl->opts->dhchap_secret, + &chap->key_len); +if (IS_ERR (chap->key)) { + ret =PTR_ERR (chap->key); + chap->key =NULL ; +return ret;
drivers/nvme/host/auth.c line 618
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ } + +if (key_hash ==0 ) +return 0 ; + + hmac_name =nvme_auth_hmac_name (key_hash); +if (!hmac_name) { +pr_debug ("Invalid key hash id %d\n" , key_hash); +return -EKEYREJECTED ; + }
Why does the user influence the hmac used? isn't that is driven by the susbsystem?
I don't think that the user should choose in this level.
drivers/nvme/host/auth.c line 624
Quoted 6 lines
drivers/nvme/host/auth.c new file +6
-0+ + key_tfm =crypto_alloc_shash (hmac_name,0 ,0 ); +if (IS_ERR (key_tfm)) { +kfree (chap->key); + chap->key =NULL ; + ret =PTR_ERR (key_tfm);
You set ret and later return 0? I think that the success path in the else clause is hard to read and error prone...
Quoted 8 lines
drivers/nvme/host/auth.c new file +8
-0+ }else { +SHASH_DESC_ON_STACK (shash, key_tfm); + + shash->tfm = key_tfm; + ret =crypto_shash_setkey (key_tfm, chap->key, + chap->key_len); +if (ret <0 ) { +crypto_free_shash (key_tfm);
drivers/nvme/host/auth.c line 642
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+kfree (chap->key); + chap->key =NULL ; +return ret; + } +crypto_shash_init (shash); +crypto_shash_update (shash, ctrl->opts->host->nqn, +strlen (ctrl->opts->host->nqn)); +crypto_shash_update (shash,"NVMe-over-Fabrics" ,17 ); +crypto_shash_final (shash, chap->key); +crypto_free_shash (key_tfm);
Shouldn't these be done when preparing the dh-hmac-chap reply?
Quoted 6 lines
drivers/nvme/host/auth.c new file +6
-0+ } +return 0 ; +} + +void nvme_auth_free (struct nvme_dhchap_context *chap) +{
drivers/nvme/host/auth.c line 658
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+if (chap->shash_tfm) +crypto_free_shash (chap->shash_tfm); +if (chap->key) +kfree (chap->key); +if (chap->ctrl_key) +kfree (chap->ctrl_key); +if (chap->host_key) +kfree (chap->host_key); +if (chap->sess_key) +kfree (chap->sess_key);
No need to check null for kfree...
Quoted 26 lines
drivers/nvme/host/auth.c new file +26
-0+kfree (chap); +} + +int nvme_auth_negotiate (struct nvme_ctrl *ctrl,int qid) +{ +struct nvme_dhchap_context *chap; +void *buf; +size_t buf_size, tl; +int ret =0 ; + + chap =kzalloc (sizeof (*chap),GFP_KERNEL ); +if (!chap) +return -ENOMEM ; + chap->qid = qid; + chap->transaction = ctrl->transaction++; + + ret =nvme_auth_generate_key (ctrl, chap); +if (ret) { +dev_dbg (ctrl->device,"%s: failed to generate key, error %d\n" , + __func__, ret); +nvme_auth_free (chap); +return ret; + } + +/* +* Allocate a large enough buffer for the entire negotiation:
drivers/nvme/host/auth.c line 694
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+* 4k should be enough to ffdhe8192. +*/ + buf_size =4096 ; + buf =kzalloc (buf_size,GFP_KERNEL ); +if (!buf) { + ret = -ENOMEM ; + goto out; + } + +/* DH-HMAC-CHAP Step 1: send negotiate */
I'd consider breaking these into sub-routines.
Quoted 283 lines
drivers/nvme/host/auth.c new file +119
-0+dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP negotiate\n" , + __func__, qid); + ret =nvme_auth_dhchap_negotiate (ctrl, chap, buf, buf_size); +if (ret <0 ) + goto out; + tl = ret; + ret =nvme_auth_send (ctrl, qid, buf, tl); +if (ret) + goto out; + +memset (buf,0 , buf_size); + ret =nvme_auth_receive (ctrl, qid, buf, buf_size, chap->transaction, +NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE ); +if (ret <0 ) { +dev_dbg (ctrl->device, +"%s: qid %d DH-HMAC-CHAP failed to receive challenge\n" , + __func__, qid); + goto out; + } +if (ret >0 ) { + chap->status = ret; + goto fail1; + } + +/* DH-HMAC-CHAP Step 2: receive challenge */ +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP challenge\n" , + __func__, qid); + + ret =nvme_auth_dhchap_challenge (ctrl, chap, buf, buf_size); +if (ret) { +/* Invalid parameters for negotiate */ + goto fail2; + } + +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP select hash\n" , + __func__, qid); + ret =nvme_auth_select_hash (ctrl, chap); +if (ret) + goto fail2; + +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP host response\n" , + __func__, qid); + ret =nvme_auth_dhchap_host_response (ctrl, chap); +if (ret) + goto fail2; + +/* DH-HMAC-CHAP Step 3: send reply */ +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP reply\n" , + __func__, qid); + ret =nvme_auth_dhchap_reply (ctrl, chap, buf, buf_size); +if (ret <0 ) + goto fail2; + + tl = ret; + ret =nvme_auth_send (ctrl, qid, buf, tl); +if (ret) + goto fail2; + +memset (buf,0 , buf_size); + ret =nvme_auth_receive (ctrl, qid, buf, buf_size, chap->transaction, +NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1 ); +if (ret <0 ) { +dev_dbg (ctrl->device, +"%s: qid %d DH-HMAC-CHAP failed to receive success1\n" , + __func__, qid); + goto out; + } +if (ret >0 ) { + chap->status = ret; + goto fail1; + } + +if (ctrl->opts->dhchap_auth) { +dev_dbg (ctrl->device, +"%s: qid %d DH-HMAC-CHAP controller response\n" , + __func__, qid); + ret =nvme_auth_dhchap_ctrl_response (ctrl, chap); +if (ret) + goto fail2; + } + +/* DH-HMAC-CHAP Step 4: receive success1 */ +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP success1\n" , + __func__, qid); + ret =nvme_auth_dhchap_success1 (ctrl, chap, buf, buf_size); +if (ret <0 ) { +/* Controller authentication failed */ + goto fail2; + } + tl = ret; +/* DH-HMAC-CHAP Step 5: send success2 */ +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP success2\n" , + __func__, qid); + tl =nvme_auth_dhchap_success2 (ctrl, chap, buf, buf_size); + ret =nvme_auth_send (ctrl, qid, buf, tl); +if (!ret) + goto out; + +fail1: +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP failure1, status %x\n" , + __func__, qid, chap->status); + goto out; + +fail2: +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP failure2, status %x\n" , + __func__, qid, chap->status); + tl =nvme_auth_dhchap_failure2 (ctrl, chap, buf, buf_size); + ret =nvme_auth_send (ctrl, qid, buf, tl); + +out: +if (!ret && chap->status) + ret = -EPROTO ; +if (!ret) { + ctrl->dhchap_hash = chap->hash_id; + } +kfree (buf); +nvme_auth_free (chap); +return ret; +}drivers/nvme/host/auth.h new file +23
-0@@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions +*/ + +#ifndef _NVME_AUTH_H +#define _NVME_AUTH_H + +const char *nvme_auth_dhgroup_name (int dhgroup_id); +int nvme_auth_dhgroup_pubkey_size (int dhgroup_id); +int nvme_auth_dhgroup_privkey_size (int dhgroup_id); +const char *nvme_auth_dhgroup_kpp (int dhgroup_id); +int nvme_auth_dhgroup_id (const char *dhgroup_name); + +const char *nvme_auth_hmac_name (int hmac_id); +const char *nvme_auth_digest_name (int hmac_id); +int nvme_auth_hmac_id (const char *hmac_name); +int nvme_auth_hmac_len (int hmac_len); + +unsigned char *nvme_auth_extract_secret (unsigned char *dhchap_secret, +size_t *dhchap_key_len); + +#endif /* _NVME_AUTH_H */ drivers/nvme/host/core.c +76
-1@@ -708,7 +708,9 @@ bool __nvme_check_ready(struct nvme_ctrl *ctrl, struct request *rq,switch (ctrl->state) {case NVME_CTRL_CONNECTING :if (blk_rq_is_passthrough (rq) &&nvme_is_fabrics (req->cmd) && - req->cmd->fabrics.fctype == nvme_fabrics_type_connect) + (req->cmd->fabrics.fctype == nvme_fabrics_type_connect || + req->cmd->fabrics.fctype == nvme_fabrics_type_auth_send || + req->cmd->fabrics.fctype == nvme_fabrics_type_auth_receive))return true;break ;default :@@ -3426,6 +3428,66 @@ static ssize_t nvme_ctrl_fast_io_fail_tmo_store(struct device *dev, static DEVICE_ATTR(fast_io_fail_tmo, S_IRUGO | S_IWUSR, nvme_ctrl_fast_io_fail_tmo_show, nvme_ctrl_fast_io_fail_tmo_store); +#ifdef CONFIG_NVME_AUTH +struct nvmet_dhchap_hash_map { +int id; +const char name[15 ]; +} hash_map[] = { + {.id =NVME_AUTH_DHCHAP_HASH_SHA256 , + .name ="hmac(sha256)" , }, + {.id =NVME_AUTH_DHCHAP_HASH_SHA384 , + .name ="hmac(sha384)" , }, + {.id =NVME_AUTH_DHCHAP_HASH_SHA512 , + .name ="hmac(sha512)" , }, +}; + +static ssize_t dhchap_hash_show (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct nvme_ctrl *ctrl =dev_get_drvdata (dev); +int i; + +for (i =0 ; i <ARRAY_SIZE (hash_map); i++) { +if (hash_map[i].id == ctrl->dhchap_hash) +return sprintf (buf,"%s\n" , hash_map[i].name); + } +return sprintf (buf,"none\n" ); +} +DEVICE_ATTR_RO (dhchap_hash); + +struct nvmet_dhchap_group_map { +int id; +const char name[15 ]; +} dhgroup_map[] = { + {.id =NVME_AUTH_DHCHAP_DHGROUP_NULL , + .name ="NULL" , }, + {.id =NVME_AUTH_DHCHAP_DHGROUP_2048 , + .name ="ffdhe2048" , }, + {.id =NVME_AUTH_DHCHAP_DHGROUP_3072 , + .name ="ffdhe3072" , }, + {.id =NVME_AUTH_DHCHAP_DHGROUP_4096 , + .name ="ffdhe4096" , }, + {.id =NVME_AUTH_DHCHAP_DHGROUP_6144 , + .name ="ffdhe6144" , }, + {.id =NVME_AUTH_DHCHAP_DHGROUP_8192 , + .name ="ffdhe8192" , }, +}; + +static ssize_t dhchap_dhgroup_show (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct nvme_ctrl *ctrl =dev_get_drvdata (dev); +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (hash_map[i].id == ctrl->dhchap_dhgroup) +return sprintf (buf,"%s\n" , dhgroup_map[i].name); + } +return sprintf (buf,"none\n" ); +} +DEVICE_ATTR_RO (dhchap_dhgroup); +#endif +static struct attribute *nvme_dev_attrs[] = { &dev_attr_reset_controller.attr, &dev_attr_rescan_controller.attr,@@ -3447,6 +3509,10 @@ static struct attribute *nvme_dev_attrs[] = { &dev_attr_reconnect_delay.attr, &dev_attr_fast_io_fail_tmo.attr, &dev_attr_kato.attr, +#ifdef CONFIG_NVME_AUTH + &dev_attr_dhchap_hash.attr, + &dev_attr_dhchap_dhgroup.attr, +#endif NULL };@@ -3470,6 +3536,10 @@ static umode_t nvme_dev_attrs_are_visible(struct kobject *kobj,return 0 ;if (a == &dev_attr_fast_io_fail_tmo.attr && !ctrl->opts)return 0 ; +#ifdef CONFIG_NVME_AUTH +if (a == &dev_attr_dhchap_hash.attr && !ctrl->opts) +return 0 ; +#endif return a->mode; }@@ -4581,6 +4651,11 @@ static inline void _nvme_check_size(void)BUILD_BUG_ON (sizeof (struct nvme_smart_log ) !=512 );BUILD_BUG_ON (sizeof (struct nvme_dbbuf ) !=64 );BUILD_BUG_ON (sizeof (struct nvme_directive_cmd ) !=64 ); +BUILD_BUG_ON (sizeof (struct nvmf_auth_dhchap_negotiate_data ) !=8 ); +BUILD_BUG_ON (sizeof (struct nvmf_auth_dhchap_challenge_data ) !=16 ); +BUILD_BUG_ON (sizeof (struct nvmf_auth_dhchap_reply_data ) !=16 ); +BUILD_BUG_ON (sizeof (struct nvmf_auth_dhchap_success1_data ) !=16 ); +BUILD_BUG_ON (sizeof (struct nvmf_auth_dhchap_success2_data ) !=16 ); }drivers/nvme/host/fabrics.c +2
-2@@ -366,6 +366,7 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl)union nvme_result res;struct nvmf_connect_data *data;int ret; +u32 result; cmd.connect.opcode = nvme_fabrics_command; cmd.connect.fctype = nvme_fabrics_type_connect;@@ -398,8 +399,18 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) goto out_free_data; } - ctrl->cntlid =le16_to_cpu (res.u16); - + result =le32_to_cpu (res.u32);
drivers/nvme/host/fabrics.c int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) · line 412
Quoted 10 lines
drivers/nvme/host/fabrics.c +10
-0+ ctrl->cntlid = result &0xFFFF ; +if ((result >>16 ) &2 ) { +/* Authentication required */ + ret =nvme_auth_negotiate (ctrl,NVME_QID_ANY ); +if (ret) +dev_warn (ctrl->device, +"qid 0: authentication failed\n" ); +else +dev_info (ctrl->device, +"qid 0: authenticated\n" );
info is too chatty.
Quoted 31 lines
drivers/nvme/host/fabrics.c +13
-0+ } out_free_data:kfree (data);return ret;@@ -432,6 +443,7 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid)struct nvmf_connect_data *data;union nvme_result res;int ret; +u32 result; cmd.connect.opcode = nvme_fabrics_command; cmd.connect.fctype = nvme_fabrics_type_connect;@@ -457,6 +469,17 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid)nvmf_log_connect_error (ctrl, ret,le32_to_cpu (res.u32), &cmd, data); } + result =le32_to_cpu (res.u32); +if ((result >>16 ) &2 ) { +/* Authentication required */ + ret =nvme_auth_negotiate (ctrl, qid); +if (ret) +dev_warn (ctrl->device, +"qid %u: authentication failed\n" , qid); +else +dev_info (ctrl->device, +"qid %u: authenticated\n" , qid); + }kfree (data);return ret; }
drivers/nvme/host/fabrics.c static const match_table_t opt_tokens = { · line 576
Quoted 6 lines
drivers/nvme/host/fabrics.c +3
-0@@ -548,6 +571,9 @@ static const match_table_t opt_tokens = { { NVMF_OPT_NR_POLL_QUEUES, "nr_poll_queues=%d" }, { NVMF_OPT_TOS, "tos=%d" }, { NVMF_OPT_FAIL_FAST_TMO, "fast_io_fail_tmo=%d" }, + { NVMF_OPT_DHCHAP_SECRET, "dhchap_secret=%s" }, + { NVMF_OPT_DHCHAP_AUTH, "authenticate" }, + { NVMF_OPT_DHCHAP_GROUP, "dhchap_group=%s" },
Isn't the group driven by the subsystem? also why is there a "authenticate" boolean? what is it good for?
Quoted 97 lines
drivers/nvme/host/fabrics.c +34
-1{ NVMF_OPT_ERR, NULL } };@@ -824,6 +850,35 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts, } opts->tos = token;break ; +case NVMF_OPT_DHCHAP_SECRET : + p =match_strdup (args); +if (!p) { + ret = -ENOMEM ; + goto out; + } +if (strncmp (p,"DHHC-1:00:" ,10 )) { +pr_err ("Invalid DH-CHAP secret %s\n" , p); + ret = -EINVAL ; + goto out; + } +kfree (opts->dhchap_secret); + opts->dhchap_secret = p; +break ; +case NVMF_OPT_DHCHAP_AUTH : + opts->dhchap_auth = true; +break ; +case NVMF_OPT_DHCHAP_GROUP : +if (match_int (args, &token)) { + ret = -EINVAL ; + goto out; + } +if (token <=0 ) { +pr_err ("Invalid dhchap_group %d\n" , token); + ret = -EINVAL ; + goto out; + } + opts->dhchap_group = token; +break ;default :pr_warn ("unknown parameter or missing value '%s' in ctrl creation request\n" , p);@@ -942,6 +997,7 @@ void nvmf_free_options(struct nvmf_ctrl_options *opts)kfree (opts->subsysnqn);kfree (opts->host_traddr);kfree (opts->host_iface); +kfree (opts->dhchap_secret);kfree (opts); }EXPORT_SYMBOL_GPL (nvmf_free_options);@@ -951,7 +1007,10 @@ EXPORT_SYMBOL_GPL(nvmf_free_options); NVMF_OPT_KATO | NVMF_OPT_HOSTNQN | \ NVMF_OPT_HOST_ID | NVMF_OPT_DUP_CONNECT |\ NVMF_OPT_DISABLE_SQFLOW |\ - NVMF_OPT_FAIL_FAST_TMO) + NVMF_OPT_CTRL_LOSS_TMO |\ + NVMF_OPT_FAIL_FAST_TMO |\ + NVMF_OPT_DHCHAP_SECRET |\ + NVMF_OPT_DHCHAP_AUTH | NVMF_OPT_DHCHAP_GROUP)static struct nvme_ctrl *nvmf_create_ctrl (struct device *dev,const char *buf)drivers/nvme/host/fabrics.h +8
-0@@ -67,6 +67,9 @@ enum {NVMF_OPT_TOS =1 <<19 ,NVMF_OPT_FAIL_FAST_TMO =1 <<20 ,NVMF_OPT_HOST_IFACE =1 <<21 , +NVMF_OPT_DHCHAP_SECRET =1 <<22 , +NVMF_OPT_DHCHAP_AUTH =1 <<23 , + NVMF_OPT_DHCHAP_GROUP = 1 << 24, };/** @@ -96,6 +99,8 @@ enum {* @max_reconnects: maximum number of allowed reconnect attempts before removing * the controller, (-1) means reconnect forever, zero means remove * immediately; +* @dhchap_secret: DH-HMAC-CHAP secret +* @dhchap_auth: DH-HMAC-CHAP authenticate controller * @disable_sqflow: disable controller sq flow control * @hdr_digest: generate/verify header digest (TCP) * @data_digest: generate/verify data digest (TCP) @@ -120,6 +125,9 @@ struct nvmf_ctrl_options {unsigned int kato;struct nvmf_host *host;int max_reconnects; +char *dhchap_secret; +int dhchap_group; +bool dhchap_auth;bool disable_sqflow;bool hdr_digest;bool data_digest;drivers/nvme/host/nvme.h +0
-0
drivers/nvme/host/nvme.h struct nvme_ctrl { · line 334
Quoted 7 lines
drivers/nvme/host/nvme.h +4
-0@@ -328,6 +328,12 @@ struct nvme_ctrl {struct work_struct ana_work;#endif +#ifdef CONFIG_NVME_AUTH +u16 transaction; +u8 dhchap_hash; +u8 dhchap_dhgroup;
Do multiple controllers in the same subsystem have different params? no, so I think these should be lifted to subsys.
drivers/nvme/host/trace.c
Quoted 25 lines
drivers/nvme/host/nvme.h +11
-0+#endif +/* Power saving configuration */ u64 ps_max_latency_us;bool apst_enabled;@@ -874,6 +880,15 @@ static inline bool nvme_ctrl_sgl_supported(struct nvme_ctrl *ctrl)return ctrl->sgls & ((1 <<0 ) | (1 <<1 )); } +#ifdef CONFIG_NVME_AUTH +int nvme_auth_negotiate (struct nvme_ctrl *ctrl,int qid); +#else +static inline int nvme_auth_negotiate (struct nvme_ctrl *ctrl,int qid) +{ +return -EPROTONOSUPPORT ; +} +#endif +u32 nvme_command_effects (struct nvme_ctrl *ctrl,struct nvme_ns *ns,u8 opcode);int nvme_execute_passthru_rq (struct request *rq);drivers/nvme/host/trace.c +0
-0
I'd split out the tracing logic.
Quoted 47 lines
drivers/nvme/host/trace.c +32
-0@@ -271,6 +271,34 @@ static const char *nvme_trace_fabrics_property_get(struct trace_seq *p, u8 *spc)return ret; } +static const char *nvme_trace_fabrics_auth_send (struct trace_seq *p,u8 *spc) +{ +const char *ret =trace_seq_buffer_ptr (p); +u8 spsp0 = spc[1 ]; +u8 spsp1 = spc[2 ]; +u8 secp = spc[3 ]; +u32 tl =get_unaligned_le32 (spc +4 ); + +trace_seq_printf (p,"spsp0=%02x, spsp1=%02x, secp=%02x, tl=%u" , + spsp0, spsp1, secp, tl); +trace_seq_putc (p,0 ); +return ret; +} + +static const char *nvme_trace_fabrics_auth_receive (struct trace_seq *p,u8 *spc) +{ +const char *ret =trace_seq_buffer_ptr (p); +u8 spsp0 = spc[1 ]; +u8 spsp1 = spc[2 ]; +u8 secp = spc[3 ]; +u32 al =get_unaligned_le32 (spc +4 ); + +trace_seq_printf (p,"spsp0=%02x, spsp1=%02x, secp=%02x, al=%u" , + spsp0, spsp1, secp, al); +trace_seq_putc (p,0 ); +return ret; +} +static const char *nvme_trace_fabrics_common (struct trace_seq *p,u8 *spc) {const char *ret =trace_seq_buffer_ptr (p);@@ -290,6 +318,10 @@ const char *nvme_trace_parse_fabrics_cmd(struct trace_seq *p,return nvme_trace_fabrics_connect (p, spc);case nvme_fabrics_type_property_get:return nvme_trace_fabrics_property_get (p, spc); +case nvme_fabrics_type_auth_send: +return nvme_trace_fabrics_auth_send (p, spc); +case nvme_fabrics_type_auth_receive: +return nvme_trace_fabrics_auth_receive (p, spc);default :return nvme_trace_fabrics_common (p, spc); }
Hannes Reinecke
maintainerRe: [PATCH 06/11] nvme: Implement In-Band authentication
Quoted 9 lines · Sagi Grimberg
Quoted 2 lines
Implement NVMe-oF In-Band authentication. This patch adds two new fabric options '
dhchap_key' to specify the PSKpre-shared-key.
Also, we need a sysfs knob to rotate the key that will trigger re-authentication or even a simple controller(s-plural) reset, so this should go beyond just the connection string.
Yeah, re-authentication currently is not implemented. I first wanted to get this patchset out such that we can settle on the userspace interface (both from host and target).
I'll have to think on how we should handle authentication; one of the really interesting cases would be when one malicious admin will _just_ send a 'negotiate' command to the controller. As per spec the controller will be waiting for an 'authentication receive' command to send a 'challenge' payload back to the host. But that will never come, so as it stands currently the controller is required to abort the connection. Not very nice.
Quoted 2 lines
P.S. can you add also the nvme-cli code in the next go?
Oh, sure. It's already sitting around in my local repo (surprise, surprise); will be ending it out next time.
Quoted 5 lines
Quoted 2 lines
and '
dhchap_authenticate'
to request bi-directional authentication of both the host and the controller.bidirectional? not uni-directional?
Yeah, that's a bit of a misnomer. When a PSK is specified, the controller will start the authentication protocol such that the _controller_ can validate the host. If the host wants to authenticate the controller is needs to set this flag. Hence bi-directional authentication.
But I'm the first to admit that this is poor wording for the flag.
Quoted 26 lines
Quoted 26 lines
Signed-off-by Hannes Reinecke <[email protected]>---
drivers/nvme/host/Kconfig | 11 +
drivers/nvme/host/Makefile | 1 +drivers/nvme/host/auth.c | 813 ++++++++++++++++++++++++++++++++++++ drivers/nvme/host/auth.h | 23 + drivers/nvme/host/core.c | 77 +++-
drivers/nvme/host/fabrics.c| 65 ++-
drivers/nvme/host/fabrics.h| 8 +
drivers/nvme/host/nvme.h | 15 +
drivers/nvme/host/trace.c | 32 ++9 files changed, 1041 insertions(+), 4 deletions(-)create mode 100644
drivers/nvme/host/auth.c
create mode 100644drivers/nvme/host/auth.hdrivers/nvme/host/Kconfig +0
-0@@ -85,3 +85,14 @@ config NVME_TCPfrom https://github.com/linux-nvme/nvme-cli.
If unsure, say N.drivers/nvme/host/Kconfig +3
-0+ +config NVME_AUTH + bool "NVM Express over Fabrics In-Band Authentication"
Quoted 9 lines
Quoted 6 lines
+ depends on
NVME_TCP
+ selectCRYPTO_SHA256
+ selectCRYPTO_SHA512
+ help
+ This provides support for NVMe over Fabrics In-Band Authentication + for the NVMe over TCP transport.In this form, nothing is specific to nvme-tcp here afaict.
Indeed. I guess we can leave out the nvme-tcp reference here.
Quoted 37 lines
Quoted 24 lines
drivers/nvme/host/Kconfig +2
-0+ + If unsure, say N.drivers/nvme/host/Makefile +0
-0@@ -20,6 +20,7 @@ nvme-core-$(CONFIG_NVME_HWMON) += hwmon.onvme-y += pci.o
nvme-fabrics-y += fabrics.o
+nvme-fabrics-$(CONFIG_NVME_AUTH) += auth.o
nvme-rdma-y += rdma.odrivers/nvme/host/auth.c new file +7
-0@@ -0,0 +1,813 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2020 Hannes Reinecke, SUSE Linux +*/ + +#include <linux/crc32.h> +#include <linux/base64.h> drivers/nvme/host/auth.c line 17
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+#include <asm/unaligned.h> +#include <crypto/hash.h> +#include <crypto/kpp.h> +#include "nvme.h" +#include "fabrics.h" +#include "auth.h" + +static u32 nvme_dhchap_seqnum; + +struct nvme_dhchap_context {Maybe
nvme_dhchap_queue_context?
Quoted 6 lines
I'm thinking that we should perhaps split
it to host-wide, subsys-wide and queue specific
auth contexts?Let's see...
Interestingly enough, that's what I did for the target side.
For the host side I found it easier that way, as then we'll have a single authentication context which can be deleted after authentication finished, and be sure that we removed _all_ information. Security and all that.
Splitting it off would require to remove information on three different places, and observing life-time rules for them.
So more of a chance to mess things up :-)
Quoted 16 lines
Quoted 16 lines
drivers/nvme/host/auth.c new file +16
-0+ struct crypto_shash *shash_tfm; + unsigned char *key; + size_t key_len; + int qid; + u32 s1; + u32 s2; + u16 transaction; + u8 status; + u8 hash_id; + u8 hash_len; + u8 c1[64]; + u8 c2[64]; + u8 response[64]; + u8 *ctrl_key; + int ctrl_key_len; + u8 *host_key;
Quoted 9 lines
drivers/nvme/host/auth.c line 39
Quoted 6 lines
drivers/nvme/host/auth.c new file +6
-0+ int host_key_len; + u8 *sess_key; + int sess_key_len; +}; + +struct nvmet_dhchap_hash_map {nvmet?
Yeah; originally I coded that for the target side, and only later moved it into the host side to have it usable for both. Will be fixing it up.
Quoted 11 lines
Quoted 11 lines
drivers/nvme/host/auth.c new file +11
-0+ int id; + int hash_len; + const char hmac[15]; + const char digest[15]; +} hash_map[] = { + {.id = NVME_AUTH_DHCHAP_HASH_SHA256, + .hash_len = 32, + .hmac = "hmac(sha256)", .digest = "sha256" }, + {.id = NVME_AUTH_DHCHAP_HASH_SHA384, + .hash_len = 48, + .hmac = "hmac(sha384)", .digest = "sha384" },
Quoted 9 lines
drivers/nvme/host/auth.c line 56
Quoted 6 lines
drivers/nvme/host/auth.c new file +6
-0+ {.id = NVME_AUTH_DHCHAP_HASH_SHA512, + .hash_len = 64, + .hmac = "hmac(sha512)", .digest = "sha512" }, +}; + +const char *nvme_auth_hmac_name (int hmac_id)Should these arrays be static?
Definitely.
Quoted 52 lines
Quoted 52 lines
drivers/nvme/host/auth.c new file +52
-0+{ + int i; + + for (i = 0; i < ARRAY_SIZE(hash_map); i++) { + if (hash_map[i].id == hmac_id) + return hash_map[i].hmac; + } + return NULL; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_name); + +const char *nvme_auth_digest_name (int hmac_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hash_map); i++) { + if (hash_map[i].id == hmac_id) + return hash_map[i].digest; + } + return NULL; +} +EXPORT_SYMBOL_GPL (nvme_auth_digest_name); + +int nvme_auth_hmac_len (int hmac_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hash_map); i++) { + if (hash_map[i].id == hmac_id) + return hash_map[i].hash_len; + } + return -1; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_len); + +int nvme_auth_hmac_id (const char *hmac_name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hash_map); i++) { + if (!strncmp(hash_map[i].hmac, hmac_name, + strlen(hash_map[i].hmac))) + return hash_map[i].id; + } + return -1; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_id); + +unsigned char *nvme_auth_extract_secret (unsigned char *dhchap_secret, + size_t *dhchap_key_len) +{ + unsigned char *dhchap_key;
Quoted 9 lines
drivers/nvme/host/auth.c line 113
Quoted 5 lines
drivers/nvme/host/auth.c new file +5
-0+ u32 crc; + int key_len; + size_t allocated_len; + + allocated_len = strlen(dhchap_secret) - 10;the 10 feels like a magic here, should at least note this is the "DHHC-1:..." prefix.
It _is_ magic. And it might even be better to just pass in the string _without_ the DHHC-1: prefix.
Quoted 16 lines
Quoted 5 lines
drivers/nvme/host/auth.c new file +5
-0+ dhchap_key = kzalloc(allocated_len, GFP_KERNEL); + if (!dhchap_key) + return ERR_PTR(-ENOMEM); + + key_len = base64_decode(dhchap_secret + 10,drivers/nvme/host/auth.c line 128
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ allocated_len, dhchap_key); + if (key_len != 36 && key_len != 52 && + key_len != 68) { + pr_debug("Invalid DH-HMAC-CHAP key len %d\n", + key_len); + kfree(dhchap_key); + return ERR_PTR(-EINVAL); + } + pr_debug("DH-HMAC-CHAP Key: %*ph\n", + (int)key_len, dhchap_key);
Quoted 2 lines
One can argue if even printing this is problematic..
Debugging scaffolding. You wouldn't believe how many things can go wrong...
And yes, that should be removed.
Quoted 36 lines
Quoted 36 lines
drivers/nvme/host/auth.c new file +36
-0+ + /* The last four bytes is the CRC in little-endian format */ + key_len -= 4; + /* + * The linux implementation doesn't do pre- and post-increments, + * so we have to do it manually. + */ + crc = ~crc32(~0, dhchap_key, key_len); + + if (get_unaligned_le32(dhchap_key + key_len) != crc) { + pr_debug("DH-HMAC-CHAP crc mismatch (key %08x, crc %08x)\n", + get_unaligned_le32(dhchap_key + key_len), crc); + kfree(dhchap_key); + return ERR_PTR(-EKEYREJECTED); + } + *dhchap_key_len = key_len; + return dhchap_key; +} +EXPORT_SYMBOL_GPL (nvme_auth_extract_secret); + +static int nvme_auth_send (struct nvme_ctrl *ctrl,int qid, + void *data, size_t tl) +{ + struct nvme_command cmd = {}; + blk_mq_req_flags_t flags = qid == NVME_QID_ANY ? + 0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED; + struct request_queue *q = qid == NVME_QID_ANY ? + ctrl->fabrics_q : ctrl->connect_q; + int ret; + + cmd.auth_send.opcode = nvme_fabrics_command; + cmd.auth_send.fctype = nvme_fabrics_type_auth_send; + cmd.auth_send.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER; + cmd.auth_send.spsp0 = 0x01; + cmd.auth_send.spsp1 = 0x01; + cmd.auth_send.tl = tl;
Quoted 9 lines
drivers/nvme/host/auth.c line 170
Quoted 6 lines
drivers/nvme/host/auth.c new file +6
-0+ + ret = __nvme_submit_sync_cmd(q, &cmd, NULL, data, tl, 0, qid, + 0, flags); + if (ret) + dev_dbg(ctrl->device, + "%s: qid %d error %d\n", __func__, qid, ret);Maybe a little more informative print rather than
__func__?
Yes, can do.
Quoted 54 lines
Quoted 54 lines
drivers/nvme/host/auth.c new file +54
-0+ return ret; +} + +static int nvme_auth_receive (struct nvme_ctrl *ctrl,int qid, + void *buf, size_t al, + u16 transaction, u8 expected_msg ) +{ + struct nvme_command cmd = {}; + struct nvmf_auth_dhchap_failure_data *data = buf; + blk_mq_req_flags_t flags = qid == NVME_QID_ANY ? + 0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED; + struct request_queue *q = qid == NVME_QID_ANY ? + ctrl->fabrics_q : ctrl->connect_q; + int ret; + + cmd.auth_receive.opcode = nvme_fabrics_command; + cmd.auth_receive.fctype = nvme_fabrics_type_auth_receive; + cmd.auth_receive.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER; + cmd.auth_receive.spsp0 = 0x01; + cmd.auth_receive.spsp1 = 0x01; + cmd.auth_receive.al = al; + + ret = __nvme_submit_sync_cmd(q, &cmd, NULL, buf, al, 0, qid, + 0, flags); + if (ret > 0) { + dev_dbg(ctrl->device, "%s: qid %d nvme status %x\n", + __func__, qid, ret); + ret = -EIO; + } + if (ret < 0) { + dev_dbg(ctrl->device, "%s: qid %d error %d\n", + __func__, qid, ret); + return ret; + } + dev_dbg(ctrl->device, "%s: qid %d auth_type %d auth_id %x\n", + __func__, qid, data->auth_type, data->auth_id); + if (data->auth_type == NVME_AUTH_COMMON_MESSAGES && + data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) { + return data->reason_code_explanation; + } + if (data->auth_type != NVME_AUTH_DHCHAP_MESSAGES || + data->auth_id != expected_msg) { + dev_warn(ctrl->device, + "qid %d invalid message %02x/%02x\n", + qid, data->auth_type, data->auth_id); + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD; + } + if (le16_to_cpu(data->t_id) != transaction) { + dev_warn(ctrl->device, + "qid %d invalid transaction ID %d\n", + qid, le16_to_cpu(data->t_id)); + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD; + } +
Quoted 9 lines
drivers/nvme/host/auth.c line 230
Quoted 6 lines
drivers/nvme/host/auth.c new file +6
-0+ return 0; +} + +static int nvme_auth_dhchap_negotiate (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap, + void *buf, size_t buf_size)Maybe
nvme_auth_set_dhchap_negotiate_data?
These are the individual steps in the state machine later on, so I wanted to keep the names identical. But I'm open to suggestions.
Quoted 18 lines
Quoted 18 lines
drivers/nvme/host/auth.c new file +17
-0+{ + struct nvmf_auth_dhchap_negotiate_data *data = buf; + size_t size = sizeof(*data) + sizeof(union nvmf_auth_protocol); + + if (buf_size < size) + return -EINVAL; + + memset((u8 *)buf, 0, size); + 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 */ + data->napd = 1; + data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID; + data->auth_protocol[0].dhchap.halen = 3; + data->auth_protocol[0].dhchap.dhlen = 1; + data->auth_protocol[0].dhchap.idlist[0] =
NVME_AUTH_DHCHAP_HASH_SHA256;
Quoted 9 lines
Quoted 6 lines
+ data->auth_protocol[0].dhchap.idlist[1] =
NVME_AUTH_DHCHAP_HASH_SHA384;
+ data->auth_protocol[0].dhchap.idlist[2] =NVME_AUTH_DHCHAP_HASH_SHA512;
+ data->auth_protocol[0].dhchap.idlist[3] =NVME_AUTH_DHCHAP_DHGROUP_NULL;You should comment that this routine expects buf to have enough room for both negotiate and
auth_protostructures.
Hmm. I do a check for the overall size at the start, so I'm not sure what this will buy us. And actually, anyone wanting to make sense of the implementation would need to look at the spec anyway.
Quoted 10 lines
drivers/nvme/host/auth.c line 257
Quoted 7 lines
drivers/nvme/host/auth.c new file +7
-0+ + return size; +} + +static int nvme_auth_dhchap_challenge (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap, + void *buf, size_t buf_size)Maybe
nvme_auth_process_dhchap_challange?
See above. I'd rather have consistent names for the state machine. But I can change them to 'nvme_process_chchap_<statename>'
Quoted 28 lines
Quoted 28 lines
drivers/nvme/host/auth.c new file +28
-0+{ + struct nvmf_auth_dhchap_challenge_data *data = buf; + size_t size = sizeof(*data) + data->hl + data->dhvlen; + const char *gid_name; + + if (buf_size < size) { + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD; + return -ENOMSG; + } + + if (data->hashid != NVME_AUTH_DHCHAP_HASH_SHA256 && + data->hashid != NVME_AUTH_DHCHAP_HASH_SHA384 && + data->hashid != NVME_AUTH_DHCHAP_HASH_SHA512) { + dev_warn(ctrl->device, + "qid %d: DH-HMAC-CHAP: invalid HASH ID %d\n", + chap->qid, data->hashid); + chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; + return -EPROTO; + } + switch (data->dhgid) { + case NVME_AUTH_DHCHAP_DHGROUP_NULL: + gid_name = "null"; + break; + default: + gid_name = NULL; + break; + } + if (!gid_name) {
Quoted 9 lines
Quoted 6 lines
+ dev_warn(ctrl->device,
+ "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n", +chap->qid,data->dhgid);
+chap->status=NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; + return-EPROTO;
+ }Maybe some spaces between condition blocks?
Ok.
Quoted 26 lines
Quoted 26 lines
+ if (
data->dhgid!=NVME_AUTH_DHCHAP_DHGROUP_NULL) {
+chap->status=NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; + return-EPROTO;
+ }
+ if (data->dhgid==NVME_AUTH_DHCHAP_DHGROUP_NULL&&data->dhvlen!= 0) {
+ dev_warn(ctrl->device,
+ "qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n", +chap->qid);
+chap->status=NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; + return-EPROTO;
+ }
+ dev_dbg(ctrl->device, "%s: qid %d requested hash id %d\n", +__func__,chap->qid,data->hashid);
+ if (nvme_auth_hmac_len(data->hashid)!=data->hl) {
+ dev_warn(ctrl->device,
+ "qid %d: DH-HMAC-CHAP: invalid hash length\n", +chap->qid);
+chap->status=NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; + return-EPROTO;
+ }
+chap->hash_id=data->hashid;
+chap->hash_len=data->hl;
+chap->s1=le32_to_cpu(data->seqnum);
+memcpy(chap->c1, data->cval, chap->hash_len);
+
Quoted 9 lines
drivers/nvme/host/auth.c line 322
Quoted 6 lines
+ return 0;
drivers/nvme/host/auth.c new file +5
-0+} + +static int nvme_auth_dhchap_reply (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap, + void *buf, size_t buf_size)
nvme_auth_set_dhchap_reply
Ah. Now I see what you're getting at.
Okay, will be changing it.
Quoted 10 lines
drivers/nvme/host/auth.c line 328
Quoted 6 lines
drivers/nvme/host/auth.c new file +6
-0+{ + struct nvmf_auth_dhchap_reply_data *data = buf; + size_t size = sizeof(*data); + + size += 2 * chap->hash_len; + if (ctrl->opts->dhchap_auth) {The ctrl opts is not clear to me. what is
dhchap_authmean?
As stated above, this is for bi-directional authentication. And yes, it is poor wording.
'dhchap_bidirectional' ?
Quoted 2 lines
Also shouldn't these params be lifted to the subsys?
I kinda like to have it all encapsulated in a common per-queue structure; on the host side this one isn't even attached to anything, so any new authentication attempt will allocate a new one, with no chance of accidentally re-using existing values. I thought this to be a rather nice property for a state-machine.
Quoted 31 lines
Quoted 31 lines
+
get_random_bytes(chap->c2, chap->hash_len); +chap->s2= nvme_dhchap_seqnum++;
+ } else
+memset(chap->c2, 0, chap->hash_len);drivers/nvme/host/auth.c new file +27
-0+ + if (chap->host_key_len) + size += chap->host_key_len; + + if (buf_size < size) + return -EINVAL; + + memset(buf, 0, size); + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES; + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_REPLY; + data->t_id = cpu_to_le16(chap->transaction); + data->hl = chap->hash_len; + data->dhvlen = chap->host_key_len; + data->seqnum = cpu_to_le32(chap->s2); + memcpy(data->rval, chap->response, chap->hash_len); + if (ctrl->opts->dhchap_auth) { + dev_dbg(ctrl->device, "%s: qid %d ctrl challenge %*ph\n", + __func__, chap->qid, + chap->hash_len, chap->c2); + data->cvalid = 1; + memcpy(data->rval + chap->hash_len, chap->c2, + chap->hash_len); + } + if (chap->host_key_len) + memcpy(data->rval + 2 * chap->hash_len, chap->host_key, + chap->host_key_len); +
Quoted 9 lines
drivers/nvme/host/auth.c line 365
Quoted 6 lines
+ return size;
drivers/nvme/host/auth.c new file +5
-0+} + +static int nvme_auth_dhchap_success1 (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap, + void *buf, size_t buf_size)
nvme_auth_process_dhchap_success1
OK.
Quoted 77 lines
Quoted 35 lines
drivers/nvme/host/auth.c new file +35
-0+{ + struct nvmf_auth_dhchap_success1_data *data = buf; + size_t size = sizeof(*data); + + if (ctrl->opts->dhchap_auth) + size += chap->hash_len; + + + if (buf_size < size) { + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD; + return -ENOMSG; + } + + if (data->hl != chap->hash_len) { + dev_warn(ctrl->device, + "qid %d: DH-HMAC-CHAP: invalid hash length %d\n", + chap->qid, data->hl); + chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; + return -EPROTO; + } + + if (!data->rvalid) + return 0; + + /* Validate controller response */ + if (memcmp(chap->response, data->rval, data->hl)) { + dev_dbg(ctrl->device, "%s: qid %d ctrl response %*ph\n", + __func__, chap->qid, chap->hash_len, data->rval); + dev_dbg(ctrl->device, "%s: qid %d host response %*ph\n", + __func__, chap->qid, chap->hash_len, chap->response); + dev_warn(ctrl->device, + "qid %d: DH-HMAC-CHAP: controller authentication failed\n", + chap->qid); + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD; + return -EPROTO;drivers/nvme/host/auth.c line 410
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ } + dev_info(ctrl->device, + "qid %d: DH-HMAC-CHAP: controller authenticated\n", + chap->qid); + return 0; +} + +static int nvme_auth_dhchap_success2 (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap, + void *buf, size_t buf_size)same
Quoted 5 lines
drivers/nvme/host/auth.c new file +5
-0+{ + struct nvmf_auth_dhchap_success2_data *data = buf; + size_t size = sizeof(*data); + + memset(buf, 0, size);drivers/nvme/host/auth.c line 425
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ data->auth_type = NVME_AUTH_DHCHAP_MESSAGES; + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2; + data->t_id = cpu_to_le16(chap->transaction); + + return size; +} + +static int nvme_auth_dhchap_failure2 (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap, + void *buf, size_t buf_size)same
Quoted 11 lines
drivers/nvme/host/auth.c new file +11
-0+{ + struct nvmf_auth_dhchap_failure_data *data = buf; + size_t size = sizeof(*data); + + memset(buf, 0, size); + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES; + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2; + data->t_id = cpu_to_le16(chap->transaction); + data->reason_code = 1; + data->reason_code_explanation = chap->status; +
Quoted 9 lines
drivers/nvme/host/auth.c line 441
Quoted 5 lines
+ return size;
drivers/nvme/host/auth.c new file +4
-0+} + +int nvme_auth_select_hash (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap)Maybe
_select_hf(hash function)? not a must, just sticks to the spec language.
Hmm. Will be checking.
Quoted 28 lines
Quoted 28 lines
drivers/nvme/host/auth.c new file +28
-0+{ + char *hash_name; + int ret; + + switch (chap->hash_id) { + case NVME_AUTH_DHCHAP_HASH_SHA256: + hash_name = "hmac(sha256)"; + break; + case NVME_AUTH_DHCHAP_HASH_SHA384: + hash_name = "hmac(sha384)"; + break; + case NVME_AUTH_DHCHAP_HASH_SHA512: + hash_name = "hmac(sha512)"; + break; + default: + hash_name = NULL; + break; + } + if (!hash_name) { + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE; + return -EPROTO; + } + chap->shash_tfm = crypto_alloc_shash(hash_name, 0, + CRYPTO_ALG_ALLOCATES_MEMORY); + if (IS_ERR(chap->shash_tfm)) { + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE; + chap->shash_tfm = NULL; + return -EPROTO;
Quoted 9 lines
Quoted 6 lines
+ }
+ if (!chap->key) {
+dev_warn(ctrl->device, "qid %d: cannot select hash, no key\n", + chap->qid);
+chap->status=NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE; +crypto_free_shash(chap->shash_tfm);Wouldn't it better to check this before allocating the tfm?
Indeed. Will be changing it.
Quoted 41 lines
Quoted 41 lines
+
chap->shash_tfm= NULL;
+ return-EINVAL;
+ }
+ ret =crypto_shash_setkey(chap->shash_tfm, chap->key, chap->key_len);
+ if (ret) {
+chap->status=NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE; +crypto_free_shash(chap->shash_tfm);
+chap->shash_tfm= NULL;
+ return ret;
+ }
+dev_info(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n", + chap->qid, hash_name);
+ return 0;drivers/nvme/host/auth.c new file +10
-0+} + +static int nvme_auth_dhchap_host_response (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap) +{ + SHASH_DESC_ON_STACK(shash, chap->shash_tfm); + u8 buf[4], *challenge = chap->c1; + int ret; + + dev_dbg(ctrl->device, "%s: qid %d host response seq %dtransaction %d\n",
+__func__,chap->qid,chap->s1,chap->transaction); +shash->tfm=chap->shash_tfm;
+ ret =crypto_shash_init(shash);
+ if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, challenge, chap->hash_len); + if (ret)
+ goto out;
+put_unaligned_le32(chap->s1, buf);
+ ret =crypto_shash_update(shash, buf, 4);
+ if (ret)
+ goto out;
+put_unaligned_le16(chap->transaction, buf);
+ ret =crypto_shash_update(shash, buf, 2);
+ if (ret)
+ goto out;
Quoted 9 lines
Quoted 5 lines
+
memset(buf, 0, sizeof(buf));
+ ret =crypto_shash_update(shash, buf, 1); + if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, "HostHost", 8);HostHost ? Can you refer me to the specific section that talks about this?
NVMe 2.0 section DH-HMAC-CHAP_Reply Message, paragraph Response Value. HostHost.
Quoted 3 lines
Would be good to have a comment on the format fed to the shash.
Yes, will be doing so.
Quoted 78 lines
Quoted 78 lines
+ if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, ctrl->opts->host->nqn, + strlen(ctrl->opts->host->nqn)); + if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, buf, 1);
+ if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, ctrl->opts->subsysnqn, + strlen(ctrl->opts->subsysnqn));
+ if (ret)
+ goto out;
+ ret =crypto_shash_final(shash, chap->response);drivers/nvme/host/auth.c new file +12
-0+out: + return ret; +} + +static int nvme_auth_dhchap_ctrl_response (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap) +{ + SHASH_DESC_ON_STACK(shash, chap->shash_tfm); + u8 buf[4], *challenge = chap->c2; + int ret; + + dev_dbg(ctrl->device, "%s: qid %d host response seq %dtransaction %d\n",
+__func__,chap->qid,chap->s2,chap->transaction); + dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n", +__func__,chap->qid,chap->hash_len, challenge); + dev_dbg(ctrl->device, "%s: qid %d subsysnqn %s\n", +__func__,chap->qid,ctrl->opts->subsysnqn);
+ dev_dbg(ctrl->device, "%s: qid %d hostnqn %s\n",
+__func__,chap->qid,ctrl->opts->host->nqn);
+shash->tfm=chap->shash_tfm;
+ ret =crypto_shash_init(shash);
+ if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, challenge, chap->hash_len); + if (ret)
+ goto out;
+put_unaligned_le32(chap->s2, buf);
+ ret =crypto_shash_update(shash, buf, 4);
+ if (ret)
+ goto out;
+put_unaligned_le16(chap->transaction, buf);
+ ret =crypto_shash_update(shash, buf, 2);
+ if (ret)
+ goto out;
+memset(buf, 0, 4);
+ ret =crypto_shash_update(shash, buf, 1);
+ if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, "Controller", 10); + if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, ctrl->opts->subsysnqn, + strlen(ctrl->opts->subsysnqn));
+ if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, buf, 1);
+ if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, ctrl->opts->host->nqn, + strlen(ctrl->opts->host->nqn));
+ if (ret)
+ goto out;
+ ret =crypto_shash_final(shash, chap->response);drivers/nvme/host/auth.c new file +10
-0+out: + return ret; +} + +int nvme_auth_generate_key (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap) +{ + int ret; + u8 key_hash; + const char *hmac_name;
Quoted 10 lines
Quoted 5 lines
+ struct
crypto_shash*key_tfm;
+
+ if (sscanf(ctrl->opts->dhchap_secret, "DHHC-1:%hhd:%*s:", + &key_hash)!= 1)
+ return-EINVAL;I'd expect that the user will pass in a secret key (as binary) the the driver will build the spec compliant formatted string no?
Quoted 1 line
Am I not reading this correctly?
I'm under the impression that this is the format into which the User/Admin will get hold of the secret key. Spec says:
'... all NVMe over Fabrics entities shall support the following ASCII representation of secrets ...'
And as the userspace interface is the only way how the user/admin _could_ interact with the NVMe over Fabrics entities in Linux I guess we'll need to be able to parse it.
We sure could allow a binary secret, too, but then what would be the point in converting it into the secret representation? The protocol revolves around the binary secret, not the transport representation.
Quoted 21 lines
Quoted 7 lines
drivers/nvme/host/auth.c new file +7
-0+ + chap->key = nvme_auth_extract_secret(ctrl->opts->dhchap_secret, + &chap->key_len); + if (IS_ERR(chap->key)) { + ret = PTR_ERR(chap->key); + chap->key = NULL; + return ret;drivers/nvme/host/auth.c line 618
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ } + + if (key_hash == 0) + return 0; + + hmac_name = nvme_auth_hmac_name(key_hash); + if (!hmac_name) { + pr_debug("Invalid key hash id %d\n", key_hash); + return -EKEYREJECTED; + }Why does the user influence the hmac used? isn't that is driven by the susbsystem?
Quoted 2 lines
I don't think that the user should choose in this level.
That is another weirdness of the spec.
The _secret_ will be hashed with a specific function, and that function is stated in the transport representation. (Cf section "DH-HMAC-CHAP Security Requirements").
This is _not_ the hash function used by the authentication itself, which will be selected by the protocol. So it's not the user here, but rather the transport specification of the key which selects the hash algorithm.
Quoted 10 lines
Quoted 6 lines
+
+key_tfm=crypto_alloc_shash(hmac_name, 0, 0); + if (IS_ERR(key_tfm)) {
+kfree(chap->key);
+chap->key= NULL;
+ ret =PTR_ERR(key_tfm);You set ret and later return 0? I think that the success path in the else clause is hard to read and error prone...
Do I? Will need to fix it up.
Quoted 19 lines
Quoted 10 lines
+ } else {
+SHASH_DESC_ON_STACK(shash, key_tfm); +
+shash->tfm=key_tfm;
+ ret =crypto_shash_setkey(key_tfm, chap->key, + chap->key_len);
+ if (ret < 0) {
+crypto_free_shash(key_tfm);
+kfree(chap->key);
+chap->key= NULL;Quoted 8 lines
+ return ret;
+ }
+crypto_shash_init(shash);
+ crypto_shash_update(shash,ctrl->opts->host->nqn, +strlen(ctrl->opts->host->nqn)); +crypto_shash_update(shash, "NVMe-over-Fabrics", 17); +crypto_shash_final(shash, chap->key);
+crypto_free_shash(key_tfm);
Quoted 2 lines
Shouldn't these be done when preparing the dh-hmac-chap reply?
By setting the hash here I avoid having to pass the required hash function for the secret transformation.
I could be doing the entire secret transformation thingie when preparing the reply; reason why I did it here is that _having_ a secret is the precondition to everything else, so I wanted to check upfront for that. But I'll check what would happen if I move it.
Quoted 10 lines
Quoted 10 lines
+ }
+ return 0;drivers/nvme/host/auth.c new file +8
-0+} + +void nvme_auth_free (struct nvme_dhchap_context *chap) +{ + if (chap->shash_tfm) + crypto_free_shash(chap->shash_tfm); + if (chap->key) + kfree(chap->key);
Quoted 9 lines
Quoted 6 lines
+ if (
chap->ctrl_key)
+kfree(chap->ctrl_key);
+ if (chap->host_key)
+kfree(chap->host_key);
+ if (chap->sess_key)
+kfree(chap->sess_key);No need to check null for kfree...
Will be fixing it up.
Quoted 30 lines
Quoted 30 lines
+ kfree(chap);
drivers/nvme/host/auth.c new file +29
-0+} + +int nvme_auth_negotiate (struct nvme_ctrl *ctrl,int qid) +{ + struct nvme_dhchap_context *chap; + void *buf; + size_t buf_size, tl; + int ret = 0; + + chap = kzalloc(sizeof(*chap), GFP_KERNEL); + if (!chap) + return -ENOMEM; + chap->qid = qid; + chap->transaction = ctrl->transaction++; + + ret = nvme_auth_generate_key(ctrl, chap); + if (ret) { + dev_dbg(ctrl->device, "%s: failed to generate key, error %d\n", + __func__, ret); + nvme_auth_free(chap); + return ret; + } + + /* + * Allocate a large enough buffer for the entire negotiation: + * 4k should be enough to ffdhe8192. + */ + buf_size = 4096; + buf = kzalloc(buf_size, GFP_KERNEL);
Quoted 9 lines
Quoted 6 lines
+ if (!buf) {
+ ret =-ENOMEM;
+ goto out;
+ }
+
+ /* DH-HMAC-CHAP Step 1: send negotiate */I'd consider breaking these into sub-routines.
Which ones? The preparation step?
Sure, can do.
Quoted 288 lines
Quoted 288 lines
+
dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP negotiate\n", + __func__, qid);
+ ret =nvme_auth_dhchap_negotiate(ctrl, chap, buf, buf_size); + if (ret < 0)
+ goto out;
+ tl = ret;
+ ret =nvme_auth_send(ctrl, qid, buf, tl);
+ if (ret)
+ goto out;drivers/nvme/host/auth.c new file +91
-0+ + memset(buf, 0, buf_size); + ret = nvme_auth_receive(ctrl, qid, buf, buf_size, chap->transaction, + NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE); + if (ret < 0) { + dev_dbg(ctrl->device, + "%s: qid %d DH-HMAC-CHAP failed to receive challenge\n", + __func__, qid); + goto out; + } + if (ret > 0) { + chap->status = ret; + goto fail1; + } + + /* DH-HMAC-CHAP Step 2: receive challenge */ + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP challenge\n", + __func__, qid); + + ret = nvme_auth_dhchap_challenge(ctrl, chap, buf, buf_size); + if (ret) { + /* Invalid parameters for negotiate */ + goto fail2; + } + + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP select hash\n", + __func__, qid); + ret = nvme_auth_select_hash(ctrl, chap); + if (ret) + goto fail2; + + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP host response\n", + __func__, qid); + ret = nvme_auth_dhchap_host_response(ctrl, chap); + if (ret) + goto fail2; + + /* DH-HMAC-CHAP Step 3: send reply */ + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP reply\n", + __func__, qid); + ret = nvme_auth_dhchap_reply(ctrl, chap, buf, buf_size); + if (ret < 0) + goto fail2; + + tl = ret; + ret = nvme_auth_send(ctrl, qid, buf, tl); + if (ret) + goto fail2; + + memset(buf, 0, buf_size); + ret = nvme_auth_receive(ctrl, qid, buf, buf_size, chap->transaction, + NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1); + if (ret < 0) { + dev_dbg(ctrl->device, + "%s: qid %d DH-HMAC-CHAP failed to receive success1\n", + __func__, qid); + goto out; + } + if (ret > 0) { + chap->status = ret; + goto fail1; + } + + if (ctrl->opts->dhchap_auth) { + dev_dbg(ctrl->device, + "%s: qid %d DH-HMAC-CHAP controller response\n", + __func__, qid); + ret = nvme_auth_dhchap_ctrl_response(ctrl, chap); + if (ret) + goto fail2; + } + + /* DH-HMAC-CHAP Step 4: receive success1 */ + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP success1\n", + __func__, qid); + ret = nvme_auth_dhchap_success1(ctrl, chap, buf, buf_size); + if (ret < 0) { + /* Controller authentication failed */ + goto fail2; + } + tl = ret; + /* DH-HMAC-CHAP Step 5: send success2 */ + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP success2\n", + __func__, qid); + tl = nvme_auth_dhchap_success2(ctrl, chap, buf, buf_size); + ret = nvme_auth_send(ctrl, qid, buf, tl); + if (!ret) + goto out; + +fail1: + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP failure1, status%x\n",
+__func__, qid,chap->status);
+ goto out;drivers/nvme/host/auth.c new file +3
-0+ +fail2: + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP failure2, status%x\n",
+__func__, qid,chap->status);
+ tl =nvme_auth_dhchap_failure2(ctrl, chap, buf, buf_size); + ret =nvme_auth_send(ctrl, qid, buf, tl);drivers/nvme/host/auth.c new file +11
-0+ +out: + if (!ret && chap->status) + ret = -EPROTO; + if (!ret) { + ctrl->dhchap_hash = chap->hash_id; + } + kfree(buf); + nvme_auth_free(chap); + return ret; +}drivers/nvme/host/auth.h new file +23
-0@@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions +*/ + +#ifndef _NVME_AUTH_H +#define _NVME_AUTH_H + +const char *nvme_auth_dhgroup_name (int dhgroup_id); +int nvme_auth_dhgroup_pubkey_size (int dhgroup_id); +int nvme_auth_dhgroup_privkey_size (int dhgroup_id); +const char *nvme_auth_dhgroup_kpp (int dhgroup_id); +int nvme_auth_dhgroup_id (const char *dhgroup_name); + +const char *nvme_auth_hmac_name (int hmac_id); +const char *nvme_auth_digest_name (int hmac_id); +int nvme_auth_hmac_id (const char *hmac_name); +int nvme_auth_hmac_len (int hmac_len); + +unsigned char *nvme_auth_extract_secret (unsigned char *dhchap_secret, + size_t *dhchap_key_len); + +#endif /* _NVME_AUTH_H */ drivers/nvme/host/core.c +0
-0@@ -708,7 +708,9 @@ bool __nvme_check_ready(struct nvme_ctrl *ctrl, struct request *rq, switch (ctrl->state) {case
NVME_CTRL_CONNECTING:
if (blk_rq_is_passthrough(rq)&&nvme_is_fabrics(req->cmd)&& -req->cmd->fabrics.fctype==nvme_fabrics_type_connect) + (req->cmd->fabrics.fctype==nvme_fabrics_type_connect||
+req->cmd->fabrics.fctype==nvme_fabrics_type_auth_send||
+req->cmd->fabrics.fctype==nvme_fabrics_type_auth_receive))
return true;
break;
default:drivers/nvme/host/core.c +64
-0@@ -3426,6 +3428,66 @@ static ssize_t nvme_ctrl_fast_io_fail_tmo_store(struct device *dev, static DEVICE_ATTR(fast_io_fail_tmo, S_IRUGO | S_IWUSR, nvme_ctrl_fast_io_fail_tmo_show, nvme_ctrl_fast_io_fail_tmo_store); +#ifdef CONFIG_NVME_AUTH +struct nvmet_dhchap_hash_map { + int id; + const char name[15]; +} hash_map[] = { + {.id = NVME_AUTH_DHCHAP_HASH_SHA256, + .name = "hmac(sha256)", }, + {.id = NVME_AUTH_DHCHAP_HASH_SHA384, + .name = "hmac(sha384)", }, + {.id = NVME_AUTH_DHCHAP_HASH_SHA512, + .name = "hmac(sha512)", }, +}; + +static ssize_t dhchap_hash_show (struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(hash_map); i++) { + if (hash_map[i].id == ctrl->dhchap_hash) + return sprintf(buf, "%s\n", hash_map[i].name); + } + return sprintf(buf, "none\n"); +} +DEVICE_ATTR_RO (dhchap_hash); + +struct nvmet_dhchap_group_map { + int id; + const char name[15]; +} dhgroup_map[] = { + {.id = NVME_AUTH_DHCHAP_DHGROUP_NULL, + .name = "NULL", }, + {.id = NVME_AUTH_DHCHAP_DHGROUP_2048, + .name = "ffdhe2048", }, + {.id = NVME_AUTH_DHCHAP_DHGROUP_3072, + .name = "ffdhe3072", }, + {.id = NVME_AUTH_DHCHAP_DHGROUP_4096, + .name = "ffdhe4096", }, + {.id = NVME_AUTH_DHCHAP_DHGROUP_6144, + .name = "ffdhe6144", }, + {.id = NVME_AUTH_DHCHAP_DHGROUP_8192, + .name = "ffdhe8192", }, +}; + +static ssize_t dhchap_dhgroup_show (struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) { + if (hash_map[i].id == ctrl->dhchap_dhgroup) + return sprintf(buf, "%s\n", dhgroup_map[i].name); + } + return sprintf(buf, "none\n"); +} +DEVICE_ATTR_RO (dhchap_dhgroup); +#endif + static struct attribute *nvme_dev_attrs[] = { &dev_attr_reset_controller.attr, &dev_attr_rescan_controller.attr,@@ -3447,6 +3509,10 @@ static struct attribute *nvme_dev_attrs[] = { &dev_attr_reconnect_delay.attr, &dev_attr_fast_io_fail_tmo.attr, &dev_attr_kato.attr, +#ifdef CONFIG_NVME_AUTH + &dev_attr_dhchap_hash.attr, + &dev_attr_dhchap_dhgroup.attr, +#endif NULL
};drivers/nvme/host/core.c +9
-0@@ -3470,6 +3536,10 @@ static umode_t nvme_dev_attrs_are_visible(struct kobject *kobj, return 0; if (a == &dev_attr_fast_io_fail_tmo.attr && !ctrl->opts) return 0; +#ifdef CONFIG_NVME_AUTH + if (a == &dev_attr_dhchap_hash.attr && !ctrl->opts) + return 0; +#endif return a->mode; }@@ -4581,6 +4651,11 @@ static inline void _nvme_check_size(void) BUILD_BUG_ON(sizeof(struct nvme_smart_log) != 512); BUILD_BUG_ON(sizeof(struct nvme_dbbuf) != 64); BUILD_BUG_ON(sizeof(struct nvme_directive_cmd) != 64); + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_negotiate_data) != 8); + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_challenge_data) != 16); + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_reply_data) != 16); + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_success1_data) != 16); + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_success2_data) != 16); }drivers/nvme/host/fabrics.c +6
-2@@ -366,6 +366,7 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) union nvme_result res; struct nvmf_connect_data *data; int ret; + u32 result; cmd.connect.opcode = nvme_fabrics_command; cmd.connect.fctype = nvme_fabrics_type_connect;@@ -398,8 +399,18 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) goto out_free_data; } - ctrl->cntlid = le16_to_cpu(res.u16); - + result = le32_to_cpu(res.u32); + ctrl->cntlid = result & 0xFFFF; + if ((result >> 16) & 2) { + /* Authentication required */ + ret = nvme_auth_negotiate(ctrl, NVME_QID_ANY);
Quoted 9 lines
drivers/nvme/host/fabrics.c int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) · line 412
Quoted 6 lines
drivers/nvme/host/fabrics.c +6
-0+ if (ret) + dev_warn(ctrl->device, + "qid 0: authentication failed\n"); + else + dev_info(ctrl->device, + "qid 0: authenticated\n");info is too chatty.
Hmm. I know I need to work on logging...
Quoted 33 lines
Quoted 33 lines
drivers/nvme/host/fabrics.c +1
-0+ }
out_free_data:
kfree(data);
return ret;drivers/nvme/host/fabrics.c +12
-0@@ -432,6 +443,7 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid) struct nvmf_connect_data *data; union nvme_result res; int ret; + u32 result; cmd.connect.opcode = nvme_fabrics_command; cmd.connect.fctype = nvme_fabrics_type_connect;@@ -457,6 +469,17 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid) nvmf_log_connect_error(ctrl, ret, le32_to_cpu(res.u32), &cmd, data); } + result = le32_to_cpu(res.u32); + if ((result >> 16) & 2) { + /* Authentication required */ + ret = nvme_auth_negotiate(ctrl, qid); + if (ret) + dev_warn(ctrl->device, + "qid %u: authentication failed\n", qid); + else + dev_info(ctrl->device, + "qid %u: authenticated\n", qid); + } kfree(data); return ret;}
drivers/nvme/host/fabrics.c +0
-0@@ -548,6 +571,9 @@ static const match_table_t opt_tokens = { { NVMF_OPT_NR_POLL_QUEUES, "nr_poll_queues=%d" },
Quoted 9 lines
drivers/nvme/host/fabrics.c static const match_table_t opt_tokens = { · line 576
Quoted 5 lines
drivers/nvme/host/fabrics.c +3
-0{ NVMF_OPT_TOS, "tos=%d" }, { NVMF_OPT_FAIL_FAST_TMO, "fast_io_fail_tmo=%d" }, + { NVMF_OPT_DHCHAP_SECRET, "dhchap_secret=%s" }, + { NVMF_OPT_DHCHAP_AUTH, "authenticate" }, + { NVMF_OPT_DHCHAP_GROUP, "dhchap_group=%s" },Isn't the group driven by the subsystem? also why is there a "authenticate" boolean? what is it good for?
Ah. Right. Of course, the 'group' is pointless here.
And the 'authenticate' bool is the abovementioned bidirectional authentication. I _do_ need to give it another name.
Quoted 97 lines
Quoted 97 lines
drivers/nvme/host/fabrics.c +29
-0{ NVMF_OPT_ERR, NULL } };@@ -824,6 +850,35 @@ static int nvmf_parse_options(structnvmf_ctrl_options *opts, } opts->tos = token; break; + case NVMF_OPT_DHCHAP_SECRET: + p = match_strdup(args); + if (!p) { + ret = -ENOMEM; + goto out; + } + if (strncmp(p, "DHHC-1:00:", 10)) { + pr_err("Invalid DH-CHAP secret %s\n", p); + ret = -EINVAL; + goto out; + } + kfree(opts->dhchap_secret); + opts->dhchap_secret = p; + break; + case NVMF_OPT_DHCHAP_AUTH: + opts->dhchap_auth = true; + break; + case NVMF_OPT_DHCHAP_GROUP: + if (match_int(args, &token)) { + ret = -EINVAL; + goto out; + } + if (token <= 0) { + pr_err("Invalid dhchap_group %d\n", token); + ret = -EINVAL; + goto out; + } + opts->dhchap_group = token; + break;default:
pr_warn("unknown parameter or missing value '%s' in ctrl creation request\n", p);drivers/nvme/host/fabrics.c +1
-0@@ -942,6 +997,7 @@ void nvmf_free_options(struct nvmf_ctrl_options *opts) kfree(opts->subsysnqn); kfree(opts->host_traddr); kfree(opts->host_iface); + kfree(opts->dhchap_secret); kfree(opts); }
EXPORT_SYMBOL_GPL(nvmf_free_options);drivers/nvme/host/fabrics.c +4
-1@@ -951,7 +1007,10 @@ EXPORT_SYMBOL_GPL(nvmf_free_options); NVMF_OPT_KATO | NVMF_OPT_HOSTNQN | \ NVMF_OPT_HOST_ID | NVMF_OPT_DUP_CONNECT |\ NVMF_OPT_DISABLE_SQFLOW |\ - NVMF_OPT_FAIL_FAST_TMO) + NVMF_OPT_CTRL_LOSS_TMO |\ + NVMF_OPT_FAIL_FAST_TMO |\ + NVMF_OPT_DHCHAP_SECRET |\ + NVMF_OPT_DHCHAP_AUTH | NVMF_OPT_DHCHAP_GROUP)static struct
nvme_ctrl*nvmf_create_ctrl(struct device *dev, const char *buf)drivers/nvme/host/fabrics.h +8
-0@@ -67,6 +67,9 @@ enum { NVMF_OPT_TOS = 1 << 19, NVMF_OPT_FAIL_FAST_TMO = 1 << 20, NVMF_OPT_HOST_IFACE = 1 << 21, + NVMF_OPT_DHCHAP_SECRET = 1 << 22, + NVMF_OPT_DHCHAP_AUTH = 1 << 23, + NVMF_OPT_DHCHAP_GROUP = 1 << 24, }; /**@@ -96,6 +99,8 @@ enum { * @max_reconnects: maximum number of allowed reconnect attempts before removing * the controller, (-1) means reconnect forever, zero means remove * immediately; + * @dhchap_secret: DH-HMAC-CHAP secret + * @dhchap_auth: DH-HMAC-CHAP authenticate controller * @disable_sqflow: disable controller sq flow control * @hdr_digest: generate/verify header digest (TCP) * @data_digest: generate/verify data digest (TCP)@@ -120,6 +125,9 @@ struct nvmf_ctrl_options { unsigned int kato; struct nvmf_host *host; int max_reconnects; + char *dhchap_secret; + int dhchap_group; + bool dhchap_auth; bool disable_sqflow; bool hdr_digest; bool data_digest;drivers/nvme/host/nvme.h +0
-0@@ -328,6 +328,12 @@ struct nvme_ctrl { struct work_struct ana_work;
Quoted 9 lines
drivers/nvme/host/nvme.h struct nvme_ctrl { · line 334
Quoted 5 lines
drivers/nvme/host/nvme.h +4
-0#endif +#ifdef CONFIG_NVME_AUTH + u16 transaction; + u8 dhchap_hash; + u8 dhchap_dhgroup;Do multiple controllers in the same subsystem have different params? no, so I think these should be lifted to subsys.
It doesn't actually say in the spec; it always refers to the params as being set by the controller. So it could be either; maybe we should ask for clafication at the fmds call.
Quoted 19 lines
Quoted 19 lines
drivers/nvme/host/nvme.h +11
-0+#endif + /* Power saving configuration */ u64 ps_max_latency_us; bool apst_enabled;@@ -874,6 +880,15 @@ static inline bool nvme_ctrl_sgl_supported(struct nvme_ctrl *ctrl) return ctrl->sgls & ((1 << 0) | (1 << 1)); } +#ifdef CONFIG_NVME_AUTH +int nvme_auth_negotiate (struct nvme_ctrl *ctrl,int qid); +#else +static inline int nvme_auth_negotiate (struct nvme_ctrl *ctrl,int qid) +{ + return -EPROTONOSUPPORT; +} +#endif + u32 nvme_command_effects(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
Quoted 9 lines
drivers/nvme/host/trace.c
Quoted 6 lines
drivers/nvme/host/nvme.h +0
-0u8 opcode); int nvme_execute_passthru_rq(struct request *rq);drivers/nvme/host/trace.c +0
-0I'd split out the tracing logic.
Okay.
Cheers,
Hannes
Signature · 4 lines
Dr. Hannes Reinecke Kernel Storage Architect [email protected] +49 911 74053 688 SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
Sagi Grimberg
maintainerRe: [PATCH 06/11] nvme: Implement In-Band authentication
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines
Implement NVMe-oF In-Band authentication. This patch adds two new fabric options '
dhchap_key' to specify the PSK
Quoted 8 lines
Quoted 6 lines
pre-shared-key.
Also, we need a sysfs knob to rotate the key that will trigger re-authentication or even a simple controller(s-plural) reset, so this should go beyond just the connection string.
Yeah, re-authentication currently is not implemented. I first wanted to get this patchset out such that we can settle on the userspace interface (both from host and target).
I think this is mandatory from the start (at the very least from the host side) because credentials get rotated.
The flow imo needs to be that the target allows to update the credentials even when the subsystem is exposed and connected, but keep all existing hosts connected such that only new connections will need the new credentials. (an incremental step, which would be optional is to allow it for a configurable grace period and then disconnect existing hosts, which would force new connections with updated credentials).
From the host side, we need to expose a controller (or more appropriately a subsystem) sysfs file to override the existing key. That can initially just trigger a controller reset, and incrementally can do a graceful re-authentication).
Quoted 2 lines
I'll have to think on how we should handle authentication; one of the really interesting cases would be when one malicious admin will _just_ send a 'negotiate' command to the controller. As per spec the controller will be waiting for an 'authentication receive' command to send a 'challenge' payload back to the host. But that will never come, so as it stands currently the controller is required to abort the connection. Not very nice.
Well, at the moment we can keep it open and discuss this in the TWG.
Quoted 3 lines
Quoted 2 lines
P.S. can you add also the nvme-cli code in the next go?Oh, sure. It's already sitting around in my local repo (surprise, surprise); will be ending it out next time.
Great, thanks.
Quoted 9 lines
Quoted 5 lines
Quoted 2 lines
and '
dhchap_authenticate'
to request bi-directional authentication of both the host and the controller.bidirectional? not uni-directional?
Yeah, that's a bit of a misnomer. When a PSK is specified, the controller will start the authentication protocol such that the _controller_ can validate the host. If the host wants to authenticate the controller is needs to set this flag. Hence bi-directional authentication.
But I'm the first to admit that this is poor wording for the flag.
It is misleading. But I'll need to go look again because I didn't see the host authenticating the controller..
Quoted 79 lines
Quoted 26 lines
Quoted 26 lines
Signed-off-by Hannes Reinecke <[email protected]>---
drivers/nvme/host/Kconfig | 11 +
drivers/nvme/host/Makefile | 1 +drivers/nvme/host/auth.c | 813 ++++++++++++++++++++++++++++++++++++ drivers/nvme/host/auth.h | 23 + drivers/nvme/host/core.c | 77 +++-
drivers/nvme/host/fabrics.c| 65 ++-
drivers/nvme/host/fabrics.h| 8 +
drivers/nvme/host/nvme.h | 15 +
drivers/nvme/host/trace.c | 32 ++9 files changed, 1041 insertions(+), 4 deletions(-)create mode 100644
drivers/nvme/host/auth.c
create mode 100644drivers/nvme/host/auth.hdrivers/nvme/host/Kconfig +0
-0@@ -85,3 +85,14 @@ config NVME_TCPfrom https://github.com/linux-nvme/nvme-cli.
If unsure, say N.drivers/nvme/host/Kconfig +4
-0+ +config NVME_AUTH + bool "NVM Express over Fabrics In-Band Authentication" + depends on NVME_TCPQuoted 9 lines
Quoted 6 lines
+ select
CRYPTO_SHA256
+ selectCRYPTO_SHA512
+ help
+ This provides support for NVMe over Fabrics In-Band Authentication
+ for the NVMe over TCP transport.In this form, nothing is specific to nvme-tcp here afaict.
Indeed. I guess we can leave out the nvme-tcp reference here.
Quoted 41 lines
Quoted 24 lines
drivers/nvme/host/Kconfig +2
-0+ + If unsure, say N.drivers/nvme/host/Makefile +0
-0@@ -20,6 +20,7 @@ nvme-core-$(CONFIG_NVME_HWMON) += hwmon.onvme-y += pci.o
nvme-fabrics-y += fabrics.o
+nvme-fabrics-$(CONFIG_NVME_AUTH) += auth.o
nvme-rdma-y += rdma.odrivers/nvme/host/auth.c new file +7
-0@@ -0,0 +1,813 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2020 Hannes Reinecke, SUSE Linux +*/ + +#include <linux/crc32.h> +#include <linux/base64.h> drivers/nvme/host/auth.c line 17
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+#include <asm/unaligned.h> +#include <crypto/hash.h> +#include <crypto/kpp.h> +#include "nvme.h" +#include "fabrics.h" +#include "auth.h" + +static u32 nvme_dhchap_seqnum; + +struct nvme_dhchap_context {Maybe
nvme_dhchap_queue_context?I'm thinking that we should perhaps split
it to host-wide, subsys-wide and queue specific
auth contexts?
Quoted 8 lines
Quoted 2 lines
Let's see...
Interestingly enough, that's what I did for the target side.
For the host side I found it easier that way, as then we'll have a single authentication context which can be deleted after authentication finished, and be sure that we removed _all_ information. Security and all that.
Splitting it off would require to remove information on three different places, and observing life-time rules for them.
So more of a chance to mess things up :-)
I understand what you are saying, but this way it will be challanging to cross check stuff. It is also where things sort of belong...
Quoted 114 lines
Quoted 16 lines
Quoted 16 lines
drivers/nvme/host/auth.c new file +16
-0+ struct crypto_shash *shash_tfm; + unsigned char *key; + size_t key_len; + int qid; + u32 s1; + u32 s2; + u16 transaction; + u8 status; + u8 hash_id; + u8 hash_len; + u8 c1[64]; + u8 c2[64]; + u8 response[64]; + u8 *ctrl_key; + int ctrl_key_len; + u8 *host_key;Quoted 9 lines
drivers/nvme/host/auth.c line 39
Quoted 6 lines
drivers/nvme/host/auth.c new file +6
-0+ int host_key_len; + u8 *sess_key; + int sess_key_len; +}; + +struct nvmet_dhchap_hash_map {nvmet?
Yeah; originally I coded that for the target side, and only later moved it into the host side to have it usable for both. Will be fixing it up.
Quoted 11 lines
Quoted 11 lines
drivers/nvme/host/auth.c new file +11
-0+ int id; + int hash_len; + const char hmac[15]; + const char digest[15]; +} hash_map[] = { + {.id = NVME_AUTH_DHCHAP_HASH_SHA256, + .hash_len = 32, + .hmac = "hmac(sha256)", .digest = "sha256" }, + {.id = NVME_AUTH_DHCHAP_HASH_SHA384, + .hash_len = 48, + .hmac = "hmac(sha384)", .digest = "sha384" },Quoted 9 lines
drivers/nvme/host/auth.c line 56
Quoted 6 lines
drivers/nvme/host/auth.c new file +6
-0+ {.id = NVME_AUTH_DHCHAP_HASH_SHA512, + .hash_len = 64, + .hmac = "hmac(sha512)", .digest = "sha512" }, +}; + +const char *nvme_auth_hmac_name (int hmac_id)Should these arrays be static?
Definitely.
Quoted 52 lines
Quoted 52 lines
drivers/nvme/host/auth.c new file +52
-0+{ + int i; + + for (i = 0; i < ARRAY_SIZE(hash_map); i++) { + if (hash_map[i].id == hmac_id) + return hash_map[i].hmac; + } + return NULL; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_name); + +const char *nvme_auth_digest_name (int hmac_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hash_map); i++) { + if (hash_map[i].id == hmac_id) + return hash_map[i].digest; + } + return NULL; +} +EXPORT_SYMBOL_GPL (nvme_auth_digest_name); + +int nvme_auth_hmac_len (int hmac_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hash_map); i++) { + if (hash_map[i].id == hmac_id) + return hash_map[i].hash_len; + } + return -1; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_len); + +int nvme_auth_hmac_id (const char *hmac_name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hash_map); i++) { + if (!strncmp(hash_map[i].hmac, hmac_name, + strlen(hash_map[i].hmac))) + return hash_map[i].id; + } + return -1; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_id); + +unsigned char *nvme_auth_extract_secret (unsigned char *dhchap_secret, + size_t *dhchap_key_len) +{ + unsigned char *dhchap_key;Quoted 9 lines
drivers/nvme/host/auth.c line 113
Quoted 5 lines
drivers/nvme/host/auth.c new file +5
-0+ u32 crc; + int key_len; + size_t allocated_len; + + allocated_len = strlen(dhchap_secret) - 10;the 10 feels like a magic here, should at least note this is the "DHHC-1:..." prefix.
Quoted 1 line
It _is_ magic. And it might even be better to just pass in the string _without_ the DHHC-1: prefix.
I don't think that the user should pass in a key in that form at all, its none of its concern the compliant NVMe representation is.
Quoted 17 lines
Quoted 17 lines
Quoted 16 lines
drivers/nvme/host/auth.c new file +15
-0+ dhchap_key = kzalloc(allocated_len, GFP_KERNEL); + if (!dhchap_key) + return ERR_PTR(-ENOMEM); + + key_len = base64_decode(dhchap_secret + 10, + allocated_len, dhchap_key); + if (key_len != 36 && key_len != 52 && + key_len != 68) { + pr_debug("Invalid DH-HMAC-CHAP key len %d\n", + key_len); + kfree(dhchap_key); + return ERR_PTR(-EINVAL); + } + pr_debug("DH-HMAC-CHAP Key: %*ph\n", + (int)key_len, dhchap_key);
Quoted 6 lines
Quoted 2 lines
One can argue if even printing this is problematic..
Debugging scaffolding. You wouldn't believe how many things can go wrong...
And yes, that should be removed.
Cool.
Quoted 142 lines
Quoted 36 lines
Quoted 36 lines
drivers/nvme/host/auth.c new file +36
-0+ + /* The last four bytes is the CRC in little-endian format */ + key_len -= 4; + /* + * The linux implementation doesn't do pre- and post-increments, + * so we have to do it manually. + */ + crc = ~crc32(~0, dhchap_key, key_len); + + if (get_unaligned_le32(dhchap_key + key_len) != crc) { + pr_debug("DH-HMAC-CHAP crc mismatch (key %08x, crc %08x)\n", + get_unaligned_le32(dhchap_key + key_len), crc); + kfree(dhchap_key); + return ERR_PTR(-EKEYREJECTED); + } + *dhchap_key_len = key_len; + return dhchap_key; +} +EXPORT_SYMBOL_GPL (nvme_auth_extract_secret); + +static int nvme_auth_send (struct nvme_ctrl *ctrl,int qid, + void *data, size_t tl) +{ + struct nvme_command cmd = {}; + blk_mq_req_flags_t flags = qid == NVME_QID_ANY ? + 0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED; + struct request_queue *q = qid == NVME_QID_ANY ? + ctrl->fabrics_q : ctrl->connect_q; + int ret; + + cmd.auth_send.opcode = nvme_fabrics_command; + cmd.auth_send.fctype = nvme_fabrics_type_auth_send; + cmd.auth_send.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER; + cmd.auth_send.spsp0 = 0x01; + cmd.auth_send.spsp1 = 0x01; + cmd.auth_send.tl = tl;Quoted 9 lines
drivers/nvme/host/auth.c line 170
Quoted 6 lines
drivers/nvme/host/auth.c new file +6
-0+ + ret = __nvme_submit_sync_cmd(q, &cmd, NULL, data, tl, 0, qid, + 0, flags); + if (ret) + dev_dbg(ctrl->device, + "%s: qid %d error %d\n", __func__, qid, ret);Maybe a little more informative print rather than
__func__?Yes, can do.
Quoted 54 lines
Quoted 54 lines
drivers/nvme/host/auth.c new file +54
-0+ return ret; +} + +static int nvme_auth_receive (struct nvme_ctrl *ctrl,int qid, + void *buf, size_t al, + u16 transaction, u8 expected_msg ) +{ + struct nvme_command cmd = {}; + struct nvmf_auth_dhchap_failure_data *data = buf; + blk_mq_req_flags_t flags = qid == NVME_QID_ANY ? + 0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED; + struct request_queue *q = qid == NVME_QID_ANY ? + ctrl->fabrics_q : ctrl->connect_q; + int ret; + + cmd.auth_receive.opcode = nvme_fabrics_command; + cmd.auth_receive.fctype = nvme_fabrics_type_auth_receive; + cmd.auth_receive.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER; + cmd.auth_receive.spsp0 = 0x01; + cmd.auth_receive.spsp1 = 0x01; + cmd.auth_receive.al = al; + + ret = __nvme_submit_sync_cmd(q, &cmd, NULL, buf, al, 0, qid, + 0, flags); + if (ret > 0) { + dev_dbg(ctrl->device, "%s: qid %d nvme status %x\n", + __func__, qid, ret); + ret = -EIO; + } + if (ret < 0) { + dev_dbg(ctrl->device, "%s: qid %d error %d\n", + __func__, qid, ret); + return ret; + } + dev_dbg(ctrl->device, "%s: qid %d auth_type %d auth_id %x\n", + __func__, qid, data->auth_type, data->auth_id); + if (data->auth_type == NVME_AUTH_COMMON_MESSAGES && + data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) { + return data->reason_code_explanation; + } + if (data->auth_type != NVME_AUTH_DHCHAP_MESSAGES || + data->auth_id != expected_msg) { + dev_warn(ctrl->device, + "qid %d invalid message %02x/%02x\n", + qid, data->auth_type, data->auth_id); + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD; + } + if (le16_to_cpu(data->t_id) != transaction) { + dev_warn(ctrl->device, + "qid %d invalid transaction ID %d\n", + qid, le16_to_cpu(data->t_id)); + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD; + } +Quoted 9 lines
drivers/nvme/host/auth.c line 230
Quoted 6 lines
drivers/nvme/host/auth.c new file +6
-0+ return 0; +} + +static int nvme_auth_dhchap_negotiate (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap, + void *buf, size_t buf_size)Maybe
nvme_auth_set_dhchap_negotiate_data?These are the individual steps in the state machine later on, so I wanted to keep the names identical. But I'm open to suggestions.
Quoted 18 lines
Quoted 18 lines
drivers/nvme/host/auth.c new file +17
-0+{ + struct nvmf_auth_dhchap_negotiate_data *data = buf; + size_t size = sizeof(*data) + sizeof(union nvmf_auth_protocol); + + if (buf_size < size) + return -EINVAL; + + memset((u8 *)buf, 0, size); + 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 */ + data->napd = 1; + data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID; + data->auth_protocol[0].dhchap.halen = 3; + data->auth_protocol[0].dhchap.dhlen = 1; + data->auth_protocol[0].dhchap.idlist[0] =
NVME_AUTH_DHCHAP_HASH_SHA256;Quoted 9 lines
Quoted 6 lines
+ data->auth_protocol[0].dhchap.idlist[1] =
NVME_AUTH_DHCHAP_HASH_SHA384;
+ data->auth_protocol[0].dhchap.idlist[2] =NVME_AUTH_DHCHAP_HASH_SHA512;
+ data->auth_protocol[0].dhchap.idlist[3] =NVME_AUTH_DHCHAP_DHGROUP_NULL;You should comment that this routine expects buf to have enough room for both negotiate and
auth_protostructures.
Quoted 2 lines
Hmm. I do a check for the overall size at the start, so I'm not sure what this will buy us. And actually, anyone wanting to make sense of the implementation would need to look at the spec anyway.
Unrelated to the spec, just makes the code more readable. There is an assumption on the buffer size passed, so it would be nice to document it in the code (so it is less likely to break in the future in the presence of assumptions).
Quoted 103 lines
Quoted 10 lines
drivers/nvme/host/auth.c line 257
Quoted 7 lines
drivers/nvme/host/auth.c new file +7
-0+ + return size; +} + +static int nvme_auth_dhchap_challenge (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap, + void *buf, size_t buf_size)Maybe
nvme_auth_process_dhchap_challange?See above. I'd rather have consistent names for the state machine. But I can change them to 'nvme_process_chchap_<statename>'
Quoted 28 lines
Quoted 28 lines
drivers/nvme/host/auth.c new file +28
-0+{ + struct nvmf_auth_dhchap_challenge_data *data = buf; + size_t size = sizeof(*data) + data->hl + data->dhvlen; + const char *gid_name; + + if (buf_size < size) { + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD; + return -ENOMSG; + } + + if (data->hashid != NVME_AUTH_DHCHAP_HASH_SHA256 && + data->hashid != NVME_AUTH_DHCHAP_HASH_SHA384 && + data->hashid != NVME_AUTH_DHCHAP_HASH_SHA512) { + dev_warn(ctrl->device, + "qid %d: DH-HMAC-CHAP: invalid HASH ID %d\n", + chap->qid, data->hashid); + chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; + return -EPROTO; + } + switch (data->dhgid) { + case NVME_AUTH_DHCHAP_DHGROUP_NULL: + gid_name = "null"; + break; + default: + gid_name = NULL; + break; + } + if (!gid_name) {Quoted 9 lines
Quoted 6 lines
+ dev_warn(ctrl->device,
+ "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n", +chap->qid,data->dhgid);
+chap->status=NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; + return-EPROTO;
+ }Maybe some spaces between condition blocks?
Ok.
Quoted 26 lines
Quoted 26 lines
+ if (
data->dhgid!=NVME_AUTH_DHCHAP_DHGROUP_NULL) {
+chap->status=NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; + return-EPROTO;
+ }
+ if (data->dhgid==NVME_AUTH_DHCHAP_DHGROUP_NULL&&data->dhvlen!= 0) {
+ dev_warn(ctrl->device,
+ "qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n", +chap->qid);
+chap->status=NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; + return-EPROTO;
+ }
+ dev_dbg(ctrl->device, "%s: qid %d requested hash id %d\n", +__func__,chap->qid,data->hashid);
+ if (nvme_auth_hmac_len(data->hashid)!=data->hl) {
+ dev_warn(ctrl->device,
+ "qid %d: DH-HMAC-CHAP: invalid hash length\n", +chap->qid);
+chap->status=NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; + return-EPROTO;
+ }
+chap->hash_id=data->hashid;
+chap->hash_len=data->hl;
+chap->s1=le32_to_cpu(data->seqnum);
+memcpy(chap->c1, data->cval, chap->hash_len);
+Quoted 9 lines
drivers/nvme/host/auth.c line 322
Quoted 6 lines
+ return 0;
drivers/nvme/host/auth.c new file +5
-0+} + +static int nvme_auth_dhchap_reply (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap, + void *buf, size_t buf_size)
nvme_auth_set_dhchap_replyAh. Now I see what you're getting at.
Okay, will be changing it.Quoted 10 lines
drivers/nvme/host/auth.c line 328
Quoted 6 lines
drivers/nvme/host/auth.c new file +6
-0+{ + struct nvmf_auth_dhchap_reply_data *data = buf; + size_t size = sizeof(*data); + + size += 2 * chap->hash_len; + if (ctrl->opts->dhchap_auth) {The ctrl opts is not clear to me. what is
dhchap_authmean?
Quoted 4 lines
As stated above, this is for bi-directional authentication. And yes, it is poor wording.
'
dhchap_bidirectional' ?
dhchap_auth_ctrl maybe.
Quoted 5 lines
Quoted 2 lines
Also shouldn't these params be lifted to the subsys?
I kinda like to have it all encapsulated in a common per-queue structure; on the host side this one isn't even attached to anything, so any new authentication attempt will allocate a new one, with no chance of accidentally re-using existing values. I thought this to be a rather nice property for a state-machine.
I understand, but different controllers for a single subsystem should not behave differently. Meaning for one you authenticate and the other you don't (or with different keys).
Quoted 323 lines
Quoted 32 lines
Quoted 32 lines
+
get_random_bytes(chap->c2, chap->hash_len); +chap->s2= nvme_dhchap_seqnum++;
+ } else
+memset(chap->c2, 0, chap->hash_len);drivers/nvme/host/auth.c new file +27
-0+ + if (chap->host_key_len) + size += chap->host_key_len; + + if (buf_size < size) + return -EINVAL; + + memset(buf, 0, size); + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES; + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_REPLY; + data->t_id = cpu_to_le16(chap->transaction); + data->hl = chap->hash_len; + data->dhvlen = chap->host_key_len; + data->seqnum = cpu_to_le32(chap->s2); + memcpy(data->rval, chap->response, chap->hash_len); + if (ctrl->opts->dhchap_auth) { + dev_dbg(ctrl->device, "%s: qid %d ctrl challenge %*ph\n", + __func__, chap->qid, + chap->hash_len, chap->c2); + data->cvalid = 1; + memcpy(data->rval + chap->hash_len, chap->c2, + chap->hash_len); + } + if (chap->host_key_len) + memcpy(data->rval + 2 * chap->hash_len, chap->host_key, + chap->host_key_len); +Quoted 9 lines
drivers/nvme/host/auth.c line 365
Quoted 6 lines
+ return size;
drivers/nvme/host/auth.c new file +5
-0+} + +static int nvme_auth_dhchap_success1 (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap, + void *buf, size_t buf_size)
nvme_auth_process_dhchap_success1OK.
Quoted 78 lines
Quoted 41 lines
drivers/nvme/host/auth.c new file +32
-0+{ + struct nvmf_auth_dhchap_success1_data *data = buf; + size_t size = sizeof(*data); + + if (ctrl->opts->dhchap_auth) + size += chap->hash_len; + + + if (buf_size < size) { + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD; + return -ENOMSG; + } + + if (data->hl != chap->hash_len) { + dev_warn(ctrl->device, + "qid %d: DH-HMAC-CHAP: invalid hash length %d\n", + chap->qid, data->hl); + chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; + return -EPROTO; + } + + if (!data->rvalid) + return 0; + + /* Validate controller response */ + if (memcmp(chap->response, data->rval, data->hl)) { + dev_dbg(ctrl->device, "%s: qid %d ctrl response %*ph\n", + __func__, chap->qid, chap->hash_len, data->rval); + dev_dbg(ctrl->device, "%s: qid %d host response %*ph\n", + __func__, chap->qid, chap->hash_len, chap->response); + dev_warn(ctrl->device, + "qid %d: DH-HMAC-CHAP: controller authenticationfailed\n",
+chap->qid);
+chap->status=NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD; + return-EPROTO;
+ }
+ dev_info(ctrl->device,
+ "qid %d: DH-HMAC-CHAP: controller authenticated\n", +chap->qid);
+ return 0;drivers/nvme/host/auth.c line 410
Quoted 5 lines
drivers/nvme/host/auth.c new file +5
-0+} + +static int nvme_auth_dhchap_success2 (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap, + void *buf, size_t buf_size)same
Quoted 5 lines
drivers/nvme/host/auth.c new file +5
-0+{ + struct nvmf_auth_dhchap_success2_data *data = buf; + size_t size = sizeof(*data); + + memset(buf, 0, size);drivers/nvme/host/auth.c line 425
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ data->auth_type = NVME_AUTH_DHCHAP_MESSAGES; + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2; + data->t_id = cpu_to_le16(chap->transaction); + + return size; +} + +static int nvme_auth_dhchap_failure2 (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap, + void *buf, size_t buf_size)same
Quoted 11 lines
drivers/nvme/host/auth.c new file +11
-0+{ + struct nvmf_auth_dhchap_failure_data *data = buf; + size_t size = sizeof(*data); + + memset(buf, 0, size); + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES; + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2; + data->t_id = cpu_to_le16(chap->transaction); + data->reason_code = 1; + data->reason_code_explanation = chap->status; +Quoted 9 lines
drivers/nvme/host/auth.c line 441
Quoted 5 lines
+ return size;
drivers/nvme/host/auth.c new file +4
-0+} + +int nvme_auth_select_hash (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap)Maybe
_select_hf(hash function)? not a must, just sticks to the spec language.Hmm. Will be checking.
Quoted 28 lines
Quoted 28 lines
drivers/nvme/host/auth.c new file +28
-0+{ + char *hash_name; + int ret; + + switch (chap->hash_id) { + case NVME_AUTH_DHCHAP_HASH_SHA256: + hash_name = "hmac(sha256)"; + break; + case NVME_AUTH_DHCHAP_HASH_SHA384: + hash_name = "hmac(sha384)"; + break; + case NVME_AUTH_DHCHAP_HASH_SHA512: + hash_name = "hmac(sha512)"; + break; + default: + hash_name = NULL; + break; + } + if (!hash_name) { + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE; + return -EPROTO; + } + chap->shash_tfm = crypto_alloc_shash(hash_name, 0, + CRYPTO_ALG_ALLOCATES_MEMORY); + if (IS_ERR(chap->shash_tfm)) { + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE; + chap->shash_tfm = NULL; + return -EPROTO;Quoted 9 lines
Quoted 6 lines
+ }
+ if (!chap->key) {
+dev_warn(ctrl->device, "qid %d: cannot select hash, no key\n", + chap->qid);
+chap->status=NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE; +crypto_free_shash(chap->shash_tfm);Wouldn't it better to check this before allocating the tfm?
Indeed. Will be changing it.
Quoted 41 lines
Quoted 41 lines
+
chap->shash_tfm= NULL;
+ return-EINVAL;
+ }
+ ret =crypto_shash_setkey(chap->shash_tfm, chap->key, chap->key_len);
+ if (ret) {
+chap->status=NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE; +crypto_free_shash(chap->shash_tfm);
+chap->shash_tfm= NULL;
+ return ret;
+ }
+dev_info(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n", + chap->qid, hash_name);
+ return 0;drivers/nvme/host/auth.c new file +10
-0+} + +static int nvme_auth_dhchap_host_response (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap) +{ + SHASH_DESC_ON_STACK(shash, chap->shash_tfm); + u8 buf[4], *challenge = chap->c1; + int ret; + + dev_dbg(ctrl->device, "%s: qid %d host response seq %dtransaction %d\n",
+__func__,chap->qid,chap->s1,chap->transaction); +shash->tfm=chap->shash_tfm;
+ ret =crypto_shash_init(shash);
+ if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, challenge, chap->hash_len); + if (ret)
+ goto out;
+put_unaligned_le32(chap->s1, buf);
+ ret =crypto_shash_update(shash, buf, 4);
+ if (ret)
+ goto out;
+put_unaligned_le16(chap->transaction, buf);
+ ret =crypto_shash_update(shash, buf, 2);
+ if (ret)
+ goto out;Quoted 9 lines
Quoted 5 lines
+
memset(buf, 0, sizeof(buf));
+ ret =crypto_shash_update(shash, buf, 1); + if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, "HostHost", 8);HostHost ? Can you refer me to the specific section that talks about this?
NVMe 2.0 section DH-HMAC-CHAP_Reply Message, paragraph Response Value. HostHost.
Quoted 3 lines
Would be good to have a comment on the format fed to the shash.
Yes, will be doing so.
Quoted 79 lines
Quoted 79 lines
+ if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, ctrl->opts->host->nqn, + strlen(ctrl->opts->host->nqn)); + if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, buf, 1);
+ if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, ctrl->opts->subsysnqn, + strlen(ctrl->opts->subsysnqn));
+ if (ret)
+ goto out;
+ ret =crypto_shash_final(shash, chap->response);drivers/nvme/host/auth.c new file +12
-0+out: + return ret; +} + +static int nvme_auth_dhchap_ctrl_response (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap) +{ + SHASH_DESC_ON_STACK(shash, chap->shash_tfm); + u8 buf[4], *challenge = chap->c2; + int ret; + + dev_dbg(ctrl->device, "%s: qid %d host response seq %dtransaction %d\n",
+__func__,chap->qid,chap->s2,chap->transaction); + dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n", +__func__,chap->qid,chap->hash_len, challenge); + dev_dbg(ctrl->device, "%s: qid %d subsysnqn %s\n", +__func__,chap->qid,ctrl->opts->subsysnqn);
+ dev_dbg(ctrl->device, "%s: qid %d hostnqn %s\n",
+__func__,chap->qid,ctrl->opts->host->nqn);
+shash->tfm=chap->shash_tfm;
+ ret =crypto_shash_init(shash);
+ if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, challenge, chap->hash_len); + if (ret)
+ goto out;
+put_unaligned_le32(chap->s2, buf);
+ ret =crypto_shash_update(shash, buf, 4);
+ if (ret)
+ goto out;
+put_unaligned_le16(chap->transaction, buf);
+ ret =crypto_shash_update(shash, buf, 2);
+ if (ret)
+ goto out;
+memset(buf, 0, 4);
+ ret =crypto_shash_update(shash, buf, 1);
+ if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, "Controller", 10); + if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, ctrl->opts->subsysnqn, + strlen(ctrl->opts->subsysnqn));
+ if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, buf, 1);
+ if (ret)
+ goto out;
+ ret =crypto_shash_update(shash, ctrl->opts->host->nqn, + strlen(ctrl->opts->host->nqn));
+ if (ret)
+ goto out;
+ ret =crypto_shash_final(shash, chap->response);drivers/nvme/host/auth.c new file +11
-0+out: + return ret; +} + +int nvme_auth_generate_key (struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap) +{ + int ret; + u8 key_hash; + const char *hmac_name; + struct crypto_shash *key_tfm;Quoted 9 lines
Quoted 4 lines
+
+ if (sscanf(ctrl->opts->dhchap_secret, "DHHC-1:%hhd:%*s:", + &key_hash)!= 1)
+ return-EINVAL;I'd expect that the user will pass in a secret key (as binary) the the driver will build the spec compliant formatted string no? > Am I not reading this correctly?
Quoted 7 lines
I'm under the impression that this is the format into which the User/Admin will get hold of the secret key. Spec says:
'... all NVMe over Fabrics entities shall support the following ASCII representation of secrets ...'
And as the userspace interface is the only way how the user/admin _could_ interact with the NVMe over Fabrics entities in Linux I guess we'll need to be able to parse it.
Right... But who is responsible for crcing and encoding it? nvme-cli?
Quoted 2 lines
We sure could allow a binary secret, too, but then what would be the point in converting it into the secret representation? The protocol revolves around the binary secret, not the transport representation.
I am not sure I understand who is responsible for represnting the key this way in Linux?
Quoted 21 lines
Quoted 21 lines
Quoted 7 lines
drivers/nvme/host/auth.c new file +7
-0+ + chap->key = nvme_auth_extract_secret(ctrl->opts->dhchap_secret, + &chap->key_len); + if (IS_ERR(chap->key)) { + ret = PTR_ERR(chap->key); + chap->key = NULL; + return ret;drivers/nvme/host/auth.c line 618
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ } + + if (key_hash == 0) + return 0; + + hmac_name = nvme_auth_hmac_name(key_hash); + if (!hmac_name) { + pr_debug("Invalid key hash id %d\n", key_hash); + return -EKEYREJECTED; + }Why does the user influence the hmac used? isn't that is driven by the susbsystem?
Quoted 7 lines
Quoted 2 lines
I don't think that the user should choose in this level.
That is another weirdness of the spec.
The _secret_ will be hashed with a specific function, and that function is stated in the transport representation. (Cf section "DH-HMAC-CHAP Security Requirements").
This is _not_ the hash function used by the authentication itself, which will be selected by the protocol.
Yes, I see it now, and it is indeed confusing.
Quoted 1 line
So it's not the user here, but rather the transport specification of the key which selects the hash algorithm.
What do you mean by the transport specification?
Quoted 32 lines
Quoted 10 lines
Quoted 6 lines
+
+key_tfm=crypto_alloc_shash(hmac_name, 0, 0); + if (IS_ERR(key_tfm)) {
+kfree(chap->key);
+chap->key= NULL;
+ ret =PTR_ERR(key_tfm);You set ret and later return 0? I think that the success path in the else clause is hard to read and error prone...
Do I? Will need to fix it up.
Quoted 19 lines
Quoted 18 lines
+ } else {
+SHASH_DESC_ON_STACK(shash, key_tfm);
+
+shash->tfm=key_tfm;
+ ret =crypto_shash_setkey(key_tfm, chap->key, + chap->key_len);
+ if (ret < 0) {
+crypto_free_shash(key_tfm);
+kfree(chap->key);
+chap->key= NULL;
+ return ret;
+ }
+crypto_shash_init(shash);
+ crypto_shash_update(shash,ctrl->opts->host->nqn, +strlen(ctrl->opts->host->nqn)); +crypto_shash_update(shash, "NVMe-over-Fabrics", 17); +crypto_shash_final(shash, chap->key);
+crypto_free_shash(key_tfm);
Quoted 6 lines
Quoted 2 lines
Shouldn't these be done when preparing the dh-hmac-chap reply?
By setting the hash here I avoid having to pass the required hash function for the secret transformation.
I could be doing the entire secret transformation thingie when preparing the reply; reason why I did it here is that _having_ a secret is the precondition to everything else, so I wanted to check upfront for that. But I'll check what would happen if I move it.
Now that I understand that this is not the authentication transformation its ok I guess. Please add a comment in the code so its clearer.
Quoted 62 lines
Quoted 10 lines
Quoted 10 lines
+ }
+ return 0;drivers/nvme/host/auth.c new file +8
-0+} + +void nvme_auth_free (struct nvme_dhchap_context *chap) +{ + if (chap->shash_tfm) + crypto_free_shash(chap->shash_tfm); + if (chap->key) + kfree(chap->key);Quoted 9 lines
Quoted 6 lines
+ if (
chap->ctrl_key)
+kfree(chap->ctrl_key);
+ if (chap->host_key)
+kfree(chap->host_key);
+ if (chap->sess_key)
+kfree(chap->sess_key);No need to check null for kfree...
Will be fixing it up.
Quoted 30 lines
Quoted 30 lines
+ kfree(chap);
drivers/nvme/host/auth.c new file +29
-0+} + +int nvme_auth_negotiate (struct nvme_ctrl *ctrl,int qid) +{ + struct nvme_dhchap_context *chap; + void *buf; + size_t buf_size, tl; + int ret = 0; + + chap = kzalloc(sizeof(*chap), GFP_KERNEL); + if (!chap) + return -ENOMEM; + chap->qid = qid; + chap->transaction = ctrl->transaction++; + + ret = nvme_auth_generate_key(ctrl, chap); + if (ret) { + dev_dbg(ctrl->device, "%s: failed to generate key, error %d\n", + __func__, ret); + nvme_auth_free(chap); + return ret; + } + + /* + * Allocate a large enough buffer for the entire negotiation: + * 4k should be enough to ffdhe8192. + */ + buf_size = 4096; + buf = kzalloc(buf_size, GFP_KERNEL);Quoted 9 lines
Quoted 6 lines
+ if (!buf) {
+ ret =-ENOMEM;
+ goto out;
+ }
+
+ /* DH-HMAC-CHAP Step 1: send negotiate */I'd consider breaking these into sub-routines.
Quoted 1 line
Which ones? The preparation step?
I'm thinking:
1. nvme_auth_initiate_negotiation
- nvme_auth_set_dhchap_negotiate_data
- nvme_auth_send
2. nvme_auth_do_challange
- nvme_auth_receive
- nvme_auth_process_dhchap_challange
- nvme_auth_select_hash
- nvme_auth_dhchap_host_response
- nvme_auth_set_dhchap_reply
- nvme_auth_send
- nvme_auth_receive
- nvme_auth_process_dhchap_success1
3. if (ctrl->opts->dhchap_auth_ctrl)
- nvme_auth_dhchap_authenticate_ctrl
(e.g. nvme_auth_dhchap_ctrl_response)
4. nvme_auth_acknowledge_transaction
- nvme_auth_set_dhchap_success2
- nvme_auth_send
if steps 1,2,3 failed, goto target will have a func:
5. nvme_auth_fail_transaction
- nvme_auth_set_dhchap_failure2
- nvme_auth_send
Quoted 458 lines
Sure, can do.
Quoted 291 lines
Quoted 291 lines
+
dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP negotiate\n", + __func__, qid);
+ ret =nvme_auth_dhchap_negotiate(ctrl, chap, buf, buf_size); + if (ret < 0)
+ goto out;
+ tl = ret;
+ ret =nvme_auth_send(ctrl, qid, buf, tl);
+ if (ret)
+ goto out;
+
+memset(buf, 0, buf_size);
+ ret = nvme_auth_receive(ctrl, qid, buf,buf_size,chap->transaction,
+NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE);
+ if (ret < 0) {
+ dev_dbg(ctrl->device,
+ "%s: qid %d DH-HMAC-CHAP failed to receive challenge\n", +__func__, qid);
+ goto out;
+ }
+ if (ret > 0) {
+chap->status= ret;
+ goto fail1;
+ }drivers/nvme/host/auth.c new file +37
-0+ + /* DH-HMAC-CHAP Step 2: receive challenge */ + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP challenge\n", + __func__, qid); + + ret = nvme_auth_dhchap_challenge(ctrl, chap, buf, buf_size); + if (ret) { + /* Invalid parameters for negotiate */ + goto fail2; + } + + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP select hash\n", + __func__, qid); + ret = nvme_auth_select_hash(ctrl, chap); + if (ret) + goto fail2; + + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP host response\n", + __func__, qid); + ret = nvme_auth_dhchap_host_response(ctrl, chap); + if (ret) + goto fail2; + + /* DH-HMAC-CHAP Step 3: send reply */ + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP reply\n", + __func__, qid); + ret = nvme_auth_dhchap_reply(ctrl, chap, buf, buf_size); + if (ret < 0) + goto fail2; + + tl = ret; + ret = nvme_auth_send(ctrl, qid, buf, tl); + if (ret) + goto fail2; + + memset(buf, 0, buf_size); + ret = nvme_auth_receive(ctrl, qid, buf, buf_size,
chap->transaction,
+NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1);
+ if (ret < 0) {
+ dev_dbg(ctrl->device,
+ "%s: qid %d DH-HMAC-CHAP failed to receive success1\n", +__func__, qid);
+ goto out;
+ }
+ if (ret > 0) {
+chap->status= ret;
+ goto fail1;
+ }drivers/nvme/host/auth.c new file +29
-0+ + if (ctrl->opts->dhchap_auth) { + dev_dbg(ctrl->device, + "%s: qid %d DH-HMAC-CHAP controller response\n", + __func__, qid); + ret = nvme_auth_dhchap_ctrl_response(ctrl, chap); + if (ret) + goto fail2; + } + + /* DH-HMAC-CHAP Step 4: receive success1 */ + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP success1\n", + __func__, qid); + ret = nvme_auth_dhchap_success1(ctrl, chap, buf, buf_size); + if (ret < 0) { + /* Controller authentication failed */ + goto fail2; + } + tl = ret; + /* DH-HMAC-CHAP Step 5: send success2 */ + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP success2\n", + __func__, qid); + tl = nvme_auth_dhchap_success2(ctrl, chap, buf, buf_size); + ret = nvme_auth_send(ctrl, qid, buf, tl); + if (!ret) + goto out; + +fail1: + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP failure1, status%x\n",
+__func__, qid,chap->status);
+ goto out;drivers/nvme/host/auth.c new file +3
-0+ +fail2: + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP failure2, status%x\n",
+__func__, qid,chap->status);
+ tl =nvme_auth_dhchap_failure2(ctrl, chap, buf, buf_size); + ret =nvme_auth_send(ctrl, qid, buf, tl);drivers/nvme/host/auth.c new file +11
-0+ +out: + if (!ret && chap->status) + ret = -EPROTO; + if (!ret) { + ctrl->dhchap_hash = chap->hash_id; + } + kfree(buf); + nvme_auth_free(chap); + return ret; +}drivers/nvme/host/auth.h new file +23
-0@@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions +*/ + +#ifndef _NVME_AUTH_H +#define _NVME_AUTH_H + +const char *nvme_auth_dhgroup_name (int dhgroup_id); +int nvme_auth_dhgroup_pubkey_size (int dhgroup_id); +int nvme_auth_dhgroup_privkey_size (int dhgroup_id); +const char *nvme_auth_dhgroup_kpp (int dhgroup_id); +int nvme_auth_dhgroup_id (const char *dhgroup_name); + +const char *nvme_auth_hmac_name (int hmac_id); +const char *nvme_auth_digest_name (int hmac_id); +int nvme_auth_hmac_id (const char *hmac_name); +int nvme_auth_hmac_len (int hmac_len); + +unsigned char *nvme_auth_extract_secret (unsigned char *dhchap_secret, + size_t *dhchap_key_len); + +#endif /* _NVME_AUTH_H */ drivers/nvme/host/core.c +0
-0@@ -708,7 +708,9 @@ bool __nvme_check_ready(struct nvme_ctrl *ctrl, struct request *rq, switch (ctrl->state) {case
NVME_CTRL_CONNECTING:
if (blk_rq_is_passthrough(rq)&&nvme_is_fabrics(req->cmd)&& -req->cmd->fabrics.fctype==nvme_fabrics_type_connect) + (req->cmd->fabrics.fctype==nvme_fabrics_type_connect||
+req->cmd->fabrics.fctype==nvme_fabrics_type_auth_send||
+req->cmd->fabrics.fctype==nvme_fabrics_type_auth_receive))
return true;
break;
default:drivers/nvme/host/core.c +64
-0@@ -3426,6 +3428,66 @@ static ssize_t nvme_ctrl_fast_io_fail_tmo_store(struct device *dev, static DEVICE_ATTR(fast_io_fail_tmo, S_IRUGO | S_IWUSR, nvme_ctrl_fast_io_fail_tmo_show, nvme_ctrl_fast_io_fail_tmo_store); +#ifdef CONFIG_NVME_AUTH +struct nvmet_dhchap_hash_map { + int id; + const char name[15]; +} hash_map[] = { + {.id = NVME_AUTH_DHCHAP_HASH_SHA256, + .name = "hmac(sha256)", }, + {.id = NVME_AUTH_DHCHAP_HASH_SHA384, + .name = "hmac(sha384)", }, + {.id = NVME_AUTH_DHCHAP_HASH_SHA512, + .name = "hmac(sha512)", }, +}; + +static ssize_t dhchap_hash_show (struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(hash_map); i++) { + if (hash_map[i].id == ctrl->dhchap_hash) + return sprintf(buf, "%s\n", hash_map[i].name); + } + return sprintf(buf, "none\n"); +} +DEVICE_ATTR_RO (dhchap_hash); + +struct nvmet_dhchap_group_map { + int id; + const char name[15]; +} dhgroup_map[] = { + {.id = NVME_AUTH_DHCHAP_DHGROUP_NULL, + .name = "NULL", }, + {.id = NVME_AUTH_DHCHAP_DHGROUP_2048, + .name = "ffdhe2048", }, + {.id = NVME_AUTH_DHCHAP_DHGROUP_3072, + .name = "ffdhe3072", }, + {.id = NVME_AUTH_DHCHAP_DHGROUP_4096, + .name = "ffdhe4096", }, + {.id = NVME_AUTH_DHCHAP_DHGROUP_6144, + .name = "ffdhe6144", }, + {.id = NVME_AUTH_DHCHAP_DHGROUP_8192, + .name = "ffdhe8192", }, +}; + +static ssize_t dhchap_dhgroup_show (struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) { + if (hash_map[i].id == ctrl->dhchap_dhgroup) + return sprintf(buf, "%s\n", dhgroup_map[i].name); + } + return sprintf(buf, "none\n"); +} +DEVICE_ATTR_RO (dhchap_dhgroup); +#endif + static struct attribute *nvme_dev_attrs[] = { &dev_attr_reset_controller.attr, &dev_attr_rescan_controller.attr,@@ -3447,6 +3509,10 @@ static struct attribute *nvme_dev_attrs[] = { &dev_attr_reconnect_delay.attr, &dev_attr_fast_io_fail_tmo.attr, &dev_attr_kato.attr, +#ifdef CONFIG_NVME_AUTH + &dev_attr_dhchap_hash.attr, + &dev_attr_dhchap_dhgroup.attr, +#endif NULL
};drivers/nvme/host/core.c +9
-0@@ -3470,6 +3536,10 @@ static umode_t nvme_dev_attrs_are_visible(struct kobject *kobj, return 0; if (a == &dev_attr_fast_io_fail_tmo.attr && !ctrl->opts) return 0; +#ifdef CONFIG_NVME_AUTH + if (a == &dev_attr_dhchap_hash.attr && !ctrl->opts) + return 0; +#endif return a->mode; }@@ -4581,6 +4651,11 @@ static inline void _nvme_check_size(void) BUILD_BUG_ON(sizeof(struct nvme_smart_log) != 512); BUILD_BUG_ON(sizeof(struct nvme_dbbuf) != 64); BUILD_BUG_ON(sizeof(struct nvme_directive_cmd) != 64); + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_negotiate_data) != 8); + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_challenge_data) != 16); + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_reply_data) != 16); + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_success1_data) != 16); + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_success2_data) != 16); }drivers/nvme/host/fabrics.c +6
-2@@ -366,6 +366,7 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) union nvme_result res; struct nvmf_connect_data *data; int ret; + u32 result; cmd.connect.opcode = nvme_fabrics_command; cmd.connect.fctype = nvme_fabrics_type_connect;@@ -398,8 +399,18 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) goto out_free_data; } - ctrl->cntlid = le16_to_cpu(res.u16); - + result = le32_to_cpu(res.u32); + ctrl->cntlid = result & 0xFFFF; + if ((result >> 16) & 2) { + /* Authentication required */ + ret = nvme_auth_negotiate(ctrl, NVME_QID_ANY);Quoted 9 lines
drivers/nvme/host/fabrics.c int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) · line 412
Quoted 6 lines
drivers/nvme/host/fabrics.c +6
-0+ if (ret) + dev_warn(ctrl->device, + "qid 0: authentication failed\n"); + else + dev_info(ctrl->device, + "qid 0: authenticated\n");info is too chatty.
Hmm. I know I need to work on logging...
Quoted 33 lines
Quoted 33 lines
drivers/nvme/host/fabrics.c +1
-0+ }
out_free_data:
kfree(data);
return ret;drivers/nvme/host/fabrics.c +12
-0@@ -432,6 +443,7 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid) struct nvmf_connect_data *data; union nvme_result res; int ret; + u32 result; cmd.connect.opcode = nvme_fabrics_command; cmd.connect.fctype = nvme_fabrics_type_connect;@@ -457,6 +469,17 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid) nvmf_log_connect_error(ctrl, ret, le32_to_cpu(res.u32), &cmd, data); } + result = le32_to_cpu(res.u32); + if ((result >> 16) & 2) { + /* Authentication required */ + ret = nvme_auth_negotiate(ctrl, qid); + if (ret) + dev_warn(ctrl->device, + "qid %u: authentication failed\n", qid); + else + dev_info(ctrl->device, + "qid %u: authenticated\n", qid); + } kfree(data); return ret;}
drivers/nvme/host/fabrics.c +0
-0@@ -548,6 +571,9 @@ static const match_table_t opt_tokens = { { NVMF_OPT_NR_POLL_QUEUES, "nr_poll_queues=%d" },Quoted 9 lines
drivers/nvme/host/fabrics.c static const match_table_t opt_tokens = { · line 576
Quoted 5 lines
drivers/nvme/host/fabrics.c +3
-0{ NVMF_OPT_TOS, "tos=%d" }, { NVMF_OPT_FAIL_FAST_TMO, "fast_io_fail_tmo=%d" }, + { NVMF_OPT_DHCHAP_SECRET, "dhchap_secret=%s" }, + { NVMF_OPT_DHCHAP_AUTH, "authenticate" }, + { NVMF_OPT_DHCHAP_GROUP, "dhchap_group=%s" },Isn't the group driven by the subsystem? also why is there a "authenticate" boolean? what is it good for?
Ah. Right. Of course, the 'group' is pointless here.
And the 'authenticate' bool is the abovementioned bidirectional authentication. I _do_ need to give it another name.Quoted 97 lines
Quoted 97 lines
drivers/nvme/host/fabrics.c +29
-0{ NVMF_OPT_ERR, NULL } };@@ -824,6 +850,35 @@ static int nvmf_parse_options(structnvmf_ctrl_options *opts, } opts->tos = token; break; + case NVMF_OPT_DHCHAP_SECRET: + p = match_strdup(args); + if (!p) { + ret = -ENOMEM; + goto out; + } + if (strncmp(p, "DHHC-1:00:", 10)) { + pr_err("Invalid DH-CHAP secret %s\n", p); + ret = -EINVAL; + goto out; + } + kfree(opts->dhchap_secret); + opts->dhchap_secret = p; + break; + case NVMF_OPT_DHCHAP_AUTH: + opts->dhchap_auth = true; + break; + case NVMF_OPT_DHCHAP_GROUP: + if (match_int(args, &token)) { + ret = -EINVAL; + goto out; + } + if (token <= 0) { + pr_err("Invalid dhchap_group %d\n", token); + ret = -EINVAL; + goto out; + } + opts->dhchap_group = token; + break;default:
pr_warn("unknown parameter or missing value '%s' in ctrl creation request\n", p);drivers/nvme/host/fabrics.c +1
-0@@ -942,6 +997,7 @@ void nvmf_free_options(struct nvmf_ctrl_options *opts) kfree(opts->subsysnqn); kfree(opts->host_traddr); kfree(opts->host_iface); + kfree(opts->dhchap_secret); kfree(opts); }
EXPORT_SYMBOL_GPL(nvmf_free_options);drivers/nvme/host/fabrics.c +4
-1@@ -951,7 +1007,10 @@ EXPORT_SYMBOL_GPL(nvmf_free_options); NVMF_OPT_KATO | NVMF_OPT_HOSTNQN | \ NVMF_OPT_HOST_ID | NVMF_OPT_DUP_CONNECT |\ NVMF_OPT_DISABLE_SQFLOW |\ - NVMF_OPT_FAIL_FAST_TMO) + NVMF_OPT_CTRL_LOSS_TMO |\ + NVMF_OPT_FAIL_FAST_TMO |\ + NVMF_OPT_DHCHAP_SECRET |\ + NVMF_OPT_DHCHAP_AUTH | NVMF_OPT_DHCHAP_GROUP)static struct
nvme_ctrl*nvmf_create_ctrl(struct device *dev, const char *buf)drivers/nvme/host/fabrics.h +8
-0@@ -67,6 +67,9 @@ enum { NVMF_OPT_TOS = 1 << 19, NVMF_OPT_FAIL_FAST_TMO = 1 << 20, NVMF_OPT_HOST_IFACE = 1 << 21, + NVMF_OPT_DHCHAP_SECRET = 1 << 22, + NVMF_OPT_DHCHAP_AUTH = 1 << 23, + NVMF_OPT_DHCHAP_GROUP = 1 << 24, }; /**@@ -96,6 +99,8 @@ enum { * @max_reconnects: maximum number of allowed reconnect attempts before removing * the controller, (-1) means reconnect forever, zero means remove * immediately; + * @dhchap_secret: DH-HMAC-CHAP secret + * @dhchap_auth: DH-HMAC-CHAP authenticate controller * @disable_sqflow: disable controller sq flow control * @hdr_digest: generate/verify header digest (TCP) * @data_digest: generate/verify data digest (TCP)@@ -120,6 +125,9 @@ struct nvmf_ctrl_options { unsigned int kato; struct nvmf_host *host; int max_reconnects; + char *dhchap_secret; + int dhchap_group; + bool dhchap_auth; bool disable_sqflow; bool hdr_digest; bool data_digest;drivers/nvme/host/nvme.h +0
-0@@ -328,6 +328,12 @@ struct nvme_ctrl { struct work_struct ana_work;Quoted 9 lines
drivers/nvme/host/nvme.h struct nvme_ctrl { · line 334
Quoted 5 lines
drivers/nvme/host/nvme.h +4
-0#endif +#ifdef CONFIG_NVME_AUTH + u16 transaction; + u8 dhchap_hash; + u8 dhchap_dhgroup;Do multiple controllers in the same subsystem have different params? no, so I think these should be lifted to subsys.
Quoted 2 lines
It doesn't actually say in the spec; it always refers to the params as being set by the controller. So it could be either; maybe we should ask for clafication at the fmds call.
We should, but I'd be surprised that different controllers in the same subsystem can authenticate difrerently...
_______________________________________________
Linux-nvme mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-nvme
Re: [PATCH 06/11] nvme: Implement In-Band authentication
In reply to Hannes ReineckeQuoted 11 lines · Hannes Reinecke
Quoted 9 lines · Sagi Grimberg
Quoted 2 lines
Implement NVMe-oF In-Band authentication. This patch adds two new fabric options '
dhchap_key' to specify the PSKpre-shared-key.
Also, we need a sysfs knob to rotate the key that will trigger re-authentication or even a simple controller(s-plural) reset, so this should go beyond just the connection string.
Quoted 10 lines
Yeah, re-authentication currently is not implemented. I first wanted to get this patchset out such that we can settle on the userspace interface (both from host and target).
I'll have to think on how we should handle authentication; one of the really interesting cases would be when one malicious admin will _just_ send a 'negotiate' command to the controller. As per spec the controller will be waiting for an 'authentication receive' command to send a 'challenge' payload back to the host. But that will never come, so as it stands currently the controller is required to abort the connection. Not very nice.
Yes, in this case after some reasonable timeout (I would suggest 10-15 seconds) the controller expected to abort connection and clean up all allocated resources.
To handle DoS possibility to make too many such "orphan" negotiations, hence consume all controller memory, some additional handling is needed. For simplicity as a first step I would suggest to have a global limit on number of currently being authenticated connections.
[...]
Quoted 23 lines
Quoted 20 lines
Quoted 7 lines
+
chap->key=nvme_auth_extract_secret(ctrl->opts->dhchap_secret, + &chap->key_len);
+ if (IS_ERR(chap->key)) {
+ ret =PTR_ERR(chap->key);
+chap->key= NULL;
+ return ret;
+ }drivers/nvme/host/auth.c line 618
Quoted 9 lines
drivers/nvme/host/auth.c new file +9
-0+ + if (key_hash == 0) + return 0; + + hmac_name = nvme_auth_hmac_name(key_hash); + if (!hmac_name) { + pr_debug("Invalid key hash id %d\n", key_hash); + return -EKEYREJECTED; + }Why does the user influence the hmac used? isn't that is driven by the susbsystem?
Quoted 2 lines
I don't think that the user should choose in this level.
Quoted 8 lines
That is another weirdness of the spec.
The _secret_ will be hashed with a specific function, and that function is stated in the transport representation.
(Cf section "DH-HMAC-CHAP Security Requirements").
This is _not_ the hash function used by the authentication itself, which will be selected by the protocol.
So it's not the user here, but rather the transport specification of the key which selects the hash algorithm.
Yes, good catch. It looks as a minor errata material to specify that hash function here is implementation specific.
I would suggest to just hardcode SHA512 here. Users don't have to be confused by this.
Vlad
_______________________________________________
Linux-nvme mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-nvme
Hannes Reinecke
maintainerRe: [PATCH 06/11] nvme: Implement In-Band authentication
Quoted 67 lines · Vladislav Bolkhovitin
Quoted 11 lines · Hannes Reinecke
Quoted 9 lines · Sagi Grimberg
Quoted 2 lines
Implement NVMe-oF In-Band authentication. This patch adds two new fabric options '
dhchap_key' to specify the PSKpre-shared-key.
Also, we need a sysfs knob to rotate the key that will trigger re-authentication or even a simple controller(s-plural) reset, so this should go beyond just the connection string.
Quoted 10 lines
Yeah, re-authentication currently is not implemented. I first wanted to get this patchset out such that we can settle on the userspace interface (both from host and target).
I'll have to think on how we should handle authentication; one of the really interesting cases would be when one malicious admin will _just_ send a 'negotiate' command to the controller. As per spec the controller will be waiting for an 'authentication receive' command to send a 'challenge' payload back to the host. But that will never come, so as it stands currently the controller is required to abort the connection. Not very nice.Yes, in this case after some reasonable timeout (I would suggest 10-15 seconds) the controller expected to abort connection and clean up all allocated resources.
To handle DoS possibility to make too many such "orphan" negotiations, hence consume all controller memory, some additional handling is needed. For simplicity as a first step I would suggest to have a global limit on number of currently being authenticated connections.
[...]
Quoted 23 lines
Quoted 20 lines
Quoted 7 lines
+
chap->key=nvme_auth_extract_secret(ctrl->opts->dhchap_secret, + &chap->key_len);
+ if (IS_ERR(chap->key)) {
+ ret =PTR_ERR(chap->key);
+chap->key= NULL;
+ return ret;
+ }drivers/nvme/host/auth.c line 618
Quoted 9 lines
drivers/nvme/host/auth.c new file +9
-0+ + if (key_hash == 0) + return 0; + + hmac_name = nvme_auth_hmac_name(key_hash); + if (!hmac_name) { + pr_debug("Invalid key hash id %d\n", key_hash); + return -EKEYREJECTED; + }Why does the user influence the hmac used? isn't that is driven by the susbsystem?
Quoted 2 lines
I don't think that the user should choose in this level.
Quoted 8 lines
That is another weirdness of the spec.
The _secret_ will be hashed with a specific function, and that function is stated in the transport representation.
(Cf section "DH-HMAC-CHAP Security Requirements").
This is _not_ the hash function used by the authentication itself, which will be selected by the protocol.
So it's not the user here, but rather the transport specification of the key which selects the hash algorithm.
Quoted 6 lines
Yes, good catch. It looks as a minor errata material to specify that hash function here is implementation specific.
I would suggest to just hardcode SHA512 here. Users don't have to be confused by this.
Sure, can do. My reasoning was that the target absolutely has to support the hash functions specified in the PSK, so that will be a safe bet to choose for the hash function in the protocol itself.
(Any other hash function _might_ not be preset on the target.) But if the PSK does not specify a hash the target need to pick one; and for that of course we can use SHA512.
Cheers,
Hannes
Signature · 4 lines
Dr. Hannes Reinecke Kernel Storage Architect [email protected] +49 911 74053 688 SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg HRB 36809 (AG Nürnberg), GF: Felix Imendörffer
Re: [PATCH 06/11] nvme: Implement In-Band authentication
In reply to Hannes ReineckeAm Freitag, 16. Juli 2021, 13:04:23 CEST schrieb Hannes Reinecke:
Hi Hannes,
Quoted 25 lines
Implement NVMe-oF In-Band authentication. This patch adds two new fabric options '
dhchap_key' to specify the PSK and 'dhchap_authenticate' to request bi-directional authentication of both the host and the controller.Signed-off-by Hannes Reinecke <[email protected]>--- drivers/nvme/host/Kconfig | 11 + drivers/nvme/host/Makefile | 1 + drivers/nvme/host/auth.c | 813 ++++++++++++++++++++++++++++++++++++ drivers/nvme/host/auth.h | 23 + drivers/nvme/host/core.c | 77 +++- drivers/nvme/host/fabrics.c | 65 ++- drivers/nvme/host/fabrics.h | 8 + drivers/nvme/host/nvme.h | 15 + drivers/nvme/host/trace.c | 32 ++ 9 files changed, 1041 insertions(+), 4 deletions(-) create mode 100644 drivers/nvme/host/auth.c create mode 100644 drivers/nvme/host/auth.hdrivers/nvme/host/Kconfig +0
-0
drivers/nvme/host/Kconfig config NVME_TCP · line 93
Quoted 9 lines
drivers/nvme/host/Kconfig +6
-0@@ -85,3 +85,14 @@ config NVME_TCP from https://github.com/linux-nvme/nvme-cli. If unsure, say N. + +config NVME_AUTH + bool "NVM Express over Fabrics In-Band Authentication" + depends on NVME_TCP + select CRYPTO_SHA256 + select CRYPTO_SHA512
What about adding CRYPTO_HMAC here?
Quoted 126 lines
drivers/nvme/host/Kconfig +5
-0+ help + This provides support for NVMe over Fabrics In-Band Authentication + for the NVMe over TCP transport. + + If unsure, say N.drivers/nvme/host/Makefile +1
-0@@ -20,6 +20,7 @@ nvme-core-$(CONFIG_NVME_HWMON) += hwmon.o nvme-y += pci.o nvme-fabrics-y += fabrics.o +nvme-fabrics-$(CONFIG_NVME_AUTH) += auth.o nvme-rdma-y += rdma.odrivers/nvme/host/auth.c new file +103
-0@@ -0,0 +1,813 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2020 Hannes Reinecke, SUSE Linux +*/ + +#include <linux/crc32.h> +#include <linux/base64.h> +#include <asm/unaligned.h> +#include <crypto/hash.h> +#include <crypto/kpp.h> +#include "nvme.h" +#include "fabrics.h" +#include "auth.h" + +static u32 nvme_dhchap_seqnum; + +struct nvme_dhchap_context { +struct crypto_shash *shash_tfm; +unsigned char *key; +size_t key_len; +int qid; +u32 s1; +u32 s2; +u16 transaction; +u8 status; +u8 hash_id; +u8 hash_len; +u8 c1[64 ]; +u8 c2[64 ]; +u8 response[64 ]; +u8 *ctrl_key; +int ctrl_key_len; +u8 *host_key; +int host_key_len; +u8 *sess_key; +int sess_key_len; +}; + +struct nvmet_dhchap_hash_map { +int id; +int hash_len; +const char hmac[15 ]; +const char digest[15 ]; +} hash_map[] = { + {.id =NVME_AUTH_DHCHAP_HASH_SHA256 , + .hash_len =32 , + .hmac ="hmac(sha256)" , .digest ="sha256" }, + {.id =NVME_AUTH_DHCHAP_HASH_SHA384 , + .hash_len =48 , + .hmac ="hmac(sha384)" , .digest ="sha384" }, + {.id =NVME_AUTH_DHCHAP_HASH_SHA512 , + .hash_len =64 , + .hmac ="hmac(sha512)" , .digest ="sha512" }, +}; + +const char *nvme_auth_hmac_name (int hmac_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (hash_map); i++) { +if (hash_map[i].id == hmac_id) +return hash_map[i].hmac; + } +return NULL ; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_name); + +const char *nvme_auth_digest_name (int hmac_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (hash_map); i++) { +if (hash_map[i].id == hmac_id) +return hash_map[i].digest; + } +return NULL ; +} +EXPORT_SYMBOL_GPL (nvme_auth_digest_name); + +int nvme_auth_hmac_len (int hmac_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (hash_map); i++) { +if (hash_map[i].id == hmac_id) +return hash_map[i].hash_len; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_len); + +int nvme_auth_hmac_id (const char *hmac_name) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (hash_map); i++) { +if (!strncmp (hash_map[i].hmac, hmac_name, +strlen (hash_map[i].hmac))) +return hash_map[i].id; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_id);
drivers/nvme/host/auth.c line 113
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ +unsigned char *nvme_auth_extract_secret (unsigned char *dhchap_secret, +size_t *dhchap_key_len) +{ +unsigned char *dhchap_key; +u32 crc; +int key_len; +size_t allocated_len; + + allocated_len =strlen (dhchap_secret) -10 ;
Are you sure that the string is always at least 10 bytes long? If so, can you please add a comment to it?
Also, is it guaranteed that we have an ASCII string? Note, a secret sounds to be like a binary string which may contain \0 as an appropriate value.
drivers/nvme/host/auth.c line 114
Quoted 1 line
drivers/nvme/host/auth.c new file +1
-0+ dhchap_key =kzalloc (allocated_len,GFP_KERNEL );
What about aligning it to CRYPTO_MINALIGN_ATTR to save a memcpy in shash_final?
Quoted 205 lines
drivers/nvme/host/auth.c new file +205
-0+if (!dhchap_key) +return ERR_PTR (-ENOMEM ); + + key_len =base64_decode (dhchap_secret +10 , + allocated_len, dhchap_key); +if (key_len !=36 && key_len !=52 && + key_len !=68 ) { +pr_debug ("Invalid DH-HMAC-CHAP key len %d\n" , + key_len); +kfree (dhchap_key); +return ERR_PTR (-EINVAL ); + } +pr_debug ("DH-HMAC-CHAP Key: %*ph\n" , + (int )key_len, dhchap_key); + +/* The last four bytes is the CRC in little-endian format */ + key_len -=4 ; +/* +* The linux implementation doesn't do pre- and post-increments, +* so we have to do it manually. +*/ + crc = ~crc32 (~0 , dhchap_key, key_len); + +if (get_unaligned_le32 (dhchap_key + key_len) != crc) { +pr_debug ("DH-HMAC-CHAP crc mismatch (key %08x, crc %08x)\n" , +get_unaligned_le32 (dhchap_key + key_len), crc); +kfree (dhchap_key); +return ERR_PTR (-EKEYREJECTED ); + } + *dhchap_key_len = key_len; +return dhchap_key; +} +EXPORT_SYMBOL_GPL (nvme_auth_extract_secret); + +static int nvme_auth_send (struct nvme_ctrl *ctrl,int qid, +void *data,size_t tl) +{ +struct nvme_command cmd = {}; +blk_mq_req_flags_t flags = qid ==NVME_QID_ANY ? +0 :BLK_MQ_REQ_NOWAIT |BLK_MQ_REQ_RESERVED ; +struct request_queue *q = qid ==NVME_QID_ANY ? + ctrl->fabrics_q : ctrl->connect_q; +int ret; + + cmd.auth_send.opcode = nvme_fabrics_command; + cmd.auth_send.fctype = nvme_fabrics_type_auth_send; + cmd.auth_send.secp =NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER ; + cmd.auth_send.spsp0 =0x01 ; + cmd.auth_send.spsp1 =0x01 ; + cmd.auth_send.tl = tl; + + ret =__nvme_submit_sync_cmd (q, &cmd,NULL , data, tl,0 , qid, +0 , flags); +if (ret) +dev_dbg (ctrl->device, +"%s: qid %d error %d\n" , __func__, qid, ret); +return ret; +} + +static int nvme_auth_receive (struct nvme_ctrl *ctrl,int qid, +void *buf,size_t al, +u16 transaction,u8 expected_msg ) +{ +struct nvme_command cmd = {}; +struct nvmf_auth_dhchap_failure_data *data = buf; +blk_mq_req_flags_t flags = qid ==NVME_QID_ANY ? +0 :BLK_MQ_REQ_NOWAIT |BLK_MQ_REQ_RESERVED ; +struct request_queue *q = qid ==NVME_QID_ANY ? + ctrl->fabrics_q : ctrl->connect_q; +int ret; + + cmd.auth_receive.opcode = nvme_fabrics_command; + cmd.auth_receive.fctype = nvme_fabrics_type_auth_receive; + cmd.auth_receive.secp =NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER ; + cmd.auth_receive.spsp0 =0x01 ; + cmd.auth_receive.spsp1 =0x01 ; + cmd.auth_receive.al = al; + + ret =__nvme_submit_sync_cmd (q, &cmd,NULL , buf, al,0 , qid, +0 , flags); +if (ret >0 ) { +dev_dbg (ctrl->device,"%s: qid %d nvme status %x\n" , + __func__, qid, ret); + ret = -EIO ; + } +if (ret <0 ) { +dev_dbg (ctrl->device,"%s: qid %d error %d\n" , + __func__, qid, ret); +return ret; + } +dev_dbg (ctrl->device,"%s: qid %d auth_type %d auth_id %x\n" , + __func__, qid, data->auth_type, data->auth_id); +if (data->auth_type ==NVME_AUTH_COMMON_MESSAGES && + data->auth_id ==NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ) { +return data->reason_code_explanation; + } +if (data->auth_type !=NVME_AUTH_DHCHAP_MESSAGES || + data->auth_id != expected_msg) { +dev_warn (ctrl->device, +"qid %d invalid message %02x/%02x\n" , + qid, data->auth_type, data->auth_id); +return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; + } +if (le16_to_cpu (data->t_id) != transaction) { +dev_warn (ctrl->device, +"qid %d invalid transaction ID %d\n" , + qid,le16_to_cpu (data->t_id)); +return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; + } + +return 0 ; +} + +static int nvme_auth_dhchap_negotiate (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size) +{ +struct nvmf_auth_dhchap_negotiate_data *data = buf; +size_t size =sizeof (*data) +sizeof (union nvmf_auth_protocol ); + +if (buf_size < size) +return -EINVAL ; + +memset ((u8 *)buf,0 , size); + 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 */ + data->napd =1 ; + data->auth_protocol[0 ].dhchap.authid =NVME_AUTH_DHCHAP_AUTH_ID ; + data->auth_protocol[0 ].dhchap.halen =3 ; + data->auth_protocol[0 ].dhchap.dhlen =1 ; + data->auth_protocol[0 ].dhchap.idlist[0 ] =NVME_AUTH_DHCHAP_HASH_SHA256 ; + data->auth_protocol[0 ].dhchap.idlist[1 ] =NVME_AUTH_DHCHAP_HASH_SHA384 ; + data->auth_protocol[0 ].dhchap.idlist[2 ] =NVME_AUTH_DHCHAP_HASH_SHA512 ; + data->auth_protocol[0 ].dhchap.idlist[3 ] =NVME_AUTH_DHCHAP_DHGROUP_NULL ; + +return size; +} + +static int nvme_auth_dhchap_challenge (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size) +{ +struct nvmf_auth_dhchap_challenge_data *data = buf; +size_t size =sizeof (*data) + data->hl + data->dhvlen; +const char *gid_name; + +if (buf_size < size) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; +return -ENOMSG ; + } + +if (data->hashid !=NVME_AUTH_DHCHAP_HASH_SHA256 && + data->hashid !=NVME_AUTH_DHCHAP_HASH_SHA384 && + data->hashid !=NVME_AUTH_DHCHAP_HASH_SHA512 ) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: invalid HASH ID %d\n" , + chap->qid, data->hashid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ; +return -EPROTO ; + } +switch (data->dhgid) { +case NVME_AUTH_DHCHAP_DHGROUP_NULL : + gid_name ="null" ; +break ; +default : + gid_name =NULL ; +break ; + } +if (!gid_name) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: invalid DH group id %d\n" , + chap->qid, data->dhgid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; +return -EPROTO ; + } +if (data->dhgid !=NVME_AUTH_DHCHAP_DHGROUP_NULL ) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; +return -EPROTO ; + } +if (data->dhgid ==NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen !=0 ) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; +return -EPROTO ; + } +dev_dbg (ctrl->device,"%s: qid %d requested hash id %d\n" , + __func__, chap->qid, data->hashid); +if (nvme_auth_hmac_len (data->hashid) != data->hl) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: invalid hash length\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ; +return -EPROTO ; + } + chap->hash_id = data->hashid; + chap->hash_len = data->hl; + chap->s1 =le32_to_cpu (data->seqnum); +memcpy (chap->c1, data->cval, chap->hash_len); + +return 0 ; +} +
drivers/nvme/host/auth.c line 329
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+static int nvme_auth_dhchap_reply (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size) +{ +struct nvmf_auth_dhchap_reply_data *data = buf; +size_t size =sizeof (*data); + + size +=2 * chap->hash_len; +if (ctrl->opts->dhchap_auth) { +get_random_bytes (chap->c2, chap->hash_len);
Why are you using CRYPTO_RNG_DEFAULT when you are using get_random_bytes here?
Quoted 159 lines
drivers/nvme/host/auth.c new file +159
-0+ chap->s2 = nvme_dhchap_seqnum++; + }else +memset (chap->c2,0 , chap->hash_len); + +if (chap->host_key_len) + size += chap->host_key_len; + +if (buf_size < size) +return -EINVAL ; + +memset (buf,0 , size); + data->auth_type =NVME_AUTH_DHCHAP_MESSAGES ; + data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_REPLY ; + data->t_id =cpu_to_le16 (chap->transaction); + data->hl = chap->hash_len; + data->dhvlen = chap->host_key_len; + data->seqnum =cpu_to_le32 (chap->s2); +memcpy (data->rval, chap->response, chap->hash_len); +if (ctrl->opts->dhchap_auth) { +dev_dbg (ctrl->device,"%s: qid %d ctrl challenge %*ph\n" , + __func__, chap->qid, + chap->hash_len, chap->c2); + data->cvalid =1 ; +memcpy (data->rval + chap->hash_len, chap->c2, + chap->hash_len); + } +if (chap->host_key_len) +memcpy (data->rval +2 * chap->hash_len, chap->host_key, + chap->host_key_len); + +return size; +} + +static int nvme_auth_dhchap_success1 (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size) +{ +struct nvmf_auth_dhchap_success1_data *data = buf; +size_t size =sizeof (*data); + +if (ctrl->opts->dhchap_auth) + size += chap->hash_len; + + +if (buf_size < size) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; +return -ENOMSG ; + } + +if (data->hl != chap->hash_len) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: invalid hash length %d\n" , + chap->qid, data->hl); + chap->status =NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ; +return -EPROTO ; + } + +if (!data->rvalid) +return 0 ; + +/* Validate controller response */ +if (memcmp (chap->response, data->rval, data->hl)) { +dev_dbg (ctrl->device,"%s: qid %d ctrl response %*ph\n" , + __func__, chap->qid, chap->hash_len, data->rval); +dev_dbg (ctrl->device,"%s: qid %d host response %*ph\n" , + __func__, chap->qid, chap->hash_len, chap->response); +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: controller authentication failed\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; +return -EPROTO ; + } +dev_info (ctrl->device, +"qid %d: DH-HMAC-CHAP: controller authenticated\n" , + chap->qid); +return 0 ; +} + +static int nvme_auth_dhchap_success2 (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size) +{ +struct nvmf_auth_dhchap_success2_data *data = buf; +size_t size =sizeof (*data); + +memset (buf,0 , size); + data->auth_type =NVME_AUTH_DHCHAP_MESSAGES ; + data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 ; + data->t_id =cpu_to_le16 (chap->transaction); + +return size; +} + +static int nvme_auth_dhchap_failure2 (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size) +{ +struct nvmf_auth_dhchap_failure_data *data = buf; +size_t size =sizeof (*data); + +memset (buf,0 , size); + data->auth_type =NVME_AUTH_DHCHAP_MESSAGES ; + data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 ; + data->t_id =cpu_to_le16 (chap->transaction); + data->reason_code =1 ; + data->reason_code_explanation = chap->status; + +return size; +} + +int nvme_auth_select_hash (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap) +{ +char *hash_name; +int ret; + +switch (chap->hash_id) { +case NVME_AUTH_DHCHAP_HASH_SHA256 : + hash_name ="hmac(sha256)" ; +break ; +case NVME_AUTH_DHCHAP_HASH_SHA384 : + hash_name ="hmac(sha384)" ; +break ; +case NVME_AUTH_DHCHAP_HASH_SHA512 : + hash_name ="hmac(sha512)" ; +break ; +default : + hash_name =NULL ; +break ; + } +if (!hash_name) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +return -EPROTO ; + } + chap->shash_tfm =crypto_alloc_shash (hash_name,0 , +CRYPTO_ALG_ALLOCATES_MEMORY ); +if (IS_ERR (chap->shash_tfm)) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; + chap->shash_tfm =NULL ; +return -EPROTO ; + } +if (!chap->key) { +dev_warn (ctrl->device,"qid %d: cannot select hash, no key\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->shash_tfm); + chap->shash_tfm =NULL ; +return -EINVAL ; + } + ret =crypto_shash_setkey (chap->shash_tfm, chap->key, chap->key_len); +if (ret) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->shash_tfm); + chap->shash_tfm =NULL ; +return ret; + } +dev_info (ctrl->device,"qid %d: DH-HMAC_CHAP: selected hash %s\n" , + chap->qid, hash_name); +return 0 ;
drivers/nvme/host/auth.c
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+} + +static int nvme_auth_dhchap_host_response (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap) +{ +SHASH_DESC_ON_STACK (shash, chap->shash_tfm); +u8 buf[4 ], *challenge = chap->c1; +int ret; + + dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction
%d\n",
Quoted 37 lines
drivers/nvme/host/auth.c new file +37
-0+ __func__, chap->qid, chap->s1, chap->transaction); + shash->tfm = chap->shash_tfm; + ret =crypto_shash_init (shash); +if (ret) + goto out; + ret =crypto_shash_update (shash, challenge, chap->hash_len); +if (ret) + goto out; +put_unaligned_le32 (chap->s1, buf); + ret =crypto_shash_update (shash, buf,4 ); +if (ret) + goto out; +put_unaligned_le16 (chap->transaction, buf); + ret =crypto_shash_update (shash, buf,2 ); +if (ret) + goto out; +memset (buf,0 ,sizeof (buf)); + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash,"HostHost" ,8 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->opts->host->nqn, +strlen (ctrl->opts->host->nqn)); +if (ret) + goto out; + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->opts->subsysnqn, +strlen (ctrl->opts->subsysnqn)); +if (ret) + goto out; + ret =crypto_shash_final (shash, chap->response); +out: +return ret;
drivers/nvme/host/auth.c
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+} + +static int nvme_auth_dhchap_ctrl_response (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap) +{ +SHASH_DESC_ON_STACK (shash, chap->shash_tfm); +u8 buf[4 ], *challenge = chap->c2; +int ret; + + dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction
%d\n",
Quoted 104 lines
drivers/nvme/host/auth.c new file +104
-0+ __func__, chap->qid, chap->s2, chap->transaction); +dev_dbg (ctrl->device,"%s: qid %d challenge %*ph\n" , + __func__, chap->qid, chap->hash_len, challenge); +dev_dbg (ctrl->device,"%s: qid %d subsysnqn %s\n" , + __func__, chap->qid, ctrl->opts->subsysnqn); +dev_dbg (ctrl->device,"%s: qid %d hostnqn %s\n" , + __func__, chap->qid, ctrl->opts->host->nqn); + shash->tfm = chap->shash_tfm; + ret =crypto_shash_init (shash); +if (ret) + goto out; + ret =crypto_shash_update (shash, challenge, chap->hash_len); +if (ret) + goto out; +put_unaligned_le32 (chap->s2, buf); + ret =crypto_shash_update (shash, buf,4 ); +if (ret) + goto out; +put_unaligned_le16 (chap->transaction, buf); + ret =crypto_shash_update (shash, buf,2 ); +if (ret) + goto out; +memset (buf,0 ,4 ); + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash,"Controller" ,10 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->opts->subsysnqn, +strlen (ctrl->opts->subsysnqn)); +if (ret) + goto out; + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->opts->host->nqn, +strlen (ctrl->opts->host->nqn)); +if (ret) + goto out; + ret =crypto_shash_final (shash, chap->response); +out: +return ret; +} + +int nvme_auth_generate_key (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap) +{ +int ret; +u8 key_hash; +const char *hmac_name; +struct crypto_shash *key_tfm; + +if (sscanf (ctrl->opts->dhchap_secret,"DHHC-1:%hhd:%*s:" , + &key_hash) !=1 ) +return -EINVAL ; + + chap->key =nvme_auth_extract_secret (ctrl->opts->dhchap_secret, + &chap->key_len); +if (IS_ERR (chap->key)) { + ret =PTR_ERR (chap->key); + chap->key =NULL ; +return ret; + } + +if (key_hash ==0 ) +return 0 ; + + hmac_name =nvme_auth_hmac_name (key_hash); +if (!hmac_name) { +pr_debug ("Invalid key hash id %d\n" , key_hash); +return -EKEYREJECTED ; + } + + key_tfm =crypto_alloc_shash (hmac_name,0 ,0 ); +if (IS_ERR (key_tfm)) { +kfree (chap->key); + chap->key =NULL ; + ret =PTR_ERR (key_tfm); + }else { +SHASH_DESC_ON_STACK (shash, key_tfm); + + shash->tfm = key_tfm; + ret =crypto_shash_setkey (key_tfm, chap->key, + chap->key_len); +if (ret <0 ) { +crypto_free_shash (key_tfm); +kfree (chap->key); + chap->key =NULL ; +return ret; + } +crypto_shash_init (shash); +crypto_shash_update (shash, ctrl->opts->host->nqn, +strlen (ctrl->opts->host->nqn)); +crypto_shash_update (shash,"NVMe-over-Fabrics" ,17 ); +crypto_shash_final (shash, chap->key); +crypto_free_shash (key_tfm); + } +return 0 ; +} + +void nvme_auth_free (struct nvme_dhchap_context *chap) +{ +if (chap->shash_tfm)
drivers/nvme/host/auth.c line 659
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+crypto_free_shash (chap->shash_tfm); +if (chap->key) +kfree (chap->key); +if (chap->ctrl_key) +kfree (chap->ctrl_key); +if (chap->host_key) +kfree (chap->host_key); +if (chap->sess_key) +kfree (chap->sess_key); +kfree (chap);
kfree_sensitive in all cases as all buffers have sensitive data?
drivers/nvme/host/auth.c line 669
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+} + +int nvme_auth_negotiate (struct nvme_ctrl *ctrl,int qid) +{ +struct nvme_dhchap_context *chap; +void *buf; +size_t buf_size, tl; +int ret =0 ; + + chap =kzalloc (sizeof (*chap),GFP_KERNEL );
Suggestion: make sure that chap->response is aligned to CRYPTO_MINALIGN_ATTR - then you would save a memcpy in crypto_shash_final
Quoted 178 lines
drivers/nvme/host/auth.c new file +144
-0+if (!chap) +return -ENOMEM ; + chap->qid = qid; + chap->transaction = ctrl->transaction++; + + ret =nvme_auth_generate_key (ctrl, chap); +if (ret) { +dev_dbg (ctrl->device,"%s: failed to generate key, error %d\n" , + __func__, ret); +nvme_auth_free (chap); +return ret; + } + +/* +* Allocate a large enough buffer for the entire negotiation: +* 4k should be enough to ffdhe8192. +*/ + buf_size =4096 ; + buf =kzalloc (buf_size,GFP_KERNEL ); +if (!buf) { + ret = -ENOMEM ; + goto out; + } + +/* DH-HMAC-CHAP Step 1: send negotiate */ +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP negotiate\n" , + __func__, qid); + ret =nvme_auth_dhchap_negotiate (ctrl, chap, buf, buf_size); +if (ret <0 ) + goto out; + tl = ret; + ret =nvme_auth_send (ctrl, qid, buf, tl); +if (ret) + goto out; + +memset (buf,0 , buf_size); + ret =nvme_auth_receive (ctrl, qid, buf, buf_size, chap->transaction, +NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE ); +if (ret <0 ) { +dev_dbg (ctrl->device, +"%s: qid %d DH-HMAC-CHAP failed to receive challenge\n" , + __func__, qid); + goto out; + } +if (ret >0 ) { + chap->status = ret; + goto fail1; + } + +/* DH-HMAC-CHAP Step 2: receive challenge */ +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP challenge\n" , + __func__, qid); + + ret =nvme_auth_dhchap_challenge (ctrl, chap, buf, buf_size); +if (ret) { +/* Invalid parameters for negotiate */ + goto fail2; + } + +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP select hash\n" , + __func__, qid); + ret =nvme_auth_select_hash (ctrl, chap); +if (ret) + goto fail2; + +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP host response\n" , + __func__, qid); + ret =nvme_auth_dhchap_host_response (ctrl, chap); +if (ret) + goto fail2; + +/* DH-HMAC-CHAP Step 3: send reply */ +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP reply\n" , + __func__, qid); + ret =nvme_auth_dhchap_reply (ctrl, chap, buf, buf_size); +if (ret <0 ) + goto fail2; + + tl = ret; + ret =nvme_auth_send (ctrl, qid, buf, tl); +if (ret) + goto fail2; + +memset (buf,0 , buf_size); + ret =nvme_auth_receive (ctrl, qid, buf, buf_size, chap->transaction, +NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1 ); +if (ret <0 ) { +dev_dbg (ctrl->device, +"%s: qid %d DH-HMAC-CHAP failed to receive success1\n" , + __func__, qid); + goto out; + } +if (ret >0 ) { + chap->status = ret; + goto fail1; + } + +if (ctrl->opts->dhchap_auth) { +dev_dbg (ctrl->device, +"%s: qid %d DH-HMAC-CHAP controller response\n" , + __func__, qid); + ret =nvme_auth_dhchap_ctrl_response (ctrl, chap); +if (ret) + goto fail2; + } + +/* DH-HMAC-CHAP Step 4: receive success1 */ +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP success1\n" , + __func__, qid); + ret =nvme_auth_dhchap_success1 (ctrl, chap, buf, buf_size); +if (ret <0 ) { +/* Controller authentication failed */ + goto fail2; + } + tl = ret; +/* DH-HMAC-CHAP Step 5: send success2 */ +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP success2\n" , + __func__, qid); + tl =nvme_auth_dhchap_success2 (ctrl, chap, buf, buf_size); + ret =nvme_auth_send (ctrl, qid, buf, tl); +if (!ret) + goto out; + +fail1: +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP failure1, status %x\n" , + __func__, qid, chap->status); + goto out; + +fail2: +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP failure2, status %x\n" , + __func__, qid, chap->status); + tl =nvme_auth_dhchap_failure2 (ctrl, chap, buf, buf_size); + ret =nvme_auth_send (ctrl, qid, buf, tl); + +out: +if (!ret && chap->status) + ret = -EPROTO ; +if (!ret) { + ctrl->dhchap_hash = chap->hash_id; + } +kfree (buf); +nvme_auth_free (chap); +return ret; +}drivers/nvme/host/auth.h new file +23
-0@@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions +*/ + +#ifndef _NVME_AUTH_H +#define _NVME_AUTH_H + +const char *nvme_auth_dhgroup_name (int dhgroup_id); +int nvme_auth_dhgroup_pubkey_size (int dhgroup_id); +int nvme_auth_dhgroup_privkey_size (int dhgroup_id); +const char *nvme_auth_dhgroup_kpp (int dhgroup_id); +int nvme_auth_dhgroup_id (const char *dhgroup_name); + +const char *nvme_auth_hmac_name (int hmac_id); +const char *nvme_auth_digest_name (int hmac_id); +int nvme_auth_hmac_id (const char *hmac_name); +int nvme_auth_hmac_len (int hmac_len); + +unsigned char *nvme_auth_extract_secret (unsigned char *dhchap_secret, +size_t *dhchap_key_len); + +#endif /* _NVME_AUTH_H */ drivers/nvme/host/core.c +0
-0
drivers/nvme/host/core.c bool __nvme_check_ready(struct nvme_ctrl *ctrl, struct · line 710
Quoted 3 lines
drivers/nvme/host/core.c +0
-0@@ -708,7 +708,9 @@ bool __nvme_check_ready(struct nvme_ctrl *ctrl, struct request *rq, switch (ctrl->state) {case NVME_CTRL_CONNECTING : if (blk_rq_is_passthrough(rq) && nvme_is_fabrics(req->cmd)
&&
drivers/nvme/host/core.c bool __nvme_check_ready(struct nvme_ctrl *ctrl, struct · line 711
Quoted 2 lines
drivers/nvme/host/core.c +1
-1- req->cmd->fabrics.fctype == nvme_fabrics_type_connect) + (req->cmd->fabrics.fctype == nvme_fabrics_type_connect
|| > + req->cmd->fabrics.fctype == nvme_fabrics_type_auth_send || > + req->cmd->fabrics.fctype ==
nvme_fabrics_type_auth_receive))
Quoted 199 lines
drivers/nvme/host/core.c +0
-0return true;break ;default :@@ -3426,6 +3428,66 @@ static ssize_tnvme_ctrl_fast_io_fail_tmo_store(struct device *dev, static DEVICE_ATTR(fast_io_fail_tmo,
S_IRUGO|S_IWUSR,drivers/nvme/host/core.c +73
-0nvme_ctrl_fast_io_fail_tmo_show, nvme_ctrl_fast_io_fail_tmo_store); +#ifdef CONFIG_NVME_AUTH +struct nvmet_dhchap_hash_map { +int id; +const char name[15 ]; +} hash_map[] = { + {.id =NVME_AUTH_DHCHAP_HASH_SHA256 , + .name ="hmac(sha256)" , }, + {.id =NVME_AUTH_DHCHAP_HASH_SHA384 , + .name ="hmac(sha384)" , }, + {.id =NVME_AUTH_DHCHAP_HASH_SHA512 , + .name ="hmac(sha512)" , }, +}; + +static ssize_t dhchap_hash_show (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct nvme_ctrl *ctrl =dev_get_drvdata (dev); +int i; + +for (i =0 ; i <ARRAY_SIZE (hash_map); i++) { +if (hash_map[i].id == ctrl->dhchap_hash) +return sprintf (buf,"%s\n" , hash_map[i].name); + } +return sprintf (buf,"none\n" ); +} +DEVICE_ATTR_RO (dhchap_hash); + +struct nvmet_dhchap_group_map { +int id; +const char name[15 ]; +} dhgroup_map[] = { + {.id =NVME_AUTH_DHCHAP_DHGROUP_NULL , + .name ="NULL" , }, + {.id =NVME_AUTH_DHCHAP_DHGROUP_2048 , + .name ="ffdhe2048" , }, + {.id =NVME_AUTH_DHCHAP_DHGROUP_3072 , + .name ="ffdhe3072" , }, + {.id =NVME_AUTH_DHCHAP_DHGROUP_4096 , + .name ="ffdhe4096" , }, + {.id =NVME_AUTH_DHCHAP_DHGROUP_6144 , + .name ="ffdhe6144" , }, + {.id =NVME_AUTH_DHCHAP_DHGROUP_8192 , + .name ="ffdhe8192" , }, +}; + +static ssize_t dhchap_dhgroup_show (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct nvme_ctrl *ctrl =dev_get_drvdata (dev); +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (hash_map[i].id == ctrl->dhchap_dhgroup) +return sprintf (buf,"%s\n" , dhgroup_map[i].name); + } +return sprintf (buf,"none\n" ); +} +DEVICE_ATTR_RO (dhchap_dhgroup); +#endif +static struct attribute *nvme_dev_attrs[] = { &dev_attr_reset_controller.attr, &dev_attr_rescan_controller.attr,@@ -3447,6 +3509,10 @@ static struct attribute *nvme_dev_attrs[] = { &dev_attr_reconnect_delay.attr, &dev_attr_fast_io_fail_tmo.attr, &dev_attr_kato.attr, +#ifdef CONFIG_NVME_AUTH + &dev_attr_dhchap_hash.attr, + &dev_attr_dhchap_dhgroup.attr, +#endif NULL };@@ -3470,6 +3536,10 @@ static umode_t nvme_dev_attrs_are_visible(struct kobject *kobj, return 0;if (a == &dev_attr_fast_io_fail_tmo.attr && !ctrl->opts)return 0 ; +#ifdef CONFIG_NVME_AUTH +if (a == &dev_attr_dhchap_hash.attr && !ctrl->opts) +return 0 ; +#endif return a->mode; }@@ -4581,6 +4651,11 @@ static inline void _nvme_check_size(void)BUILD_BUG_ON (sizeof (struct nvme_smart_log ) !=512 );BUILD_BUG_ON (sizeof (struct nvme_dbbuf ) !=64 );BUILD_BUG_ON (sizeof (struct nvme_directive_cmd ) !=64 ); +BUILD_BUG_ON (sizeof (struct nvmf_auth_dhchap_negotiate_data ) !=8 ); +BUILD_BUG_ON (sizeof (struct nvmf_auth_dhchap_challenge_data ) !=16 ); +BUILD_BUG_ON (sizeof (struct nvmf_auth_dhchap_reply_data ) !=16 ); +BUILD_BUG_ON (sizeof (struct nvmf_auth_dhchap_success1_data ) !=16 ); +BUILD_BUG_ON (sizeof (struct nvmf_auth_dhchap_success2_data ) !=16 ); }drivers/nvme/host/fabrics.c +49
-2@@ -366,6 +366,7 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl)union nvme_result res;struct nvmf_connect_data *data;int ret; +u32 result; cmd.connect.opcode = nvme_fabrics_command; cmd.connect.fctype = nvme_fabrics_type_connect;@@ -398,8 +399,18 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) goto out_free_data; } - ctrl->cntlid =le16_to_cpu (res.u16); - + result =le32_to_cpu (res.u32); + ctrl->cntlid = result &0xFFFF ; +if ((result >>16 ) &2 ) { +/* Authentication required */ + ret =nvme_auth_negotiate (ctrl,NVME_QID_ANY ); +if (ret) +dev_warn (ctrl->device, +"qid 0: authentication failed\n" ); +else +dev_info (ctrl->device, +"qid 0: authenticated\n" ); + } out_free_data:kfree (data);return ret;@@ -432,6 +443,7 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid) struct nvmf_connect_data *data;union nvme_result res;int ret; +u32 result; cmd.connect.opcode = nvme_fabrics_command; cmd.connect.fctype = nvme_fabrics_type_connect;@@ -457,6 +469,17 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid) nvmf_log_connect_error(ctrl, ret, le32_to_cpu(res.u32), &cmd, data); } + result =le32_to_cpu (res.u32); +if ((result >>16 ) &2 ) { +/* Authentication required */ + ret =nvme_auth_negotiate (ctrl, qid); +if (ret) +dev_warn (ctrl->device, +"qid %u: authentication failed\n" , qid); +else +dev_info (ctrl->device, +"qid %u: authenticated\n" , qid); + }kfree (data);return ret; }@@ -548,6 +571,9 @@ static const match_table_t opt_tokens = { { NVMF_OPT_NR_POLL_QUEUES, "nr_poll_queues=%d" }, { NVMF_OPT_TOS, "tos=%d" }, { NVMF_OPT_FAIL_FAST_TMO, "fast_io_fail_tmo=%d" }, + { NVMF_OPT_DHCHAP_SECRET, "dhchap_secret=%s" }, + { NVMF_OPT_DHCHAP_AUTH, "authenticate" }, + { NVMF_OPT_DHCHAP_GROUP, "dhchap_group=%s" }, { NVMF_OPT_ERR, NULL } };@@ -824,6 +850,35 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts, } opts->tos = token;break ; +case NVMF_OPT_DHCHAP_SECRET : + p =match_strdup (args); +if (!p) { + ret = -ENOMEM ; + goto out; + } +if (strncmp (p,"DHHC-1:00:" ,10 )) { +pr_err ("Invalid DH-CHAP secret %s\n" , p); + ret = -EINVAL ; + goto out; + } +kfree (opts->dhchap_secret); + opts->dhchap_secret = p; +break ; +case NVMF_OPT_DHCHAP_AUTH : + opts->dhchap_auth = true; +break ; +case NVMF_OPT_DHCHAP_GROUP : +if (match_int (args, &token)) { + ret = -EINVAL ; + goto out;
drivers/nvme/host/fabrics.c static int nvmf_parse_options(struct nvmf_ctrl_options · line 883
Quoted 10 lines
drivers/nvme/host/fabrics.c +8
-0+ } +if (token <=0 ) { +pr_err ("Invalid dhchap_group %d\n" , token); + ret = -EINVAL ; + goto out; + } + opts->dhchap_group = token; +break ;default : pr_warn("unknown parameter or missing value '%s' in ctrl
creation
Quoted 127 lines
drivers/nvme/host/fabrics.c +5
-1request\n", p);@@ -942,6 +997,7 @@ void nvmf_free_options(struct nvmf_ctrl_options *opts)kfree (opts->subsysnqn);kfree (opts->host_traddr);kfree (opts->host_iface); +kfree (opts->dhchap_secret);kfree (opts); }EXPORT_SYMBOL_GPL (nvmf_free_options);@@ -951,7 +1007,10 @@ EXPORT_SYMBOL_GPL(nvmf_free_options); NVMF_OPT_KATO | NVMF_OPT_HOSTNQN | \ NVMF_OPT_HOST_ID | NVMF_OPT_DUP_CONNECT |\ NVMF_OPT_DISABLE_SQFLOW |\ - NVMF_OPT_FAIL_FAST_TMO) + NVMF_OPT_CTRL_LOSS_TMO |\ + NVMF_OPT_FAIL_FAST_TMO |\ + NVMF_OPT_DHCHAP_SECRET |\ + NVMF_OPT_DHCHAP_AUTH | NVMF_OPT_DHCHAP_GROUP)static struct nvme_ctrl *nvmf_create_ctrl (struct device *dev,const char *buf)drivers/nvme/host/fabrics.h +3
-0@@ -67,6 +67,9 @@ enum {NVMF_OPT_TOS =1 <<19 ,NVMF_OPT_FAIL_FAST_TMO =1 <<20 ,NVMF_OPT_HOST_IFACE =1 <<21 , +NVMF_OPT_DHCHAP_SECRET =1 <<22 , +NVMF_OPT_DHCHAP_AUTH =1 <<23 , + NVMF_OPT_DHCHAP_GROUP = 1 << 24, };/** @@ -96,6 +99,8 @@ enum {* @max_reconnects: maximum number of allowed reconnect attempts before removing * the controller, (-1) means reconnect forever, zero
means remove * immediately;
+ * @dhchap_secret: DH-HMAC-CHAP secret
+ * @dhchap_auth: DH-HMAC-CHAP authenticate controller
* @disable_sqflow: disable controller sq flow control
* @hdr_digest: generate/verify header digest (TCP)
* @data_digest: generate/verify data digest (TCP)drivers/nvme/host/fabrics.h +3
-0@@ -120,6 +125,9 @@ struct nvmf_ctrl_options {unsigned int kato;struct nvmf_host *host;int max_reconnects; +char *dhchap_secret; +int dhchap_group; +bool dhchap_auth;bool disable_sqflow;bool hdr_digest;bool data_digest;drivers/nvme/host/nvme.h +15
-0@@ -328,6 +328,12 @@ struct nvme_ctrl {struct work_struct ana_work;#endif +#ifdef CONFIG_NVME_AUTH +u16 transaction; +u8 dhchap_hash; +u8 dhchap_dhgroup; +#endif +/* Power saving configuration */ u64 ps_max_latency_us;bool apst_enabled;@@ -874,6 +880,15 @@ static inline bool nvme_ctrl_sgl_supported(struct nvme_ctrl *ctrl) return ctrl->sgls & ((1 << 0) | (1 << 1)); } +#ifdef CONFIG_NVME_AUTH +int nvme_auth_negotiate (struct nvme_ctrl *ctrl,int qid); +#else +static inline int nvme_auth_negotiate (struct nvme_ctrl *ctrl,int qid) +{ +return -EPROTONOSUPPORT ; +} +#endif +u32 nvme_command_effects (struct nvme_ctrl *ctrl,struct nvme_ns *ns,u8 opcode);int nvme_execute_passthru_rq (struct request *rq);drivers/nvme/host/trace.c +26
-0@@ -271,6 +271,34 @@ static const char *nvme_trace_fabrics_property_get(struct trace_seq *p, u8 *spc) return ret; } +static const char *nvme_trace_fabrics_auth_send (struct trace_seq *p,u8 *spc) +{ +const char *ret =trace_seq_buffer_ptr (p); +u8 spsp0 = spc[1 ]; +u8 spsp1 = spc[2 ]; +u8 secp = spc[3 ]; +u32 tl =get_unaligned_le32 (spc +4 ); + +trace_seq_printf (p,"spsp0=%02x, spsp1=%02x, secp=%02x, tl=%u" , + spsp0, spsp1, secp, tl); +trace_seq_putc (p,0 ); +return ret; +} + +static const char *nvme_trace_fabrics_auth_receive (struct trace_seq *p,u8 *spc) +{ +const char *ret =trace_seq_buffer_ptr (p); +u8 spsp0 = spc[1 ]; +u8 spsp1 = spc[2 ]; +u8 secp = spc[3 ]; +u32 al =get_unaligned_le32 (spc +4 ); + +trace_seq_printf (p,"spsp0=%02x, spsp1=%02x, secp=%02x, al=%u" , + spsp0, spsp1, secp, al); +trace_seq_putc (p,0 ); +return ret; +} +static const char *nvme_trace_fabrics_common (struct trace_seq *p,u8 *spc) {const char *ret =trace_seq_buffer_ptr (p);
drivers/nvme/host/trace.c const char *nvme_trace_parse_fabrics_cmd(struct · line 327
Quoted 10 lines
drivers/nvme/host/trace.c +4
-0@@ -290,6 +318,10 @@ const char *nvme_trace_parse_fabrics_cmd(struct trace_seq *p, return nvme_trace_fabrics_connect(p, spc);case nvme_fabrics_type_property_get:return nvme_trace_fabrics_property_get (p, spc); +case nvme_fabrics_type_auth_send: +return nvme_trace_fabrics_auth_send (p, spc); +case nvme_fabrics_type_auth_receive: +return nvme_trace_fabrics_auth_receive (p, spc);default :return nvme_trace_fabrics_common (p, spc); }
Ciao
Stephan
Hannes Reinecke
maintainerRe: [PATCH 06/11] nvme: Implement In-Band authentication
Quoted 31 lines · Stephan Müller
Am Freitag, 16. Juli 2021, 13:04:23 CEST schrieb Hannes Reinecke:
Hi Hannes,
Quoted 27 lines
Implement NVMe-oF In-Band authentication. This patch adds two new fabric options '
dhchap_key' to specify the PSK and 'dhchap_authenticate' to request bi-directional authentication of both the host and the controller.Signed-off-by Hannes Reinecke <[email protected]>--- drivers/nvme/host/Kconfig | 11 + drivers/nvme/host/Makefile | 1 + drivers/nvme/host/auth.c | 813 ++++++++++++++++++++++++++++++++++++ drivers/nvme/host/auth.h | 23 + drivers/nvme/host/core.c | 77 +++- drivers/nvme/host/fabrics.c | 65 ++- drivers/nvme/host/fabrics.h | 8 + drivers/nvme/host/nvme.h | 15 + drivers/nvme/host/trace.c | 32 ++ 9 files changed, 1041 insertions(+), 4 deletions(-)create mode 100644
drivers/nvme/host/auth.c
create mode 100644drivers/nvme/host/auth.hdrivers/nvme/host/Kconfig +0
-0@@ -85,3 +85,14 @@ config NVME_TCP from https://github.com/linux-nvme/nvme-cli.
Quoted 10 lines
drivers/nvme/host/Kconfig config NVME_TCP · line 93
Quoted 7 lines
drivers/nvme/host/Kconfig +6
-0If unsure, say N. + +config NVME_AUTH + bool "NVM Express over Fabrics In-Band Authentication" + depends on NVME_TCP + select CRYPTO_SHA256 + select CRYPTO_SHA512What about adding
CRYPTO_HMAChere?
Yes, you are correct. Will be fixing it.
Quoted 140 lines
Quoted 126 lines
drivers/nvme/host/Kconfig +5
-0+ help + This provides support for NVMe over Fabrics In-Band Authentication + for the NVMe over TCP transport. + + If unsure, say N.drivers/nvme/host/Makefile +1
-0@@ -20,6 +20,7 @@ nvme-core-$(CONFIG_NVME_HWMON) += hwmon.o nvme-y += pci.o nvme-fabrics-y += fabrics.o +nvme-fabrics-$(CONFIG_NVME_AUTH) += auth.o nvme-rdma-y += rdma.odrivers/nvme/host/auth.c new file +103
-0@@ -0,0 +1,813 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2020 Hannes Reinecke, SUSE Linux +*/ + +#include <linux/crc32.h> +#include <linux/base64.h> +#include <asm/unaligned.h> +#include <crypto/hash.h> +#include <crypto/kpp.h> +#include "nvme.h" +#include "fabrics.h" +#include "auth.h" + +static u32 nvme_dhchap_seqnum; + +struct nvme_dhchap_context { +struct crypto_shash *shash_tfm; +unsigned char *key; +size_t key_len; +int qid; +u32 s1; +u32 s2; +u16 transaction; +u8 status; +u8 hash_id; +u8 hash_len; +u8 c1[64 ]; +u8 c2[64 ]; +u8 response[64 ]; +u8 *ctrl_key; +int ctrl_key_len; +u8 *host_key; +int host_key_len; +u8 *sess_key; +int sess_key_len; +}; + +struct nvmet_dhchap_hash_map { +int id; +int hash_len; +const char hmac[15 ]; +const char digest[15 ]; +} hash_map[] = { + {.id =NVME_AUTH_DHCHAP_HASH_SHA256 , + .hash_len =32 , + .hmac ="hmac(sha256)" , .digest ="sha256" }, + {.id =NVME_AUTH_DHCHAP_HASH_SHA384 , + .hash_len =48 , + .hmac ="hmac(sha384)" , .digest ="sha384" }, + {.id =NVME_AUTH_DHCHAP_HASH_SHA512 , + .hash_len =64 , + .hmac ="hmac(sha512)" , .digest ="sha512" }, +}; + +const char *nvme_auth_hmac_name (int hmac_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (hash_map); i++) { +if (hash_map[i].id == hmac_id) +return hash_map[i].hmac; + } +return NULL ; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_name); + +const char *nvme_auth_digest_name (int hmac_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (hash_map); i++) { +if (hash_map[i].id == hmac_id) +return hash_map[i].digest; + } +return NULL ; +} +EXPORT_SYMBOL_GPL (nvme_auth_digest_name); + +int nvme_auth_hmac_len (int hmac_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (hash_map); i++) { +if (hash_map[i].id == hmac_id) +return hash_map[i].hash_len; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_len); + +int nvme_auth_hmac_id (const char *hmac_name) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (hash_map); i++) { +if (!strncmp (hash_map[i].hmac, hmac_name, +strlen (hash_map[i].hmac))) +return hash_map[i].id; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_id);drivers/nvme/host/auth.c line 113
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ +unsigned char *nvme_auth_extract_secret (unsigned char *dhchap_secret, +size_t *dhchap_key_len) +{ +unsigned char *dhchap_key; +u32 crc; +int key_len; +size_t allocated_len; + + allocated_len =strlen (dhchap_secret) -10 ;Are you sure that the string is always at least 10 bytes long? If so, can you please add a comment to it?
Quoted 3 lines
Also, is it guaranteed that we have an ASCII string? Note, a secret sounds to be like a binary string which may contain \0 as an appropriate value.
The string will always be in the transport encoding as specified in the NVMe Base specification v2.0. Any other string will be rejected by the ioctl interface.
Quoted 5 lines
drivers/nvme/host/auth.c line 114
Quoted 1 line
drivers/nvme/host/auth.c new file +1
-0+ dhchap_key =kzalloc (allocated_len,GFP_KERNEL );What about aligning it to
CRYPTO_MINALIGN_ATTRto save a memcpy inshash_final?
Wasn't aware that I need to do that. Will be fixing it up.
Quoted 209 lines
Quoted 209 lines
drivers/nvme/host/auth.c new file +209
-0+if (!dhchap_key) +return ERR_PTR (-ENOMEM ); + + key_len =base64_decode (dhchap_secret +10 , + allocated_len, dhchap_key); +if (key_len !=36 && key_len !=52 && + key_len !=68 ) { +pr_debug ("Invalid DH-HMAC-CHAP key len %d\n" , + key_len); +kfree (dhchap_key); +return ERR_PTR (-EINVAL ); + } +pr_debug ("DH-HMAC-CHAP Key: %*ph\n" , + (int )key_len, dhchap_key); + +/* The last four bytes is the CRC in little-endian format */ + key_len -=4 ; +/* +* The linux implementation doesn't do pre- and post-increments, +* so we have to do it manually. +*/ + crc = ~crc32 (~0 , dhchap_key, key_len); + +if (get_unaligned_le32 (dhchap_key + key_len) != crc) { +pr_debug ("DH-HMAC-CHAP crc mismatch (key %08x, crc %08x)\n" , +get_unaligned_le32 (dhchap_key + key_len), crc); +kfree (dhchap_key); +return ERR_PTR (-EKEYREJECTED ); + } + *dhchap_key_len = key_len; +return dhchap_key; +} +EXPORT_SYMBOL_GPL (nvme_auth_extract_secret); + +static int nvme_auth_send (struct nvme_ctrl *ctrl,int qid, +void *data,size_t tl) +{ +struct nvme_command cmd = {}; +blk_mq_req_flags_t flags = qid ==NVME_QID_ANY ? +0 :BLK_MQ_REQ_NOWAIT |BLK_MQ_REQ_RESERVED ; +struct request_queue *q = qid ==NVME_QID_ANY ? + ctrl->fabrics_q : ctrl->connect_q; +int ret; + + cmd.auth_send.opcode = nvme_fabrics_command; + cmd.auth_send.fctype = nvme_fabrics_type_auth_send; + cmd.auth_send.secp =NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER ; + cmd.auth_send.spsp0 =0x01 ; + cmd.auth_send.spsp1 =0x01 ; + cmd.auth_send.tl = tl; + + ret =__nvme_submit_sync_cmd (q, &cmd,NULL , data, tl,0 , qid, +0 , flags); +if (ret) +dev_dbg (ctrl->device, +"%s: qid %d error %d\n" , __func__, qid, ret); +return ret; +} + +static int nvme_auth_receive (struct nvme_ctrl *ctrl,int qid, +void *buf,size_t al, +u16 transaction,u8 expected_msg ) +{ +struct nvme_command cmd = {}; +struct nvmf_auth_dhchap_failure_data *data = buf; +blk_mq_req_flags_t flags = qid ==NVME_QID_ANY ? +0 :BLK_MQ_REQ_NOWAIT |BLK_MQ_REQ_RESERVED ; +struct request_queue *q = qid ==NVME_QID_ANY ? + ctrl->fabrics_q : ctrl->connect_q; +int ret; + + cmd.auth_receive.opcode = nvme_fabrics_command; + cmd.auth_receive.fctype = nvme_fabrics_type_auth_receive; + cmd.auth_receive.secp =NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER ; + cmd.auth_receive.spsp0 =0x01 ; + cmd.auth_receive.spsp1 =0x01 ; + cmd.auth_receive.al = al; + + ret =__nvme_submit_sync_cmd (q, &cmd,NULL , buf, al,0 , qid, +0 , flags); +if (ret >0 ) { +dev_dbg (ctrl->device,"%s: qid %d nvme status %x\n" , + __func__, qid, ret); + ret = -EIO ; + } +if (ret <0 ) { +dev_dbg (ctrl->device,"%s: qid %d error %d\n" , + __func__, qid, ret); +return ret; + } +dev_dbg (ctrl->device,"%s: qid %d auth_type %d auth_id %x\n" , + __func__, qid, data->auth_type, data->auth_id); +if (data->auth_type ==NVME_AUTH_COMMON_MESSAGES && + data->auth_id ==NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ) { +return data->reason_code_explanation; + } +if (data->auth_type !=NVME_AUTH_DHCHAP_MESSAGES || + data->auth_id != expected_msg) { +dev_warn (ctrl->device, +"qid %d invalid message %02x/%02x\n" , + qid, data->auth_type, data->auth_id); +return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; + } +if (le16_to_cpu (data->t_id) != transaction) { +dev_warn (ctrl->device, +"qid %d invalid transaction ID %d\n" , + qid,le16_to_cpu (data->t_id)); +return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; + } + +return 0 ; +} + +static int nvme_auth_dhchap_negotiate (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size) +{ +struct nvmf_auth_dhchap_negotiate_data *data = buf; +size_t size =sizeof (*data) +sizeof (union nvmf_auth_protocol ); + +if (buf_size < size) +return -EINVAL ; + +memset ((u8 *)buf,0 , size); + 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 */ + data->napd =1 ; + data->auth_protocol[0 ].dhchap.authid =NVME_AUTH_DHCHAP_AUTH_ID ; + data->auth_protocol[0 ].dhchap.halen =3 ; + data->auth_protocol[0 ].dhchap.dhlen =1 ; + data->auth_protocol[0 ].dhchap.idlist[0 ] =NVME_AUTH_DHCHAP_HASH_SHA256 ; + data->auth_protocol[0 ].dhchap.idlist[1 ] =NVME_AUTH_DHCHAP_HASH_SHA384 ; + data->auth_protocol[0 ].dhchap.idlist[2 ] =NVME_AUTH_DHCHAP_HASH_SHA512 ; + data->auth_protocol[0 ].dhchap.idlist[3 ] =NVME_AUTH_DHCHAP_DHGROUP_NULL ; + +return size; +} + +static int nvme_auth_dhchap_challenge (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size) +{ +struct nvmf_auth_dhchap_challenge_data *data = buf; +size_t size =sizeof (*data) + data->hl + data->dhvlen; +const char *gid_name; + +if (buf_size < size) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; +return -ENOMSG ; + } + +if (data->hashid !=NVME_AUTH_DHCHAP_HASH_SHA256 && + data->hashid !=NVME_AUTH_DHCHAP_HASH_SHA384 && + data->hashid !=NVME_AUTH_DHCHAP_HASH_SHA512 ) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: invalid HASH ID %d\n" , + chap->qid, data->hashid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ; +return -EPROTO ; + } +switch (data->dhgid) { +case NVME_AUTH_DHCHAP_DHGROUP_NULL : + gid_name ="null" ; +break ; +default : + gid_name =NULL ; +break ; + } +if (!gid_name) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: invalid DH group id %d\n" , + chap->qid, data->dhgid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; +return -EPROTO ; + } +if (data->dhgid !=NVME_AUTH_DHCHAP_DHGROUP_NULL ) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; +return -EPROTO ; + } +if (data->dhgid ==NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen !=0 ) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; +return -EPROTO ; + } +dev_dbg (ctrl->device,"%s: qid %d requested hash id %d\n" , + __func__, chap->qid, data->hashid); +if (nvme_auth_hmac_len (data->hashid) != data->hl) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: invalid hash length\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ; +return -EPROTO ; + } + chap->hash_id = data->hashid; + chap->hash_len = data->hl; + chap->s1 =le32_to_cpu (data->seqnum); +memcpy (chap->c1, data->cval, chap->hash_len); + +return 0 ; +} + +static int nvme_auth_dhchap_reply (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size) +{
Quoted 9 lines
drivers/nvme/host/auth.c line 329
Quoted 6 lines
drivers/nvme/host/auth.c new file +6
-0+struct nvmf_auth_dhchap_reply_data *data = buf; +size_t size =sizeof (*data); + + size +=2 * chap->hash_len; +if (ctrl->opts->dhchap_auth) { +get_random_bytes (chap->c2, chap->hash_len);Why are you using
CRYPTO_RNG_DEFAULTwhen you are usingget_random_byteshere?
Errm ... do I?
Seems that my crypto ignorance is showing here; 'get_random_bytes()' is the usual function we're using for drivers; if there is another way for crypto please enlighten me.
Quoted 326 lines
Quoted 159 lines
drivers/nvme/host/auth.c new file +159
-0+ chap->s2 = nvme_dhchap_seqnum++; + }else +memset (chap->c2,0 , chap->hash_len); + +if (chap->host_key_len) + size += chap->host_key_len; + +if (buf_size < size) +return -EINVAL ; + +memset (buf,0 , size); + data->auth_type =NVME_AUTH_DHCHAP_MESSAGES ; + data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_REPLY ; + data->t_id =cpu_to_le16 (chap->transaction); + data->hl = chap->hash_len; + data->dhvlen = chap->host_key_len; + data->seqnum =cpu_to_le32 (chap->s2); +memcpy (data->rval, chap->response, chap->hash_len); +if (ctrl->opts->dhchap_auth) { +dev_dbg (ctrl->device,"%s: qid %d ctrl challenge %*ph\n" , + __func__, chap->qid, + chap->hash_len, chap->c2); + data->cvalid =1 ; +memcpy (data->rval + chap->hash_len, chap->c2, + chap->hash_len); + } +if (chap->host_key_len) +memcpy (data->rval +2 * chap->hash_len, chap->host_key, + chap->host_key_len); + +return size; +} + +static int nvme_auth_dhchap_success1 (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size) +{ +struct nvmf_auth_dhchap_success1_data *data = buf; +size_t size =sizeof (*data); + +if (ctrl->opts->dhchap_auth) + size += chap->hash_len; + + +if (buf_size < size) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; +return -ENOMSG ; + } + +if (data->hl != chap->hash_len) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: invalid hash length %d\n" , + chap->qid, data->hl); + chap->status =NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ; +return -EPROTO ; + } + +if (!data->rvalid) +return 0 ; + +/* Validate controller response */ +if (memcmp (chap->response, data->rval, data->hl)) { +dev_dbg (ctrl->device,"%s: qid %d ctrl response %*ph\n" , + __func__, chap->qid, chap->hash_len, data->rval); +dev_dbg (ctrl->device,"%s: qid %d host response %*ph\n" , + __func__, chap->qid, chap->hash_len, chap->response); +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: controller authentication failed\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; +return -EPROTO ; + } +dev_info (ctrl->device, +"qid %d: DH-HMAC-CHAP: controller authenticated\n" , + chap->qid); +return 0 ; +} + +static int nvme_auth_dhchap_success2 (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size) +{ +struct nvmf_auth_dhchap_success2_data *data = buf; +size_t size =sizeof (*data); + +memset (buf,0 , size); + data->auth_type =NVME_AUTH_DHCHAP_MESSAGES ; + data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 ; + data->t_id =cpu_to_le16 (chap->transaction); + +return size; +} + +static int nvme_auth_dhchap_failure2 (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap, +void *buf,size_t buf_size) +{ +struct nvmf_auth_dhchap_failure_data *data = buf; +size_t size =sizeof (*data); + +memset (buf,0 , size); + data->auth_type =NVME_AUTH_DHCHAP_MESSAGES ; + data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 ; + data->t_id =cpu_to_le16 (chap->transaction); + data->reason_code =1 ; + data->reason_code_explanation = chap->status; + +return size; +} + +int nvme_auth_select_hash (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap) +{ +char *hash_name; +int ret; + +switch (chap->hash_id) { +case NVME_AUTH_DHCHAP_HASH_SHA256 : + hash_name ="hmac(sha256)" ; +break ; +case NVME_AUTH_DHCHAP_HASH_SHA384 : + hash_name ="hmac(sha384)" ; +break ; +case NVME_AUTH_DHCHAP_HASH_SHA512 : + hash_name ="hmac(sha512)" ; +break ; +default : + hash_name =NULL ; +break ; + } +if (!hash_name) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +return -EPROTO ; + } + chap->shash_tfm =crypto_alloc_shash (hash_name,0 , +CRYPTO_ALG_ALLOCATES_MEMORY ); +if (IS_ERR (chap->shash_tfm)) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; + chap->shash_tfm =NULL ; +return -EPROTO ; + } +if (!chap->key) { +dev_warn (ctrl->device,"qid %d: cannot select hash, no key\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->shash_tfm); + chap->shash_tfm =NULL ; +return -EINVAL ; + } + ret =crypto_shash_setkey (chap->shash_tfm, chap->key, chap->key_len); +if (ret) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->shash_tfm); + chap->shash_tfm =NULL ; +return ret; + } +dev_info (ctrl->device,"qid %d: DH-HMAC_CHAP: selected hash %s\n" , + chap->qid, hash_name); +return 0 ;drivers/nvme/host/auth.c
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+} + +static int nvme_auth_dhchap_host_response (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap) +{ +SHASH_DESC_ON_STACK (shash, chap->shash_tfm); +u8 buf[4 ], *challenge = chap->c1; +int ret; + + dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction%d\n",
Quoted 37 lines
drivers/nvme/host/auth.c new file +37
-0+ __func__, chap->qid, chap->s1, chap->transaction); + shash->tfm = chap->shash_tfm; + ret =crypto_shash_init (shash); +if (ret) + goto out; + ret =crypto_shash_update (shash, challenge, chap->hash_len); +if (ret) + goto out; +put_unaligned_le32 (chap->s1, buf); + ret =crypto_shash_update (shash, buf,4 ); +if (ret) + goto out; +put_unaligned_le16 (chap->transaction, buf); + ret =crypto_shash_update (shash, buf,2 ); +if (ret) + goto out; +memset (buf,0 ,sizeof (buf)); + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash,"HostHost" ,8 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->opts->host->nqn, +strlen (ctrl->opts->host->nqn)); +if (ret) + goto out; + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->opts->subsysnqn, +strlen (ctrl->opts->subsysnqn)); +if (ret) + goto out; + ret =crypto_shash_final (shash, chap->response); +out: +return ret;drivers/nvme/host/auth.c
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+} + +static int nvme_auth_dhchap_ctrl_response (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap) +{ +SHASH_DESC_ON_STACK (shash, chap->shash_tfm); +u8 buf[4 ], *challenge = chap->c2; +int ret; + + dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction%d\n",
Quoted 108 lines
drivers/nvme/host/auth.c new file +108
-0+ __func__, chap->qid, chap->s2, chap->transaction); +dev_dbg (ctrl->device,"%s: qid %d challenge %*ph\n" , + __func__, chap->qid, chap->hash_len, challenge); +dev_dbg (ctrl->device,"%s: qid %d subsysnqn %s\n" , + __func__, chap->qid, ctrl->opts->subsysnqn); +dev_dbg (ctrl->device,"%s: qid %d hostnqn %s\n" , + __func__, chap->qid, ctrl->opts->host->nqn); + shash->tfm = chap->shash_tfm; + ret =crypto_shash_init (shash); +if (ret) + goto out; + ret =crypto_shash_update (shash, challenge, chap->hash_len); +if (ret) + goto out; +put_unaligned_le32 (chap->s2, buf); + ret =crypto_shash_update (shash, buf,4 ); +if (ret) + goto out; +put_unaligned_le16 (chap->transaction, buf); + ret =crypto_shash_update (shash, buf,2 ); +if (ret) + goto out; +memset (buf,0 ,4 ); + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash,"Controller" ,10 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->opts->subsysnqn, +strlen (ctrl->opts->subsysnqn)); +if (ret) + goto out; + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->opts->host->nqn, +strlen (ctrl->opts->host->nqn)); +if (ret) + goto out; + ret =crypto_shash_final (shash, chap->response); +out: +return ret; +} + +int nvme_auth_generate_key (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap) +{ +int ret; +u8 key_hash; +const char *hmac_name; +struct crypto_shash *key_tfm; + +if (sscanf (ctrl->opts->dhchap_secret,"DHHC-1:%hhd:%*s:" , + &key_hash) !=1 ) +return -EINVAL ; + + chap->key =nvme_auth_extract_secret (ctrl->opts->dhchap_secret, + &chap->key_len); +if (IS_ERR (chap->key)) { + ret =PTR_ERR (chap->key); + chap->key =NULL ; +return ret; + } + +if (key_hash ==0 ) +return 0 ; + + hmac_name =nvme_auth_hmac_name (key_hash); +if (!hmac_name) { +pr_debug ("Invalid key hash id %d\n" , key_hash); +return -EKEYREJECTED ; + } + + key_tfm =crypto_alloc_shash (hmac_name,0 ,0 ); +if (IS_ERR (key_tfm)) { +kfree (chap->key); + chap->key =NULL ; + ret =PTR_ERR (key_tfm); + }else { +SHASH_DESC_ON_STACK (shash, key_tfm); + + shash->tfm = key_tfm; + ret =crypto_shash_setkey (key_tfm, chap->key, + chap->key_len); +if (ret <0 ) { +crypto_free_shash (key_tfm); +kfree (chap->key); + chap->key =NULL ; +return ret; + } +crypto_shash_init (shash); +crypto_shash_update (shash, ctrl->opts->host->nqn, +strlen (ctrl->opts->host->nqn)); +crypto_shash_update (shash,"NVMe-over-Fabrics" ,17 ); +crypto_shash_final (shash, chap->key); +crypto_free_shash (key_tfm); + } +return 0 ; +} + +void nvme_auth_free (struct nvme_dhchap_context *chap) +{ +if (chap->shash_tfm) +crypto_free_shash (chap->shash_tfm); +if (chap->key) +kfree (chap->key); +if (chap->ctrl_key)
Quoted 9 lines
drivers/nvme/host/auth.c line 659
Quoted 6 lines
drivers/nvme/host/auth.c new file +6
-0+kfree (chap->ctrl_key); +if (chap->host_key) +kfree (chap->host_key); +if (chap->sess_key) +kfree (chap->sess_key); +kfree (chap);
kfree_sensitivein all cases as all buffers have sensitive data?
Yes, will be fixing it up.
Quoted 5 lines
Quoted 5 lines
drivers/nvme/host/auth.c new file +5
-0+} + +int nvme_auth_negotiate (struct nvme_ctrl *ctrl,int qid) +{ +struct nvme_dhchap_context *chap;
Quoted 9 lines
drivers/nvme/host/auth.c line 669
Quoted 5 lines
drivers/nvme/host/auth.c new file +5
-0+void *buf; +size_t buf_size, tl; +int ret =0 ; + + chap =kzalloc (sizeof (*chap),GFP_KERNEL );Suggestion: make sure that
chap->responseis aligned toCRYPTO_MINALIGN_ATTR- then you would save a memcpy incrypto_shash_final
Ok.
Cheers,
Hannes
Signature · 4 lines
Dr. Hannes Reinecke Kernel Storage Architect [email protected] +49 911 74053 688 SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
Re: [PATCH 06/11] nvme: Implement In-Band authentication
Am Sonntag, 18. Juli 2021, 14:43:43 CEST schrieb Hannes Reinecke:
Hi Hannes,
Quoted 10 lines
Quoted 6 lines
drivers/nvme/host/auth.c line 329
Quoted 3 lines
drivers/nvme/host/auth.c new file +3
-0+ size +=2 * chap->hash_len; +if (ctrl->opts->dhchap_auth) { +get_random_bytes (chap->c2, chap->hash_len);Why are you using
CRYPTO_RNG_DEFAULTwhen you are usingget_random_byteshere?Errm ... do I?
Seems that my crypto ignorance is showing here; 'get_random_bytes()' is the usual function we're using for drivers; if there is another way for crypto please enlighten me.
Apologies, I was looking at CONFIG_RNG where you set CRYPTO_RNG_DEFAULT. Please ignore my comment here.
Ciao
Stephan
Re: [PATCH 06/11] nvme: Implement In-Band authentication
In reply to Hannes ReineckeOn 7/16/21 2:04 PM, Hannes Reinecke wrote:
[...]
Quoted 6 lines
drivers/nvme/host/auth.c new file +6
-0+struct nvmet_dhchap_hash_map { +int id; +int hash_len; +const char hmac[15 ]; +const char digest[15 ]; +} hash_map[] = {
drivers/nvme/host/auth.c line 54
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ {.id = NVME_AUTH_DHCHAP_HASH_SHA256, + .hash_len = 32, + .hmac = "hmac(sha256)", .digest = "sha256" }, + {.id = NVME_AUTH_DHCHAP_HASH_SHA384, + .hash_len = 48, + .hmac = "hmac(sha384)", .digest = "sha384" }, + {.id = NVME_AUTH_DHCHAP_HASH_SHA512, + .hash_len = 64, + .hmac = "hmac(sha512)", .digest = "sha512" }, +};
"hmac()" is always here, so why not to just auto-generate hmac(sha512) from sha512?
Vlad
_______________________________________________
Linux-nvme mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-nvme
Hannes Reinecke
maintainerRe: [PATCH 06/11] nvme: Implement In-Band authentication
Quoted 16 lines · Vladislav Bolkhovitin
On 7/16/21 2:04 PM, Hannes Reinecke wrote:
[...]
Quoted 11 lines
drivers/nvme/host/auth.c new file +11
-0+struct nvmet_dhchap_hash_map { +int id; +int hash_len; +const char hmac[15 ]; +const char digest[15 ]; +} hash_map[] = { + {.id =NVME_AUTH_DHCHAP_HASH_SHA256 , + .hash_len =32 , + .hmac ="hmac(sha256)" , .digest ="sha256" }, + {.id =NVME_AUTH_DHCHAP_HASH_SHA384 , + .hash_len =48 ,
Quoted 9 lines
drivers/nvme/host/auth.c line 54
Quoted 5 lines
drivers/nvme/host/auth.c new file +5
-0+ .hmac = "hmac(sha384)", .digest = "sha384" }, + {.id = NVME_AUTH_DHCHAP_HASH_SHA512, + .hash_len = 64, + .hmac = "hmac(sha512)", .digest = "sha512" }, +};"
hmac()" is always here, so why not to just auto-generate hmac(sha512) from sha512?
... all part of the learning curve ...
If that's true then of course I can auto-generate the hmac name.
Cheers,
Hannes
Signature · 9 lines
Dr. Hannes Reinecke Kernel Storage Architect [email protected] +49 911 74053 688 SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg HRB 36809 (AG Nürnberg), GF: Felix Imendörffer _______________________________________________ Linux-nvme mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-nvme
Re: [PATCH 06/11] nvme: Implement In-Band authentication
Quoted 27 lines · Hannes Reinecke
Quoted 16 lines · Vladislav Bolkhovitin
On 7/16/21 2:04 PM, Hannes Reinecke wrote:
[...]
Quoted 11 lines
drivers/nvme/host/auth.c new file +11
-0+struct nvmet_dhchap_hash_map { +int id; +int hash_len; +const char hmac[15 ]; +const char digest[15 ]; +} hash_map[] = { + {.id =NVME_AUTH_DHCHAP_HASH_SHA256 , + .hash_len =32 , + .hmac ="hmac(sha256)" , .digest ="sha256" }, + {.id =NVME_AUTH_DHCHAP_HASH_SHA384 , + .hash_len =48 ,Quoted 9 lines
drivers/nvme/host/auth.c line 54
Quoted 5 lines
drivers/nvme/host/auth.c new file +5
-0+ .hmac = "hmac(sha384)", .digest = "sha384" }, + {.id = NVME_AUTH_DHCHAP_HASH_SHA512, + .hash_len = 64, + .hmac = "hmac(sha512)", .digest = "sha512" }, +};"
hmac()" is always here, so why not to just auto-generate hmac(sha512) from sha512?
Quoted 2 lines
... all part of the learning curve ...
If that's true then of course I can auto-generate the hmac name.
As far as I understand, this naming convention is as stable as any other interface in the kernel.
Vlad
Hannes Reinecke
maintainerPATCH 7/11 nvme-auth: augmented challenge support
In reply to Hannes ReineckeImplement support for augmented challenge using FFDHE groups.
--- drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++---- 1 file changed, 371 insertions(+), 32 deletions(-)
drivers/nvme/host/auth.c +371 -32
@@ -8,6 +8,8 @@#include <asm/unaligned.h> #include <crypto/hash.h> #include <crypto/kpp.h> +#include <crypto/dh.h> +#include <crypto/ffdhe.h> #include "nvme.h" #include "fabrics.h" #include "auth.h"
@@ -16,6 +18,8 @@ static u32 nvme_dhchap_seqnum;struct nvme_dhchap_context {struct crypto_shash *shash_tfm; +struct crypto_shash *digest_tfm; +struct crypto_kpp *dh_tfm;unsigned char *key;size_t key_len;int qid;
@@ -25,6 +29,8 @@ struct nvme_dhchap_context {u8 status;u8 hash_id;u8 hash_len; +u8 dhgroup_id; +u16 dhgroup_size;u8 c1[64 ];u8 c2[64 ];u8 response[64 ];
@@ -36,6 +42,94 @@ struct nvme_dhchap_context {int sess_key_len; }; +struct nvme_auth_dhgroup_map { +int id; +const char name[16 ]; +const char kpp[16 ]; +int privkey_size; +int pubkey_size; +} dhgroup_map[] = { + { .id =NVME_AUTH_DHCHAP_DHGROUP_NULL , + .name ="NULL" , .kpp ="NULL" , + .privkey_size =0 , .pubkey_size =0 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_2048 , + .name ="ffdhe2048" , .kpp ="dh" , + .privkey_size =256 , .pubkey_size =256 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_3072 , + .name ="ffdhe3072" , .kpp ="dh" , + .privkey_size =384 , .pubkey_size =384 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_4096 , + .name ="ffdhe4096" , .kpp ="dh" , + .privkey_size =512 , .pubkey_size =512 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_6144 , + .name ="ffdhe6144" , .kpp ="dh" , + .privkey_size =768 , .pubkey_size =768 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_8192 , + .name ="ffdhe8192" , .kpp ="dh" , + .privkey_size =1024 , .pubkey_size =1024 }, +}; + +const char *nvme_auth_dhgroup_name (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].name; + } +return NULL ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_name); + +int nvme_auth_dhgroup_pubkey_size (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].pubkey_size; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_pubkey_size); + +int nvme_auth_dhgroup_privkey_size (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].privkey_size; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_privkey_size); + +const char *nvme_auth_dhgroup_kpp (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].kpp; + } +return NULL ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_kpp); + +int nvme_auth_dhgroup_id (const char *dhgroup_name) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (!strncmp (dhgroup_map[i].name, dhgroup_name, +strlen (dhgroup_map[i].name))) +return dhgroup_map[i].id; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_id); +struct nvmet_dhchap_hash_map {int id;int hash_len;
@@ -243,11 +337,16 @@ static int nvme_auth_dhchap_negotiate(struct nvme_ctrl *ctrl, data->napd =1 ; data->auth_protocol[0 ].dhchap.authid =NVME_AUTH_DHCHAP_AUTH_ID ; data->auth_protocol[0 ].dhchap.halen =3 ; - data->auth_protocol[0 ].dhchap.dhlen =1 ; + data->auth_protocol[0 ].dhchap.dhlen =6 ; data->auth_protocol[0 ].dhchap.idlist[0 ] =NVME_AUTH_DHCHAP_HASH_SHA256 ; data->auth_protocol[0 ].dhchap.idlist[1 ] =NVME_AUTH_DHCHAP_HASH_SHA384 ; data->auth_protocol[0 ].dhchap.idlist[2 ] =NVME_AUTH_DHCHAP_HASH_SHA512 ; data->auth_protocol[0 ].dhchap.idlist[3 ] =NVME_AUTH_DHCHAP_DHGROUP_NULL ; + data->auth_protocol[0 ].dhchap.idlist[4 ] =NVME_AUTH_DHCHAP_DHGROUP_2048 ; + data->auth_protocol[0 ].dhchap.idlist[5 ] =NVME_AUTH_DHCHAP_DHGROUP_3072 ; + data->auth_protocol[0 ].dhchap.idlist[6 ] =NVME_AUTH_DHCHAP_DHGROUP_4096 ; + data->auth_protocol[0 ].dhchap.idlist[7 ] =NVME_AUTH_DHCHAP_DHGROUP_6144 ; + data->auth_protocol[0 ].dhchap.idlist[8 ] =NVME_AUTH_DHCHAP_DHGROUP_8192 ;return size; }
@@ -274,14 +373,7 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl, chap->status =NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ;return -EPROTO ; } -switch (data->dhgid) { -case NVME_AUTH_DHCHAP_DHGROUP_NULL : - gid_name ="null" ; -break ; -default : - gid_name =NULL ; -break ; - } + gid_name =nvme_auth_dhgroup_kpp (data->dhgid);if (!gid_name) { dev_warn(ctrl->device, "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n",
@@ -290,10 +382,24 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl,return -EPROTO ; }if (data->dhgid !=NVME_AUTH_DHCHAP_DHGROUP_NULL ) { - chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; -return -EPROTO ; - } -if (data->dhgid ==NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen !=0 ) { +if (data->dhvlen ==0 ) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: empty DH value\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; +return -EPROTO ; + } + chap->dh_tfm =crypto_alloc_kpp (gid_name,0 ,0 ); +if (IS_ERR (chap->dh_tfm)) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: failed to initialize %s\n" , + chap->qid, gid_name); + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; + chap->dh_tfm =NULL ; +return -EPROTO ; + } + chap->dhgroup_id = data->dhgid; + }else if (data->dhvlen !=0 ) {dev_warn (ctrl->device,"qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n" , chap->qid);
@@ -313,6 +419,16 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl, chap->hash_len = data->hl; chap->s1 =le32_to_cpu (data->seqnum);memcpy (chap->c1, data->cval, chap->hash_len); +if (data->dhvlen) { + chap->ctrl_key =kmalloc (data->dhvlen,GFP_KERNEL ); +if (!chap->ctrl_key) +return -ENOMEM ; + chap->ctrl_key_len = data->dhvlen; +memcpy (chap->ctrl_key, data->cval + chap->hash_len, + data->dhvlen); +dev_dbg (ctrl->device,"ctrl public key %*ph\n" , + (int )chap->ctrl_key_len, chap->ctrl_key); + }return 0 ; }
@@ -353,10 +469,13 @@ static int nvme_auth_dhchap_reply(struct nvme_ctrl *ctrl,memcpy (data->rval + chap->hash_len, chap->c2, chap->hash_len); } -if (chap->host_key_len) +if (chap->host_key_len) { +dev_dbg (ctrl->device,"%s: qid %d host public key %*ph\n" , + __func__, chap->qid, + chap->host_key_len, chap->host_key);memcpy (data->rval +2 * chap->hash_len, chap->host_key, chap->host_key_len); - + }return size; }
@@ -440,23 +559,10 @@ static int nvme_auth_dhchap_failure2(struct nvme_ctrl *ctrl,int nvme_auth_select_hash (struct nvme_ctrl *ctrl,struct nvme_dhchap_context *chap) { -char *hash_name; +const char *hash_name, *digest_name;int ret; -switch (chap->hash_id) { -case NVME_AUTH_DHCHAP_HASH_SHA256 : - hash_name ="hmac(sha256)" ; -break ; -case NVME_AUTH_DHCHAP_HASH_SHA384 : - hash_name ="hmac(sha384)" ; -break ; -case NVME_AUTH_DHCHAP_HASH_SHA512 : - hash_name ="hmac(sha512)" ; -break ; -default : - hash_name =NULL ; -break ; - } + hash_name =nvme_auth_hmac_name (chap->hash_id);if (!hash_name) { chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ;return -EPROTO ;
@@ -468,26 +574,100 @@ int nvme_auth_select_hash(struct nvme_ctrl *ctrl, chap->shash_tfm =NULL ;return -EPROTO ; } + digest_name =nvme_auth_digest_name (chap->hash_id); +if (!digest_name) { +crypto_free_shash (chap->shash_tfm); + chap->shash_tfm =NULL ; +return -EPROTO ; + } + chap->digest_tfm =crypto_alloc_shash (digest_name,0 ,0 ); +if (IS_ERR (chap->digest_tfm)) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->shash_tfm); + chap->shash_tfm =NULL ; + chap->digest_tfm =NULL ; +return -EPROTO ; + }if (!chap->key) {dev_warn (ctrl->device,"qid %d: cannot select hash, no key\n" , chap->qid); chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->digest_tfm);crypto_free_shash (chap->shash_tfm); chap->shash_tfm =NULL ; + chap->digest_tfm =NULL ;return -EINVAL ; } ret =crypto_shash_setkey (chap->shash_tfm, chap->key, chap->key_len);if (ret) { chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->digest_tfm);crypto_free_shash (chap->shash_tfm); chap->shash_tfm =NULL ; + chap->digest_tfm =NULL ;return ret; } -dev_info (ctrl->device,"qid %d: DH-HMAC_CHAP: selected hash %s\n" , - chap->qid, hash_name); +dev_dbg (ctrl->device,"qid %d: DH-HMAC_CHAP: selected hash %s\n" , + chap->qid, hash_name);return 0 ; } +static int nvme_auth_augmented_challenge (struct nvme_dhchap_context *chap, +u8 *challenge,u8 *aug) +{ +struct crypto_shash *tfm; +struct shash_desc *desc; +u8 *hashed_key; +const char *hash_name; +int ret; + + hashed_key =kmalloc (chap->hash_len,GFP_KERNEL ); +if (!hashed_key) +return -ENOMEM ; + + ret =crypto_shash_tfm_digest (chap->digest_tfm, chap->sess_key, + chap->sess_key_len, hashed_key); +if (ret <0 ) { +pr_debug ("failed to hash session key, err %d\n" , ret); +kfree (hashed_key); +return ret; + } + hash_name =crypto_shash_alg_name (chap->shash_tfm); +if (!hash_name) { +pr_debug ("Invalid hash algoritm\n" ); +return -EINVAL ; + } + tfm =crypto_alloc_shash (hash_name,0 ,0 ); +if (IS_ERR (tfm)) { + ret =PTR_ERR (tfm); + goto out_free_key; + } + desc =kmalloc (sizeof (struct shash_desc ) +crypto_shash_descsize (tfm), +GFP_KERNEL ); +if (!desc) { + ret = -ENOMEM ; + goto out_free_hash; + } + desc->tfm = tfm; + + ret =crypto_shash_setkey (tfm, hashed_key, chap->hash_len); +if (ret) + goto out_free_desc; + ret =crypto_shash_init (desc); +if (ret) + goto out_free_desc; +crypto_shash_update (desc, challenge, chap->hash_len); +crypto_shash_final (desc, aug); + +out_free_desc: +kfree_sensitive (desc); +out_free_hash: +crypto_free_shash (tfm); +out_free_key: +kfree (hashed_key); +return ret; +} +static int nvme_auth_dhchap_host_response (struct nvme_ctrl *ctrl,struct nvme_dhchap_context *chap) {
@@ -497,6 +677,16 @@ static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,dev_dbg (ctrl->device,"%s: qid %d host response seq %d transaction %d\n" , __func__, chap->qid, chap->s1, chap->transaction); +if (chap->dh_tfm) { + challenge =kmalloc (chap->hash_len,GFP_KERNEL ); +if (!challenge) { + ret = -ENOMEM ; + goto out; + } + ret =nvme_auth_augmented_challenge (chap, chap->c1, challenge); +if (ret) + goto out; + } shash->tfm = chap->shash_tfm; ret =crypto_shash_init (shash);if (ret)
@@ -532,6 +722,8 @@ static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl, goto out; ret =crypto_shash_final (shash, chap->response); out: +if (challenge != chap->c1) +kfree (challenge);return ret; }
@@ -542,6 +734,17 @@ static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl,u8 buf[4 ], *challenge = chap->c2;int ret; +if (chap->dh_tfm) { + challenge =kmalloc (chap->hash_len,GFP_KERNEL ); +if (!challenge) { + ret = -ENOMEM ; + goto out; + } + ret =nvme_auth_augmented_challenge (chap, chap->c2, + challenge); +if (ret) + goto out; + }dev_dbg (ctrl->device,"%s: qid %d host response seq %d transaction %d\n" , __func__, chap->qid, chap->s2, chap->transaction); dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n",
@@ -585,6 +788,8 @@ static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl, goto out; ret =crypto_shash_final (shash, chap->response); out: +if (challenge != chap->c2) +kfree (challenge);return ret; }
@@ -644,10 +849,134 @@ int nvme_auth_generate_key(struct nvme_ctrl *ctrl,return 0 ; } +static int nvme_auth_dhchap_exponential (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap) +{ +struct kpp_request *req; +struct crypto_wait wait; +struct scatterlist src, dst; +u8 *pkey; +int ret, pkey_len; + +if (chap->dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_2048 || + chap->dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_3072 || + chap->dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_4096 || + chap->dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_6144 || + chap->dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_8192 ) { +struct dh p = {0 }; +int pubkey_size =nvme_auth_dhgroup_pubkey_size (chap->dhgroup_id); + + ret =crypto_ffdhe_params (&p, pubkey_size <<3 ); +if (ret) { +dev_dbg (ctrl->device, +"failed to generate ffdhe params, error %d\n" , + ret); +return ret; + } + p.key = chap->key; + p.key_size = chap->key_len; + + pkey_len =crypto_dh_key_len (&p); + pkey =kzalloc (pkey_len,GFP_KERNEL ); + +get_random_bytes (pkey, pkey_len); + ret =crypto_dh_encode_key (pkey, pkey_len, &p); +if (ret) { +dev_dbg (ctrl->device, +"failed to encode pkey, error %d\n" , ret); +kfree (pkey); +return ret; + } + chap->host_key_len = pubkey_size; + chap->sess_key_len = pubkey_size; + }else { +dev_warn (ctrl->device,"Invalid DH group id %d\n" , + chap->dhgroup_id); + chap->status =NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; +return -EINVAL ; + } + + ret =crypto_kpp_set_secret (chap->dh_tfm, pkey, pkey_len); +if (ret) { +dev_dbg (ctrl->dev,"failed to set secret, error %d\n" , ret); +kfree (pkey); +return ret; + } + req =kpp_request_alloc (chap->dh_tfm,GFP_KERNEL ); +if (!req) { + ret = -ENOMEM ; + goto out_free_exp; + } + + chap->host_key =kzalloc (chap->host_key_len,GFP_KERNEL ); +if (!chap->host_key) { + ret = -ENOMEM ; + goto out_free_req; + } +crypto_init_wait (&wait); +kpp_request_set_input (req,NULL ,0 ); +sg_init_one (&dst, chap->host_key, chap->host_key_len); +kpp_request_set_output (req, &dst, chap->host_key_len); +kpp_request_set_callback (req,CRYPTO_TFM_REQ_MAY_BACKLOG , + crypto_req_done, &wait); + + ret =crypto_wait_req (crypto_kpp_generate_public_key (req), &wait); +if (ret == -EOVERFLOW ) { +dev_dbg (ctrl->dev, +"public key buffer too small, wants %d is %d\n" , +crypto_kpp_maxsize (chap->dh_tfm), chap->host_key_len); + goto out_free_host; + }else if (ret) { +dev_dbg (ctrl->dev, +"failed to generate public key, error %d\n" , ret); + goto out_free_host; + } + + chap->sess_key =kmalloc (chap->sess_key_len,GFP_KERNEL ); +if (!chap->sess_key) + goto out_free_host; + +crypto_init_wait (&wait); +sg_init_one (&src, chap->ctrl_key, chap->ctrl_key_len); +kpp_request_set_input (req, &src, chap->ctrl_key_len); +sg_init_one (&dst, chap->sess_key, chap->sess_key_len); +kpp_request_set_output (req, &dst, chap->sess_key_len); +kpp_request_set_callback (req,CRYPTO_TFM_REQ_MAY_BACKLOG , + crypto_req_done, &wait); + + ret =crypto_wait_req (crypto_kpp_compute_shared_secret (req), &wait); +if (ret) { +dev_dbg (ctrl->dev, +"failed to generate shared secret, error %d\n" , ret); +kfree_sensitive (chap->sess_key); + chap->sess_key =NULL ; + chap->sess_key_len =0 ; + }else +dev_dbg (ctrl->dev,"shared secret %*ph\n" , + (int )chap->sess_key_len, chap->sess_key); +out_free_host: +if (ret) { +kfree (chap->host_key); + chap->host_key =NULL ; + chap->host_key_len =0 ; + } +out_free_req: +kpp_request_free (req); +out_free_exp: +kfree_sensitive (pkey); +if (ret) + chap->status =NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; +return ret; +} +void nvme_auth_free (struct nvme_dhchap_context *chap) {if (chap->shash_tfm)crypto_free_shash (chap->shash_tfm); +if (chap->digest_tfm) +crypto_free_shash (chap->digest_tfm); +if (chap->dh_tfm) +crypto_free_kpp (chap->dh_tfm);if (chap->key)kfree (chap->key);if (chap->ctrl_key)
@@ -732,6 +1061,15 @@ int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)if (ret) goto fail2; +if (chap->ctrl_key_len) { +dev_dbg (ctrl->device, +"%s: qid %d DH-HMAC-DHAP DH exponential\n" , + __func__, qid); + ret =nvme_auth_dhchap_exponential (ctrl, chap); +if (ret) + goto fail2; + } +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP host response\n" , __func__, qid); ret =nvme_auth_dhchap_host_response (ctrl, chap);
@@ -806,6 +1144,7 @@ int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid) ret = -EPROTO ;if (!ret) { ctrl->dhchap_hash = chap->hash_id; + ctrl->dhchap_dhgroup = chap->dhgroup_id; }kfree (buf);nvme_auth_free (chap);
Signature · 1 line
2.29.2
Re: [PATCH 07/11] nvme-auth: augmented challenge support
Am Freitag, 16. Juli 2021, 13:04:24 CEST schrieb Hannes Reinecke:
Hi Hannes,
Quoted 359 lines
Implement support for augmented challenge using FFDHE groups.
Signed-off-by Hannes Reinecke <[email protected]>--- drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++---- 1 file changed, 371 insertions(+), 32 deletions(-)drivers/nvme/host/auth.c +212
-32@@ -8,6 +8,8 @@#include <asm/unaligned.h> #include <crypto/hash.h> #include <crypto/kpp.h> +#include <crypto/dh.h> +#include <crypto/ffdhe.h> #include "nvme.h" #include "fabrics.h" #include "auth.h" @@ -16,6 +18,8 @@ static u32 nvme_dhchap_seqnum;struct nvme_dhchap_context {struct crypto_shash *shash_tfm; +struct crypto_shash *digest_tfm; +struct crypto_kpp *dh_tfm;unsigned char *key;size_t key_len;int qid;@@ -25,6 +29,8 @@ struct nvme_dhchap_context {u8 status;u8 hash_id;u8 hash_len; +u8 dhgroup_id; +u16 dhgroup_size;u8 c1[64 ];u8 c2[64 ];u8 response[64 ];@@ -36,6 +42,94 @@ struct nvme_dhchap_context {int sess_key_len; }; +struct nvme_auth_dhgroup_map { +int id; +const char name[16 ]; +const char kpp[16 ]; +int privkey_size; +int pubkey_size; +} dhgroup_map[] = { + { .id =NVME_AUTH_DHCHAP_DHGROUP_NULL , + .name ="NULL" , .kpp ="NULL" , + .privkey_size =0 , .pubkey_size =0 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_2048 , + .name ="ffdhe2048" , .kpp ="dh" , + .privkey_size =256 , .pubkey_size =256 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_3072 , + .name ="ffdhe3072" , .kpp ="dh" , + .privkey_size =384 , .pubkey_size =384 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_4096 , + .name ="ffdhe4096" , .kpp ="dh" , + .privkey_size =512 , .pubkey_size =512 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_6144 , + .name ="ffdhe6144" , .kpp ="dh" , + .privkey_size =768 , .pubkey_size =768 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_8192 , + .name ="ffdhe8192" , .kpp ="dh" , + .privkey_size =1024 , .pubkey_size =1024 }, +}; + +const char *nvme_auth_dhgroup_name (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].name; + } +return NULL ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_name); + +int nvme_auth_dhgroup_pubkey_size (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].pubkey_size; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_pubkey_size); + +int nvme_auth_dhgroup_privkey_size (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].privkey_size; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_privkey_size); + +const char *nvme_auth_dhgroup_kpp (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].kpp; + } +return NULL ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_kpp); + +int nvme_auth_dhgroup_id (const char *dhgroup_name) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (!strncmp (dhgroup_map[i].name, dhgroup_name, +strlen (dhgroup_map[i].name))) +return dhgroup_map[i].id; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_id); +struct nvmet_dhchap_hash_map {int id;int hash_len;@@ -243,11 +337,16 @@ static int nvme_auth_dhchap_negotiate(struct nvme_ctrl *ctrl, data->napd =1 ; data->auth_protocol[0 ].dhchap.authid =NVME_AUTH_DHCHAP_AUTH_ID ; data->auth_protocol[0 ].dhchap.halen =3 ; - data->auth_protocol[0 ].dhchap.dhlen =1 ; + data->auth_protocol[0 ].dhchap.dhlen =6 ; data->auth_protocol[0 ].dhchap.idlist[0 ] =NVME_AUTH_DHCHAP_HASH_SHA256 ; data->auth_protocol[0 ].dhchap.idlist[1 ] =NVME_AUTH_DHCHAP_HASH_SHA384 ; data->auth_protocol[0 ].dhchap.idlist[2 ] =NVME_AUTH_DHCHAP_HASH_SHA512 ; data->auth_protocol[0 ].dhchap.idlist[3 ] =NVME_AUTH_DHCHAP_DHGROUP_NULL ; + data->auth_protocol[0 ].dhchap.idlist[4 ] =NVME_AUTH_DHCHAP_DHGROUP_2048 ; + data->auth_protocol[0 ].dhchap.idlist[5 ] =NVME_AUTH_DHCHAP_DHGROUP_3072 ; + data->auth_protocol[0 ].dhchap.idlist[6 ] =NVME_AUTH_DHCHAP_DHGROUP_4096 ; + data->auth_protocol[0 ].dhchap.idlist[7 ] =NVME_AUTH_DHCHAP_DHGROUP_6144 ; + data->auth_protocol[0 ].dhchap.idlist[8 ] =NVME_AUTH_DHCHAP_DHGROUP_8192 ;return size; }@@ -274,14 +373,7 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl, chap->status =NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ;return -EPROTO ; } -switch (data->dhgid) { -case NVME_AUTH_DHCHAP_DHGROUP_NULL : - gid_name ="null" ; -break ; -default : - gid_name =NULL ; -break ; - } + gid_name =nvme_auth_dhgroup_kpp (data->dhgid);if (!gid_name) { dev_warn(ctrl->device, "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n",@@ -290,10 +382,24 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl, return -EPROTO ; }if (data->dhgid !=NVME_AUTH_DHCHAP_DHGROUP_NULL ) { - chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; -return -EPROTO ; - } -if (data->dhgid ==NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen !=0 ) { +if (data->dhvlen ==0 ) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: empty DH value\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; +return -EPROTO ; + } + chap->dh_tfm =crypto_alloc_kpp (gid_name,0 ,0 ); +if (IS_ERR (chap->dh_tfm)) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: failed to initialize %s\n" , + chap->qid, gid_name); + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; + chap->dh_tfm =NULL ; +return -EPROTO ; + } + chap->dhgroup_id = data->dhgid; + }else if (data->dhvlen !=0 ) {dev_warn (ctrl->device,"qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n" , chap->qid);@@ -313,6 +419,16 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl, chap->hash_len = data->hl; chap->s1 =le32_to_cpu (data->seqnum);memcpy (chap->c1, data->cval, chap->hash_len); +if (data->dhvlen) { + chap->ctrl_key =kmalloc (data->dhvlen,GFP_KERNEL ); +if (!chap->ctrl_key) +return -ENOMEM ; + chap->ctrl_key_len = data->dhvlen; +memcpy (chap->ctrl_key, data->cval + chap->hash_len, + data->dhvlen); +dev_dbg (ctrl->device,"ctrl public key %*ph\n" , + (int )chap->ctrl_key_len, chap->ctrl_key); + }return 0 ; }@@ -353,10 +469,13 @@ static int nvme_auth_dhchap_reply(struct nvme_ctrl *ctrl,memcpy (data->rval + chap->hash_len, chap->c2, chap->hash_len); } -if (chap->host_key_len) +if (chap->host_key_len) { +dev_dbg (ctrl->device,"%s: qid %d host public key %*ph\n" , + __func__, chap->qid, + chap->host_key_len, chap->host_key);memcpy (data->rval +2 * chap->hash_len, chap->host_key, chap->host_key_len); - + }return size; }@@ -440,23 +559,10 @@ static int nvme_auth_dhchap_failure2(struct nvme_ctrl *ctrl, int nvme_auth_select_hash(struct nvme_ctrl *ctrl,struct nvme_dhchap_context *chap) { -char *hash_name; +const char *hash_name, *digest_name;int ret; -switch (chap->hash_id) { -case NVME_AUTH_DHCHAP_HASH_SHA256 : - hash_name ="hmac(sha256)" ; -break ; -case NVME_AUTH_DHCHAP_HASH_SHA384 : - hash_name ="hmac(sha384)" ; -break ; -case NVME_AUTH_DHCHAP_HASH_SHA512 : - hash_name ="hmac(sha512)" ; -break ; -default : - hash_name =NULL ; -break ; - } + hash_name =nvme_auth_hmac_name (chap->hash_id);if (!hash_name) { chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ;return -EPROTO ;@@ -468,26 +574,100 @@ int nvme_auth_select_hash(struct nvme_ctrl *ctrl, chap->shash_tfm =NULL ;return -EPROTO ; } + digest_name =nvme_auth_digest_name (chap->hash_id); +if (!digest_name) { +crypto_free_shash (chap->shash_tfm); + chap->shash_tfm =NULL ; +return -EPROTO ; + } + chap->digest_tfm =crypto_alloc_shash (digest_name,0 ,0 ); +if (IS_ERR (chap->digest_tfm)) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->shash_tfm); + chap->shash_tfm =NULL ; + chap->digest_tfm =NULL ; +return -EPROTO ; + }if (!chap->key) {dev_warn (ctrl->device,"qid %d: cannot select hash, no key\n" , chap->qid); chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->digest_tfm);crypto_free_shash (chap->shash_tfm); chap->shash_tfm =NULL ; + chap->digest_tfm =NULL ;return -EINVAL ; } ret =crypto_shash_setkey (chap->shash_tfm, chap->key, chap->key_len);if (ret) { chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->digest_tfm);crypto_free_shash (chap->shash_tfm); chap->shash_tfm =NULL ; + chap->digest_tfm =NULL ;return ret; } -dev_info (ctrl->device,"qid %d: DH-HMAC_CHAP: selected hash %s\n" , - chap->qid, hash_name); +dev_dbg (ctrl->device,"qid %d: DH-HMAC_CHAP: selected hash %s\n" , + chap->qid, hash_name);return 0 ; } +static int nvme_auth_augmented_challenge (struct nvme_dhchap_context *chap, +u8 *challenge,u8 *aug) +{ +struct crypto_shash *tfm; +struct shash_desc *desc; +u8 *hashed_key; +const char *hash_name; +int ret; + + hashed_key =kmalloc (chap->hash_len,GFP_KERNEL ); +if (!hashed_key) +return -ENOMEM ; + + ret =crypto_shash_tfm_digest (chap->digest_tfm, chap->sess_key, + chap->sess_key_len, hashed_key); +if (ret <0 ) { +pr_debug ("failed to hash session key, err %d\n" , ret); +kfree (hashed_key); +return ret; + } + hash_name =crypto_shash_alg_name (chap->shash_tfm); +if (!hash_name) { +pr_debug ("Invalid hash algoritm\n" ); +return -EINVAL ; + } + tfm =crypto_alloc_shash (hash_name,0 ,0 ); +if (IS_ERR (tfm)) { + ret =PTR_ERR (tfm); + goto out_free_key; + } + desc =kmalloc (sizeof (struct shash_desc ) +crypto_shash_descsize (tfm), +GFP_KERNEL ); +if (!desc) { + ret = -ENOMEM ; + goto out_free_hash; + } + desc->tfm = tfm; + + ret =crypto_shash_setkey (tfm, hashed_key, chap->hash_len); +if (ret) + goto out_free_desc; + ret =crypto_shash_init (desc); +if (ret) + goto out_free_desc; +crypto_shash_update (desc, challenge, chap->hash_len); +crypto_shash_final (desc, aug); + +out_free_desc: +kfree_sensitive (desc); +out_free_hash: +crypto_free_shash (tfm); +out_free_key: +kfree (hashed_key); +return ret; +} +static int nvme_auth_dhchap_host_response (struct nvme_ctrl *ctrl,struct nvme_dhchap_context *chap) {
drivers/nvme/host/auth.c static int nvme_auth_dhchap_host_response(struct · line 679
Quoted 3 lines
drivers/nvme/host/auth.c +0
-0@@ -497,6 +677,16 @@ static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl, dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction
%d\n",
drivers/nvme/host/auth.c static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl, · line 681
Quoted 3 lines
drivers/nvme/host/auth.c +2
-0__func__, chap->qid, chap->s1, chap->transaction); +if (chap->dh_tfm) { + challenge =kmalloc (chap->hash_len,GFP_KERNEL );
Again, alignment?
Quoted 21 lines
drivers/nvme/host/auth.c +8
-0+if (!challenge) { + ret = -ENOMEM ; + goto out; + } + ret =nvme_auth_augmented_challenge (chap, chap->c1, challenge); +if (ret) + goto out; + } shash->tfm = chap->shash_tfm; ret =crypto_shash_init (shash);drivers/nvme/host/auth.c +2
-0if (ret)@@ -532,6 +722,8 @@ static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl, goto out; ret =crypto_shash_final (shash, chap->response); out: +if (challenge != chap->c1) +kfree (challenge);return ret; }
drivers/nvme/host/auth.c static int nvme_auth_dhchap_ctrl_response(struct · line 738
Quoted 5 lines
drivers/nvme/host/auth.c +2
-0@@ -542,6 +734,17 @@ static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl, u8 buf[4], *challenge = chap->c2;int ret; +if (chap->dh_tfm) { + challenge =kmalloc (chap->hash_len,GFP_KERNEL );
dto.
drivers/nvme/host/auth.c static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl,
Quoted 10 lines
drivers/nvme/host/auth.c +9
-0+if (!challenge) { + ret = -ENOMEM ; + goto out; + } + ret =nvme_auth_augmented_challenge (chap, chap->c2, + challenge); +if (ret) + goto out; + } dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction
%d\n",
Quoted 163 lines
drivers/nvme/host/auth.c +135
-0__func__, chap->qid, chap->s2, chap->transaction); dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n",@@ -585,6 +788,8 @@ static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl, goto out; ret =crypto_shash_final (shash, chap->response); out: +if (challenge != chap->c2) +kfree (challenge);return ret; }@@ -644,10 +849,134 @@ int nvme_auth_generate_key(struct nvme_ctrl *ctrl,return 0 ; } +static int nvme_auth_dhchap_exponential (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap) +{ +struct kpp_request *req; +struct crypto_wait wait; +struct scatterlist src, dst; +u8 *pkey; +int ret, pkey_len; + +if (chap->dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_2048 || + chap->dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_3072 || + chap->dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_4096 || + chap->dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_6144 || + chap->dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_8192 ) { +struct dh p = {0 }; +int pubkey_size =nvme_auth_dhgroup_pubkey_size (chap->dhgroup_id); + + ret =crypto_ffdhe_params (&p, pubkey_size <<3 ); +if (ret) { +dev_dbg (ctrl->device, +"failed to generate ffdhe params, error %d\n" , + ret); +return ret; + } + p.key = chap->key; + p.key_size = chap->key_len; + + pkey_len =crypto_dh_key_len (&p); + pkey =kzalloc (pkey_len,GFP_KERNEL ); + +get_random_bytes (pkey, pkey_len); + ret =crypto_dh_encode_key (pkey, pkey_len, &p); +if (ret) { +dev_dbg (ctrl->device, +"failed to encode pkey, error %d\n" , ret); +kfree (pkey); +return ret; + } + chap->host_key_len = pubkey_size; + chap->sess_key_len = pubkey_size; + }else { +dev_warn (ctrl->device,"Invalid DH group id %d\n" , + chap->dhgroup_id); + chap->status =NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; +return -EINVAL ; + } + + ret =crypto_kpp_set_secret (chap->dh_tfm, pkey, pkey_len); +if (ret) { +dev_dbg (ctrl->dev,"failed to set secret, error %d\n" , ret); +kfree (pkey); +return ret; + } + req =kpp_request_alloc (chap->dh_tfm,GFP_KERNEL ); +if (!req) { + ret = -ENOMEM ; + goto out_free_exp; + } + + chap->host_key =kzalloc (chap->host_key_len,GFP_KERNEL ); +if (!chap->host_key) { + ret = -ENOMEM ; + goto out_free_req; + } +crypto_init_wait (&wait); +kpp_request_set_input (req,NULL ,0 ); +sg_init_one (&dst, chap->host_key, chap->host_key_len); +kpp_request_set_output (req, &dst, chap->host_key_len); +kpp_request_set_callback (req,CRYPTO_TFM_REQ_MAY_BACKLOG , + crypto_req_done, &wait); + + ret =crypto_wait_req (crypto_kpp_generate_public_key (req), &wait); +if (ret == -EOVERFLOW ) { +dev_dbg (ctrl->dev, +"public key buffer too small, wants %d is %d\n" , +crypto_kpp_maxsize (chap->dh_tfm), chap->host_key_len); + goto out_free_host; + }else if (ret) { +dev_dbg (ctrl->dev, +"failed to generate public key, error %d\n" , ret); + goto out_free_host; + } + + chap->sess_key =kmalloc (chap->sess_key_len,GFP_KERNEL ); +if (!chap->sess_key) + goto out_free_host; + +crypto_init_wait (&wait); +sg_init_one (&src, chap->ctrl_key, chap->ctrl_key_len); +kpp_request_set_input (req, &src, chap->ctrl_key_len); +sg_init_one (&dst, chap->sess_key, chap->sess_key_len); +kpp_request_set_output (req, &dst, chap->sess_key_len); +kpp_request_set_callback (req,CRYPTO_TFM_REQ_MAY_BACKLOG , + crypto_req_done, &wait); + + ret =crypto_wait_req (crypto_kpp_compute_shared_secret (req), &wait); +if (ret) { +dev_dbg (ctrl->dev, +"failed to generate shared secret, error %d\n" , ret); +kfree_sensitive (chap->sess_key); + chap->sess_key =NULL ; + chap->sess_key_len =0 ; + }else +dev_dbg (ctrl->dev,"shared secret %*ph\n" , + (int )chap->sess_key_len, chap->sess_key); +out_free_host: +if (ret) { +kfree (chap->host_key); + chap->host_key =NULL ; + chap->host_key_len =0 ; + } +out_free_req: +kpp_request_free (req); +out_free_exp: +kfree_sensitive (pkey); +if (ret) + chap->status =NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; +return ret; +} +void nvme_auth_free (struct nvme_dhchap_context *chap) {if (chap->shash_tfm)crypto_free_shash (chap->shash_tfm); +if (chap->digest_tfm) +crypto_free_shash (chap->digest_tfm); +if (chap->dh_tfm) +crypto_free_kpp (chap->dh_tfm);if (chap->key)kfree (chap->key);if (chap->ctrl_key)@@ -732,6 +1061,15 @@ int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid) if (ret) goto fail2; +if (chap->ctrl_key_len) { +dev_dbg (ctrl->device, +"%s: qid %d DH-HMAC-DHAP DH exponential\n" , + __func__, qid); + ret =nvme_auth_dhchap_exponential (ctrl, chap); +if (ret) + goto fail2; + } +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP host response\n" , __func__, qid); ret =nvme_auth_dhchap_host_response (ctrl, chap);
drivers/nvme/host/auth.c int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int · line 1150
Quoted 7 lines
drivers/nvme/host/auth.c +1
-0@@ -806,6 +1144,7 @@ int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid) ret = -EPROTO;if (!ret) { ctrl->dhchap_hash = chap->hash_id; + ctrl->dhchap_dhgroup = chap->dhgroup_id; }kfree (buf);nvme_auth_free (chap);
Ciao
Stephan
Hannes Reinecke
maintainerRe: [PATCH 07/11] nvme-auth: augmented challenge support
Quoted 365 lines · Stephan Müller
Am Freitag, 16. Juli 2021, 13:04:24 CEST schrieb Hannes Reinecke:
Hi Hannes,
Quoted 361 lines
Implement support for augmented challenge using FFDHE groups.
Signed-off-by Hannes Reinecke <[email protected]>--- drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++---- 1 file changed, 371 insertions(+), 32 deletions(-)drivers/nvme/host/auth.c +212
-32@@ -8,6 +8,8 @@#include <asm/unaligned.h> #include <crypto/hash.h> #include <crypto/kpp.h> +#include <crypto/dh.h> +#include <crypto/ffdhe.h> #include "nvme.h" #include "fabrics.h" #include "auth.h" @@ -16,6 +18,8 @@ static u32 nvme_dhchap_seqnum;struct nvme_dhchap_context {struct crypto_shash *shash_tfm; +struct crypto_shash *digest_tfm; +struct crypto_kpp *dh_tfm;unsigned char *key;size_t key_len;int qid;@@ -25,6 +29,8 @@ struct nvme_dhchap_context {u8 status;u8 hash_id;u8 hash_len; +u8 dhgroup_id; +u16 dhgroup_size;u8 c1[64 ];u8 c2[64 ];u8 response[64 ];@@ -36,6 +42,94 @@ struct nvme_dhchap_context {int sess_key_len; }; +struct nvme_auth_dhgroup_map { +int id; +const char name[16 ]; +const char kpp[16 ]; +int privkey_size; +int pubkey_size; +} dhgroup_map[] = { + { .id =NVME_AUTH_DHCHAP_DHGROUP_NULL , + .name ="NULL" , .kpp ="NULL" , + .privkey_size =0 , .pubkey_size =0 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_2048 , + .name ="ffdhe2048" , .kpp ="dh" , + .privkey_size =256 , .pubkey_size =256 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_3072 , + .name ="ffdhe3072" , .kpp ="dh" , + .privkey_size =384 , .pubkey_size =384 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_4096 , + .name ="ffdhe4096" , .kpp ="dh" , + .privkey_size =512 , .pubkey_size =512 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_6144 , + .name ="ffdhe6144" , .kpp ="dh" , + .privkey_size =768 , .pubkey_size =768 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_8192 , + .name ="ffdhe8192" , .kpp ="dh" , + .privkey_size =1024 , .pubkey_size =1024 }, +}; + +const char *nvme_auth_dhgroup_name (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].name; + } +return NULL ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_name); + +int nvme_auth_dhgroup_pubkey_size (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].pubkey_size; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_pubkey_size); + +int nvme_auth_dhgroup_privkey_size (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].privkey_size; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_privkey_size); + +const char *nvme_auth_dhgroup_kpp (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].kpp; + } +return NULL ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_kpp); + +int nvme_auth_dhgroup_id (const char *dhgroup_name) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (!strncmp (dhgroup_map[i].name, dhgroup_name, +strlen (dhgroup_map[i].name))) +return dhgroup_map[i].id; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_id); +struct nvmet_dhchap_hash_map {int id;int hash_len;@@ -243,11 +337,16 @@ static int nvme_auth_dhchap_negotiate(struct nvme_ctrl *ctrl, data->napd =1 ; data->auth_protocol[0 ].dhchap.authid =NVME_AUTH_DHCHAP_AUTH_ID ; data->auth_protocol[0 ].dhchap.halen =3 ; - data->auth_protocol[0 ].dhchap.dhlen =1 ; + data->auth_protocol[0 ].dhchap.dhlen =6 ; data->auth_protocol[0 ].dhchap.idlist[0 ] =NVME_AUTH_DHCHAP_HASH_SHA256 ; data->auth_protocol[0 ].dhchap.idlist[1 ] =NVME_AUTH_DHCHAP_HASH_SHA384 ; data->auth_protocol[0 ].dhchap.idlist[2 ] =NVME_AUTH_DHCHAP_HASH_SHA512 ; data->auth_protocol[0 ].dhchap.idlist[3 ] =NVME_AUTH_DHCHAP_DHGROUP_NULL ; + data->auth_protocol[0 ].dhchap.idlist[4 ] =NVME_AUTH_DHCHAP_DHGROUP_2048 ; + data->auth_protocol[0 ].dhchap.idlist[5 ] =NVME_AUTH_DHCHAP_DHGROUP_3072 ; + data->auth_protocol[0 ].dhchap.idlist[6 ] =NVME_AUTH_DHCHAP_DHGROUP_4096 ; + data->auth_protocol[0 ].dhchap.idlist[7 ] =NVME_AUTH_DHCHAP_DHGROUP_6144 ; + data->auth_protocol[0 ].dhchap.idlist[8 ] =NVME_AUTH_DHCHAP_DHGROUP_8192 ;return size; }@@ -274,14 +373,7 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl, chap->status =NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ;return -EPROTO ; } -switch (data->dhgid) { -case NVME_AUTH_DHCHAP_DHGROUP_NULL : - gid_name ="null" ; -break ; -default : - gid_name =NULL ; -break ; - } + gid_name =nvme_auth_dhgroup_kpp (data->dhgid);if (!gid_name) { dev_warn(ctrl->device, "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n",@@ -290,10 +382,24 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl, return -EPROTO ; }if (data->dhgid !=NVME_AUTH_DHCHAP_DHGROUP_NULL ) { - chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; -return -EPROTO ; - } -if (data->dhgid ==NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen !=0 ) { +if (data->dhvlen ==0 ) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: empty DH value\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; +return -EPROTO ; + } + chap->dh_tfm =crypto_alloc_kpp (gid_name,0 ,0 ); +if (IS_ERR (chap->dh_tfm)) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: failed to initialize %s\n" , + chap->qid, gid_name); + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; + chap->dh_tfm =NULL ; +return -EPROTO ; + } + chap->dhgroup_id = data->dhgid; + }else if (data->dhvlen !=0 ) {dev_warn (ctrl->device,"qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n" , chap->qid);@@ -313,6 +419,16 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl, chap->hash_len = data->hl; chap->s1 =le32_to_cpu (data->seqnum);memcpy (chap->c1, data->cval, chap->hash_len); +if (data->dhvlen) { + chap->ctrl_key =kmalloc (data->dhvlen,GFP_KERNEL ); +if (!chap->ctrl_key) +return -ENOMEM ; + chap->ctrl_key_len = data->dhvlen; +memcpy (chap->ctrl_key, data->cval + chap->hash_len, + data->dhvlen); +dev_dbg (ctrl->device,"ctrl public key %*ph\n" , + (int )chap->ctrl_key_len, chap->ctrl_key); + }return 0 ; }@@ -353,10 +469,13 @@ static int nvme_auth_dhchap_reply(struct nvme_ctrl *ctrl,memcpy (data->rval + chap->hash_len, chap->c2, chap->hash_len); } -if (chap->host_key_len) +if (chap->host_key_len) { +dev_dbg (ctrl->device,"%s: qid %d host public key %*ph\n" , + __func__, chap->qid, + chap->host_key_len, chap->host_key);memcpy (data->rval +2 * chap->hash_len, chap->host_key, chap->host_key_len); - + }return size; }@@ -440,23 +559,10 @@ static int nvme_auth_dhchap_failure2(struct nvme_ctrl *ctrl, int nvme_auth_select_hash(struct nvme_ctrl *ctrl,struct nvme_dhchap_context *chap) { -char *hash_name; +const char *hash_name, *digest_name;int ret; -switch (chap->hash_id) { -case NVME_AUTH_DHCHAP_HASH_SHA256 : - hash_name ="hmac(sha256)" ; -break ; -case NVME_AUTH_DHCHAP_HASH_SHA384 : - hash_name ="hmac(sha384)" ; -break ; -case NVME_AUTH_DHCHAP_HASH_SHA512 : - hash_name ="hmac(sha512)" ; -break ; -default : - hash_name =NULL ; -break ; - } + hash_name =nvme_auth_hmac_name (chap->hash_id);if (!hash_name) { chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ;return -EPROTO ;@@ -468,26 +574,100 @@ int nvme_auth_select_hash(struct nvme_ctrl *ctrl, chap->shash_tfm =NULL ;return -EPROTO ; } + digest_name =nvme_auth_digest_name (chap->hash_id); +if (!digest_name) { +crypto_free_shash (chap->shash_tfm); + chap->shash_tfm =NULL ; +return -EPROTO ; + } + chap->digest_tfm =crypto_alloc_shash (digest_name,0 ,0 ); +if (IS_ERR (chap->digest_tfm)) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->shash_tfm); + chap->shash_tfm =NULL ; + chap->digest_tfm =NULL ; +return -EPROTO ; + }if (!chap->key) {dev_warn (ctrl->device,"qid %d: cannot select hash, no key\n" , chap->qid); chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->digest_tfm);crypto_free_shash (chap->shash_tfm); chap->shash_tfm =NULL ; + chap->digest_tfm =NULL ;return -EINVAL ; } ret =crypto_shash_setkey (chap->shash_tfm, chap->key, chap->key_len);if (ret) { chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->digest_tfm);crypto_free_shash (chap->shash_tfm); chap->shash_tfm =NULL ; + chap->digest_tfm =NULL ;return ret; } -dev_info (ctrl->device,"qid %d: DH-HMAC_CHAP: selected hash %s\n" , - chap->qid, hash_name); +dev_dbg (ctrl->device,"qid %d: DH-HMAC_CHAP: selected hash %s\n" , + chap->qid, hash_name);return 0 ; } +static int nvme_auth_augmented_challenge (struct nvme_dhchap_context *chap, +u8 *challenge,u8 *aug) +{ +struct crypto_shash *tfm; +struct shash_desc *desc; +u8 *hashed_key; +const char *hash_name; +int ret; + + hashed_key =kmalloc (chap->hash_len,GFP_KERNEL ); +if (!hashed_key) +return -ENOMEM ; + + ret =crypto_shash_tfm_digest (chap->digest_tfm, chap->sess_key, + chap->sess_key_len, hashed_key); +if (ret <0 ) { +pr_debug ("failed to hash session key, err %d\n" , ret); +kfree (hashed_key); +return ret; + } + hash_name =crypto_shash_alg_name (chap->shash_tfm); +if (!hash_name) { +pr_debug ("Invalid hash algoritm\n" ); +return -EINVAL ; + } + tfm =crypto_alloc_shash (hash_name,0 ,0 ); +if (IS_ERR (tfm)) { + ret =PTR_ERR (tfm); + goto out_free_key; + } + desc =kmalloc (sizeof (struct shash_desc ) +crypto_shash_descsize (tfm), +GFP_KERNEL ); +if (!desc) { + ret = -ENOMEM ; + goto out_free_hash; + } + desc->tfm = tfm; + + ret =crypto_shash_setkey (tfm, hashed_key, chap->hash_len); +if (ret) + goto out_free_desc; + ret =crypto_shash_init (desc); +if (ret) + goto out_free_desc; +crypto_shash_update (desc, challenge, chap->hash_len); +crypto_shash_final (desc, aug); + +out_free_desc: +kfree_sensitive (desc); +out_free_hash: +crypto_free_shash (tfm); +out_free_key: +kfree (hashed_key); +return ret; +} +static int nvme_auth_dhchap_host_response (struct nvme_ctrl *ctrl,struct nvme_dhchap_context *chap) {@@ -497,6 +677,16 @@ static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
Quoted 8 lines
drivers/nvme/host/auth.c static int nvme_auth_dhchap_host_response(struct · line 679
Quoted 1 line
drivers/nvme/host/auth.c +0
-0dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction%d\n",
drivers/nvme/host/auth.c static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl, · line 681
Quoted 3 lines
drivers/nvme/host/auth.c +2
-0__func__, chap->qid, chap->s1, chap->transaction); +if (chap->dh_tfm) { + challenge =kmalloc (chap->hash_len,GFP_KERNEL );Again, alignment?
Again, why?
Cheers,
Hannes
Signature · 4 lines
Dr. Hannes Reinecke Kernel Storage Architect [email protected] +49 911 74053 688 SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
Re: [PATCH 07/11] nvme-auth: augmented challenge support
Am Sonntag, 18. Juli 2021, 14:27:48 CEST schrieb Hannes Reinecke:
Hi Hannes,
Quoted 243 lines · Stephan Müller
Quoted 242 lines
Am Freitag, 16. Juli 2021, 13:04:24 CEST schrieb Hannes Reinecke:
Hi Hannes,
Quoted 238 lines
Implement support for augmented challenge using FFDHE groups.
Signed-off-by Hannes Reinecke <[email protected]>---
drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++---- 1 file changed, 371 insertions(+), 32 deletions(-)drivers/nvme/host/auth.c +2
-0@@ -8,6 +8,8 @@#include <asm/unaligned.h> #include <crypto/hash.h> #include <crypto/kpp.h> +#include <crypto/dh.h> +#include <crypto/ffdhe.h> #include "nvme.h" #include "fabrics.h" #include "auth.h"drivers/nvme/host/auth.c +2
-0@@ -16,6 +18,8 @@ static u32 nvme_dhchap_seqnum;struct nvme_dhchap_context {struct crypto_shash *shash_tfm; +struct crypto_shash *digest_tfm; +struct crypto_kpp *dh_tfm;unsigned char *key;
size_tkey_len;
int qid;drivers/nvme/host/auth.c +2
-0@@ -25,6 +29,8 @@ struct nvme_dhchap_context {u8 status;u8 hash_id;u8 hash_len; +u8 dhgroup_id; +u16 dhgroup_size;u8 c1[64]; u8 c2[64]; u8 response[64];drivers/nvme/host/auth.c +88
-0@@ -36,6 +42,94 @@ struct nvme_dhchap_context {int sess_key_len; }; +struct nvme_auth_dhgroup_map { +int id; +const char name[16 ]; +const char kpp[16 ]; +int privkey_size; +int pubkey_size; +} dhgroup_map[] = { + { .id =NVME_AUTH_DHCHAP_DHGROUP_NULL , + .name ="NULL" , .kpp ="NULL" , + .privkey_size =0 , .pubkey_size =0 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_2048 , + .name ="ffdhe2048" , .kpp ="dh" , + .privkey_size =256 , .pubkey_size =256 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_3072 , + .name ="ffdhe3072" , .kpp ="dh" , + .privkey_size =384 , .pubkey_size =384 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_4096 , + .name ="ffdhe4096" , .kpp ="dh" , + .privkey_size =512 , .pubkey_size =512 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_6144 , + .name ="ffdhe6144" , .kpp ="dh" , + .privkey_size =768 , .pubkey_size =768 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_8192 , + .name ="ffdhe8192" , .kpp ="dh" , + .privkey_size =1024 , .pubkey_size =1024 }, +}; + +const char *nvme_auth_dhgroup_name (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].name; + } +return NULL ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_name); + +int nvme_auth_dhgroup_pubkey_size (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].pubkey_size; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_pubkey_size); + +int nvme_auth_dhgroup_privkey_size (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].privkey_size; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_privkey_size); + +const char *nvme_auth_dhgroup_kpp (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].kpp; + } +return NULL ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_kpp); + +int nvme_auth_dhgroup_id (const char *dhgroup_name) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (!strncmp (dhgroup_map[i].name, dhgroup_name, +strlen (dhgroup_map[i].name))) +return dhgroup_map[i].id; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_id); +struct nvmet_dhchap_hash_map {int id;
inthash_len;drivers/nvme/host/auth.c +2
-1@@ -243,11 +337,16 @@ static int nvme_auth_dhchap_negotiate(struct nvme_ctrl *ctrl, data->napd =1 ; data->auth_protocol[0 ].dhchap.authid =NVME_AUTH_DHCHAP_AUTH_ID ; data->auth_protocol[0 ].dhchap.halen =3 ; - data->auth_protocol[0 ].dhchap.dhlen =1 ; + data->auth_protocol[0 ].dhchap.dhlen =6 ; data->auth_protocol[0 ].dhchap.idlist[0 ] =NVME_AUTH_DHCHAP_HASH_SHA256 ; data->auth_protocol[0 ].dhchap.idlist[1 ] =NVME_AUTH_DHCHAP_HASH_SHA384 ; data->auth_protocol[0 ].dhchap.idlist[2 ] =NVME_AUTH_DHCHAP_HASH_SHA512 ; data->auth_protocol[0 ].dhchap.idlist[3 ] =NVME_AUTH_DHCHAP_DHGROUP_NULL ; + data->auth_protocol[0].dhchap.idlist[4] =
NVME_AUTH_DHCHAP_DHGROUP_2048;drivers/nvme/host/auth.c +1
-0+ data->auth_protocol[0].dhchap.idlist[5] =
NVME_AUTH_DHCHAP_DHGROUP_3072;drivers/nvme/host/auth.c +1
-0+ data->auth_protocol[0].dhchap.idlist[6] =
NVME_AUTH_DHCHAP_DHGROUP_4096;drivers/nvme/host/auth.c +1
-0+ data->auth_protocol[0].dhchap.idlist[7] =
NVME_AUTH_DHCHAP_DHGROUP_6144;drivers/nvme/host/auth.c +1
-0+ data->auth_protocol[0].dhchap.idlist[8] =
NVME_AUTH_DHCHAP_DHGROUP_8192;return size;
}drivers/nvme/host/auth.c +0
-0@@ -274,14 +373,7 @@ static int nvme_auth_dhchap_challenge(struct
nvme_ctrl
*ctrl,chap->status=NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;return
-EPROTO;}drivers/nvme/host/auth.c +19
-12-switch (data->dhgid) { -case NVME_AUTH_DHCHAP_DHGROUP_NULL : - gid_name ="null" ; -break ; -default : - gid_name =NULL ; -break ; - } + gid_name =nvme_auth_dhgroup_kpp (data->dhgid);if (!gid_name) { dev_warn(ctrl->device, "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n",@@ -290,10 +382,24 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl, return -EPROTO ; }if (data->dhgid !=NVME_AUTH_DHCHAP_DHGROUP_NULL ) { - chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; -return -EPROTO ; - } -if (data->dhgid ==NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen !=0 ) { +if (data->dhvlen ==0 ) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: empty DH value\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; +return -EPROTO ; + } + chap->dh_tfm =crypto_alloc_kpp (gid_name,0 ,0 ); +if (IS_ERR (chap->dh_tfm)) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: failed to initialize %s\n" , + chap->qid, gid_name); + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; + chap->dh_tfm =NULL ; +return -EPROTO ; + } + chap->dhgroup_id = data->dhgid; + } else if (data->dhvlen != 0) {
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
dev_warn(ctrl->device,
"qid %d: DH-HMAC-CHAP: invalid DH value for NULL
DH\n",
Quoted 105 lines
Quoted 105 lines
Quoted 105 lines
chap->qid);drivers/nvme/host/auth.c +0
-0@@ -313,6 +419,16 @@ static int nvme_auth_dhchap_challenge(struct
nvme_ctrl
*ctrl,chap->hash_len=data->hl;chap->s1 = le32_to_cpu(data->seqnum); memcpy(chap->c1, data->cval, chap->hash_len);drivers/nvme/host/auth.c +14
-1+if (data->dhvlen) { + chap->ctrl_key =kmalloc (data->dhvlen,GFP_KERNEL ); +if (!chap->ctrl_key) +return -ENOMEM ; + chap->ctrl_key_len = data->dhvlen; +memcpy (chap->ctrl_key, data->cval + chap->hash_len, + data->dhvlen); +dev_dbg (ctrl->device,"ctrl public key %*ph\n" , + (int )chap->ctrl_key_len, chap->ctrl_key); + }return 0 ; }@@ -353,10 +469,13 @@ static int nvme_auth_dhchap_reply(struct nvme_ctrl *ctrl,memcpy (data->rval + chap->hash_len, chap->c2, chap->hash_len); } -if (chap->host_key_len) +if (chap->host_key_len) { +dev_dbg (ctrl->device,"%s: qid %d host public key %*ph\n" , + __func__, chap->qid, + chap->host_key_len, chap->host_key); memcpy(data->rval + 2 * chap->hash_len, chap->host_key,
chap->host_key_len);drivers/nvme/host/auth.c +1
-1- + }return size; }@@ -440,23 +559,10 @@ static int nvme_auth_dhchap_failure2(struct
nvme_ctrl
*ctrl, int nvme_auth_select_hash(structnvme_ctrl*ctrl,struct
nvme_dhchap_context*chap){drivers/nvme/host/auth.c +16
-15-char *hash_name; +const char *hash_name, *digest_name;int ret; -switch (chap->hash_id) { -case NVME_AUTH_DHCHAP_HASH_SHA256 : - hash_name ="hmac(sha256)" ; -break ; -case NVME_AUTH_DHCHAP_HASH_SHA384 : - hash_name ="hmac(sha384)" ; -break ; -case NVME_AUTH_DHCHAP_HASH_SHA512 : - hash_name ="hmac(sha512)" ; -break ; -default : - hash_name =NULL ; -break ; - } + hash_name =nvme_auth_hmac_name (chap->hash_id);if (!hash_name) { chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ;return -EPROTO ;@@ -468,26 +574,100 @@ int nvme_auth_select_hash(struct nvme_ctrl *ctrl, chap->shash_tfm =NULL ;return -EPROTO ; } + digest_name =nvme_auth_digest_name (chap->hash_id); +if (!digest_name) { +crypto_free_shash (chap->shash_tfm); + chap->shash_tfm =NULL ; +return -EPROTO ; + } + chap->digest_tfm =crypto_alloc_shash (digest_name,0 ,0 ); +if (IS_ERR (chap->digest_tfm)) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->shash_tfm); + chap->shash_tfm =NULL ; + chap->digest_tfm =NULL ; +return -EPROTO ; + }
Quoted 3 lines
Quoted 3 lines
drivers/nvme/host/auth.c int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
Quoted 3 lines
drivers/nvme/host/auth.c +0
-0if (!chap->key) { dev_warn(ctrl->device, "qid %d: cannot select hash, no
key\n",
Quoted 101 lines
Quoted 10 lines
Quoted 10 lines
drivers/nvme/host/auth.c +1
-0chap->qid); chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->digest_tfm);crypto_free_shash (chap->shash_tfm); chap->shash_tfm =NULL ;Quoted 6 lines
drivers/nvme/host/auth.c int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
Quoted 6 lines
drivers/nvme/host/auth.c +1
-0+ chap->digest_tfm =NULL ;return -EINVAL ; } ret = crypto_shash_setkey(chap->shash_tfm, chap->key, chap-
key_len);Quoted 84 lines
Quoted 84 lines
drivers/nvme/host/auth.c +5
-2if (ret) { chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->digest_tfm);crypto_free_shash (chap->shash_tfm); chap->shash_tfm =NULL ; + chap->digest_tfm =NULL ;return ret; } -dev_info (ctrl->device,"qid %d: DH-HMAC_CHAP: selected hash %s\n" , - chap->qid, hash_name); +dev_dbg (ctrl->device,"qid %d: DH-HMAC_CHAP: selected hash %s\n" , + chap->qid, hash_name);return 0 ; } +static int nvme_auth_augmented_challenge(struct nvme_dhchap_context*chap,
drivers/nvme/host/auth.c +55
-0+ u8 *challenge, u8 *aug) +{ +struct crypto_shash *tfm; +struct shash_desc *desc; +u8 *hashed_key; +const char *hash_name; +int ret; + + hashed_key =kmalloc (chap->hash_len,GFP_KERNEL ); +if (!hashed_key) +return -ENOMEM ; + + ret =crypto_shash_tfm_digest (chap->digest_tfm, chap->sess_key, + chap->sess_key_len, hashed_key); +if (ret <0 ) { +pr_debug ("failed to hash session key, err %d\n" , ret); +kfree (hashed_key); +return ret; + } + hash_name =crypto_shash_alg_name (chap->shash_tfm); +if (!hash_name) { +pr_debug ("Invalid hash algoritm\n" ); +return -EINVAL ; + } + tfm =crypto_alloc_shash (hash_name,0 ,0 ); +if (IS_ERR (tfm)) { + ret =PTR_ERR (tfm); + goto out_free_key; + } + desc =kmalloc (sizeof (struct shash_desc ) +crypto_shash_descsize (tfm), +GFP_KERNEL ); +if (!desc) { + ret = -ENOMEM ; + goto out_free_hash; + } + desc->tfm = tfm; + + ret =crypto_shash_setkey (tfm, hashed_key, chap->hash_len); +if (ret) + goto out_free_desc; + ret =crypto_shash_init (desc); +if (ret) + goto out_free_desc; +crypto_shash_update (desc, challenge, chap->hash_len); +crypto_shash_final (desc, aug); + +out_free_desc: +kfree_sensitive (desc); +out_free_hash: +crypto_free_shash (tfm); +out_free_key: +kfree (hashed_key); +return ret; +} + static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
Quoted 8 lines
Quoted 8 lines
drivers/nvme/host/auth.c static int nvme_auth_dhchap_host_response(struct · line 679
Quoted 8 lines
struct
nvme_dhchap_context*chap){drivers/nvme/host/auth.c +0
-0@@ -497,6 +677,16 @@ static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl, dev_dbg(ctrl->device, "%s: qid %d host response seq %d
transaction
Quoted 11 lines
Quoted 9 lines
%d\n",
drivers/nvme/host/auth.c static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl, · line 681
Quoted 4 lines
drivers/nvme/host/auth.c +2
-0__func__, chap->qid, chap->s1, chap->transaction); +if (chap->dh_tfm) { + challenge =kmalloc (chap->hash_len,GFP_KERNEL );Again, alignment?
Again, why?
This buffer is the digest buffer of a shash_final, no? check crypto_shash_final and see that you could spare yourself some extra work if aligned.
Ciao
Stephan
Sagi Grimberg
maintainerRe: [PATCH 07/11] nvme-auth: augmented challenge support
In reply to Hannes ReineckeQuoted 1 line · Hannes Reinecke
Implement support for augmented challenge using FFDHE groups.
Please some more info for the change log...
Quoted 179 lines
Signed-off-by Hannes Reinecke <[email protected]>--- drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++---- 1 file changed, 371 insertions(+), 32 deletions(-)drivers/nvme/host/auth.c +106
-13@@ -8,6 +8,8 @@#include <asm/unaligned.h> #include <crypto/hash.h> #include <crypto/kpp.h> +#include <crypto/dh.h> +#include <crypto/ffdhe.h> #include "nvme.h" #include "fabrics.h" #include "auth.h" @@ -16,6 +18,8 @@ static u32 nvme_dhchap_seqnum;struct nvme_dhchap_context {struct crypto_shash *shash_tfm; +struct crypto_shash *digest_tfm; +struct crypto_kpp *dh_tfm;unsigned char *key;size_t key_len;int qid;@@ -25,6 +29,8 @@ struct nvme_dhchap_context {u8 status;u8 hash_id;u8 hash_len; +u8 dhgroup_id; +u16 dhgroup_size;u8 c1[64 ];u8 c2[64 ];u8 response[64 ];@@ -36,6 +42,94 @@ struct nvme_dhchap_context {int sess_key_len; }; +struct nvme_auth_dhgroup_map { +int id; +const char name[16 ]; +const char kpp[16 ]; +int privkey_size; +int pubkey_size; +} dhgroup_map[] = { + { .id =NVME_AUTH_DHCHAP_DHGROUP_NULL , + .name ="NULL" , .kpp ="NULL" , + .privkey_size =0 , .pubkey_size =0 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_2048 , + .name ="ffdhe2048" , .kpp ="dh" , + .privkey_size =256 , .pubkey_size =256 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_3072 , + .name ="ffdhe3072" , .kpp ="dh" , + .privkey_size =384 , .pubkey_size =384 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_4096 , + .name ="ffdhe4096" , .kpp ="dh" , + .privkey_size =512 , .pubkey_size =512 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_6144 , + .name ="ffdhe6144" , .kpp ="dh" , + .privkey_size =768 , .pubkey_size =768 }, + { .id =NVME_AUTH_DHCHAP_DHGROUP_8192 , + .name ="ffdhe8192" , .kpp ="dh" , + .privkey_size =1024 , .pubkey_size =1024 }, +}; + +const char *nvme_auth_dhgroup_name (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].name; + } +return NULL ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_name); + +int nvme_auth_dhgroup_pubkey_size (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].pubkey_size; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_pubkey_size); + +int nvme_auth_dhgroup_privkey_size (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].privkey_size; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_privkey_size); + +const char *nvme_auth_dhgroup_kpp (int dhgroup_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (dhgroup_map[i].id == dhgroup_id) +return dhgroup_map[i].kpp; + } +return NULL ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_kpp); + +int nvme_auth_dhgroup_id (const char *dhgroup_name) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (dhgroup_map); i++) { +if (!strncmp (dhgroup_map[i].name, dhgroup_name, +strlen (dhgroup_map[i].name))) +return dhgroup_map[i].id; + } +return -1 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_id); +struct nvmet_dhchap_hash_map {int id;int hash_len;@@ -243,11 +337,16 @@ static int nvme_auth_dhchap_negotiate(struct nvme_ctrl *ctrl, data->napd =1 ; data->auth_protocol[0 ].dhchap.authid =NVME_AUTH_DHCHAP_AUTH_ID ; data->auth_protocol[0 ].dhchap.halen =3 ; - data->auth_protocol[0 ].dhchap.dhlen =1 ; + data->auth_protocol[0 ].dhchap.dhlen =6 ; data->auth_protocol[0 ].dhchap.idlist[0 ] =NVME_AUTH_DHCHAP_HASH_SHA256 ; data->auth_protocol[0 ].dhchap.idlist[1 ] =NVME_AUTH_DHCHAP_HASH_SHA384 ; data->auth_protocol[0 ].dhchap.idlist[2 ] =NVME_AUTH_DHCHAP_HASH_SHA512 ; data->auth_protocol[0 ].dhchap.idlist[3 ] =NVME_AUTH_DHCHAP_DHGROUP_NULL ; + data->auth_protocol[0 ].dhchap.idlist[4 ] =NVME_AUTH_DHCHAP_DHGROUP_2048 ; + data->auth_protocol[0 ].dhchap.idlist[5 ] =NVME_AUTH_DHCHAP_DHGROUP_3072 ; + data->auth_protocol[0 ].dhchap.idlist[6 ] =NVME_AUTH_DHCHAP_DHGROUP_4096 ; + data->auth_protocol[0 ].dhchap.idlist[7 ] =NVME_AUTH_DHCHAP_DHGROUP_6144 ; + data->auth_protocol[0 ].dhchap.idlist[8 ] =NVME_AUTH_DHCHAP_DHGROUP_8192 ;return size; }@@ -274,14 +373,7 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl, chap->status =NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ;return -EPROTO ; } -switch (data->dhgid) { -case NVME_AUTH_DHCHAP_DHGROUP_NULL : - gid_name ="null" ; -break ; -default : - gid_name =NULL ; -break ; - } + gid_name =nvme_auth_dhgroup_kpp (data->dhgid);if (!gid_name) { dev_warn(ctrl->device, "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n",@@ -290,10 +382,24 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl,return -EPROTO ; }if (data->dhgid !=NVME_AUTH_DHCHAP_DHGROUP_NULL ) { - chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; -return -EPROTO ; - } -if (data->dhgid ==NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen !=0 ) { +if (data->dhvlen ==0 ) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: empty DH value\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ;
drivers/nvme/host/auth.c static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl, · line 399
Quoted 10 lines
drivers/nvme/host/auth.c +10
-0+return -EPROTO ; + } + chap->dh_tfm =crypto_alloc_kpp (gid_name,0 ,0 ); +if (IS_ERR (chap->dh_tfm)) { +dev_warn (ctrl->device, +"qid %d: DH-HMAC-CHAP: failed to initialize %s\n" , + chap->qid, gid_name); + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; + chap->dh_tfm =NULL ; +return -EPROTO ;
Why not propogate the error?
Quoted 26 lines
drivers/nvme/host/auth.c +13
-0+ } + chap->dhgroup_id = data->dhgid; + }else if (data->dhvlen !=0 ) {dev_warn (ctrl->device,"qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n" , chap->qid);@@ -313,6 +419,16 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl, chap->hash_len = data->hl; chap->s1 =le32_to_cpu (data->seqnum);memcpy (chap->c1, data->cval, chap->hash_len); +if (data->dhvlen) { + chap->ctrl_key =kmalloc (data->dhvlen,GFP_KERNEL ); +if (!chap->ctrl_key) +return -ENOMEM ; + chap->ctrl_key_len = data->dhvlen; +memcpy (chap->ctrl_key, data->cval + chap->hash_len, + data->dhvlen); +dev_dbg (ctrl->device,"ctrl public key %*ph\n" , + (int )chap->ctrl_key_len, chap->ctrl_key); + }return 0 ; }@@ -353,10 +469,13 @@ static int nvme_auth_dhchap_reply(struct nvme_ctrl *ctrl,memcpy (data->rval + chap->hash_len, chap->c2, chap->hash_len);
drivers/nvme/host/auth.c static int nvme_auth_dhchap_reply(struct nvme_ctrl *ctrl, · line 478
Quoted 10 lines
drivers/nvme/host/auth.c +5
-2} -if (chap->host_key_len) +if (chap->host_key_len) { +dev_dbg (ctrl->device,"%s: qid %d host public key %*ph\n" , + __func__, chap->qid, + chap->host_key_len, chap->host_key);memcpy (data->rval +2 * chap->hash_len, chap->host_key, chap->host_key_len); - + }
Is this change only adding the debug print?
Quoted 46 lines
drivers/nvme/host/auth.c +15
-15return size; }@@ -440,23 +559,10 @@ static int nvme_auth_dhchap_failure2(struct nvme_ctrl *ctrl,int nvme_auth_select_hash (struct nvme_ctrl *ctrl,struct nvme_dhchap_context *chap) { -char *hash_name; +const char *hash_name, *digest_name;int ret; -switch (chap->hash_id) { -case NVME_AUTH_DHCHAP_HASH_SHA256 : - hash_name ="hmac(sha256)" ; -break ; -case NVME_AUTH_DHCHAP_HASH_SHA384 : - hash_name ="hmac(sha384)" ; -break ; -case NVME_AUTH_DHCHAP_HASH_SHA512 : - hash_name ="hmac(sha512)" ; -break ; -default : - hash_name =NULL ; -break ; - } + hash_name =nvme_auth_hmac_name (chap->hash_id);if (!hash_name) { chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ;return -EPROTO ;@@ -468,26 +574,100 @@ int nvme_auth_select_hash(struct nvme_ctrl *ctrl, chap->shash_tfm =NULL ;return -EPROTO ; } + digest_name =nvme_auth_digest_name (chap->hash_id); +if (!digest_name) { +crypto_free_shash (chap->shash_tfm); + chap->shash_tfm =NULL ; +return -EPROTO ; + } + chap->digest_tfm =crypto_alloc_shash (digest_name,0 ,0 ); +if (IS_ERR (chap->digest_tfm)) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->shash_tfm); + chap->shash_tfm =NULL ; + chap->digest_tfm =NULL ; +return -EPROTO ;
drivers/nvme/host/auth.c int nvme_auth_select_hash(struct nvme_ctrl *ctrl, · line 599
Quoted 10 lines
drivers/nvme/host/auth.c +3
-0+ }if (!chap->key) {dev_warn (ctrl->device,"qid %d: cannot select hash, no key\n" , chap->qid); chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->digest_tfm);crypto_free_shash (chap->shash_tfm); chap->shash_tfm =NULL ; + chap->digest_tfm =NULL ;return -EINVAL ;
Please have a structured goto targets in reverse order, this repeated cleanup is a mess...
Quoted 25 lines
drivers/nvme/host/auth.c +12
-2} ret =crypto_shash_setkey (chap->shash_tfm, chap->key, chap->key_len);if (ret) { chap->status =NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE ; +crypto_free_shash (chap->digest_tfm);crypto_free_shash (chap->shash_tfm); chap->shash_tfm =NULL ; + chap->digest_tfm =NULL ;return ret; } -dev_info (ctrl->device,"qid %d: DH-HMAC_CHAP: selected hash %s\n" , - chap->qid, hash_name); +dev_dbg (ctrl->device,"qid %d: DH-HMAC_CHAP: selected hash %s\n" , + chap->qid, hash_name);return 0 ; } +static int nvme_auth_augmented_challenge (struct nvme_dhchap_context *chap, +u8 *challenge,u8 *aug) +{ +struct crypto_shash *tfm; +struct shash_desc *desc; +u8 *hashed_key; +const char *hash_name; +int ret;
drivers/nvme/host/auth.c int nvme_auth_select_hash(struct nvme_ctrl *ctrl, · line 632
Quoted 10 lines
drivers/nvme/host/auth.c +10
-0+ + hashed_key =kmalloc (chap->hash_len,GFP_KERNEL ); +if (!hashed_key) +return -ENOMEM ; + + ret =crypto_shash_tfm_digest (chap->digest_tfm, chap->sess_key, + chap->sess_key_len, hashed_key); +if (ret <0 ) { +pr_debug ("failed to hash session key, err %d\n" , ret); +kfree (hashed_key);
Same here...
drivers/nvme/host/auth.c int nvme_auth_select_hash(struct nvme_ctrl *ctrl, · line 634
Quoted 2 lines
drivers/nvme/host/auth.c +2
-0+return ret; + }
Spaces between if conditions please?
Quoted 84 lines
drivers/nvme/host/auth.c +59
-0+ hash_name =crypto_shash_alg_name (chap->shash_tfm); +if (!hash_name) { +pr_debug ("Invalid hash algoritm\n" ); +return -EINVAL ; + } + tfm =crypto_alloc_shash (hash_name,0 ,0 ); +if (IS_ERR (tfm)) { + ret =PTR_ERR (tfm); + goto out_free_key; + } + desc =kmalloc (sizeof (struct shash_desc ) +crypto_shash_descsize (tfm), +GFP_KERNEL ); +if (!desc) { + ret = -ENOMEM ; + goto out_free_hash; + } + desc->tfm = tfm; + + ret =crypto_shash_setkey (tfm, hashed_key, chap->hash_len); +if (ret) + goto out_free_desc; + ret =crypto_shash_init (desc); +if (ret) + goto out_free_desc; +crypto_shash_update (desc, challenge, chap->hash_len); +crypto_shash_final (desc, aug); + +out_free_desc: +kfree_sensitive (desc); +out_free_hash: +crypto_free_shash (tfm); +out_free_key: +kfree (hashed_key); +return ret; +} +static int nvme_auth_dhchap_host_response (struct nvme_ctrl *ctrl,struct nvme_dhchap_context *chap) {@@ -497,6 +677,16 @@ static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,dev_dbg (ctrl->device,"%s: qid %d host response seq %d transaction %d\n" , __func__, chap->qid, chap->s1, chap->transaction); +if (chap->dh_tfm) { + challenge =kmalloc (chap->hash_len,GFP_KERNEL ); +if (!challenge) { + ret = -ENOMEM ; + goto out; + } + ret =nvme_auth_augmented_challenge (chap, chap->c1, challenge); +if (ret) + goto out; + } shash->tfm = chap->shash_tfm; ret =crypto_shash_init (shash);if (ret)@@ -532,6 +722,8 @@ static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl, goto out; ret =crypto_shash_final (shash, chap->response); out: +if (challenge != chap->c1) +kfree (challenge);return ret; }@@ -542,6 +734,17 @@ static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl,u8 buf[4 ], *challenge = chap->c2;int ret; +if (chap->dh_tfm) { + challenge =kmalloc (chap->hash_len,GFP_KERNEL ); +if (!challenge) { + ret = -ENOMEM ; + goto out; + } + ret =nvme_auth_augmented_challenge (chap, chap->c2, + challenge); +if (ret) + goto out; + }dev_dbg (ctrl->device,"%s: qid %d host response seq %d transaction %d\n" , __func__, chap->qid, chap->s2, chap->transaction); dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n",
drivers/nvme/host/auth.c static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl, · line 792
Quoted 5 lines
drivers/nvme/host/auth.c +2
-0@@ -585,6 +788,8 @@ static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl, goto out; ret =crypto_shash_final (shash, chap->response); out: +if (challenge != chap->c2) +kfree (challenge);
Just free ?! what about failing?
Quoted 74 lines
drivers/nvme/host/auth.c +67
-0return ret; }@@ -644,10 +849,134 @@ int nvme_auth_generate_key(struct nvme_ctrl *ctrl,return 0 ; } +static int nvme_auth_dhchap_exponential (struct nvme_ctrl *ctrl, +struct nvme_dhchap_context *chap) +{ +struct kpp_request *req; +struct crypto_wait wait; +struct scatterlist src, dst; +u8 *pkey; +int ret, pkey_len; + +if (chap->dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_2048 || + chap->dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_3072 || + chap->dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_4096 || + chap->dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_6144 || + chap->dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_8192 ) { +struct dh p = {0 }; +int pubkey_size =nvme_auth_dhgroup_pubkey_size (chap->dhgroup_id); + + ret =crypto_ffdhe_params (&p, pubkey_size <<3 ); +if (ret) { +dev_dbg (ctrl->device, +"failed to generate ffdhe params, error %d\n" , + ret); +return ret; + } + p.key = chap->key; + p.key_size = chap->key_len; + + pkey_len =crypto_dh_key_len (&p); + pkey =kzalloc (pkey_len,GFP_KERNEL ); + +get_random_bytes (pkey, pkey_len); + ret =crypto_dh_encode_key (pkey, pkey_len, &p); +if (ret) { +dev_dbg (ctrl->device, +"failed to encode pkey, error %d\n" , ret); +kfree (pkey); +return ret; + } + chap->host_key_len = pubkey_size; + chap->sess_key_len = pubkey_size; + }else { +dev_warn (ctrl->device,"Invalid DH group id %d\n" , + chap->dhgroup_id); + chap->status =NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; +return -EINVAL ; + } + + ret =crypto_kpp_set_secret (chap->dh_tfm, pkey, pkey_len); +if (ret) { +dev_dbg (ctrl->dev,"failed to set secret, error %d\n" , ret); +kfree (pkey); +return ret; + } + req =kpp_request_alloc (chap->dh_tfm,GFP_KERNEL ); +if (!req) { + ret = -ENOMEM ; + goto out_free_exp; + } + + chap->host_key =kzalloc (chap->host_key_len,GFP_KERNEL ); +if (!chap->host_key) { + ret = -ENOMEM ; + goto out_free_req; + } +crypto_init_wait (&wait); +kpp_request_set_input (req,NULL ,0 ); +sg_init_one (&dst, chap->host_key, chap->host_key_len);
drivers/nvme/host/auth.c int nvme_auth_generate_key(struct nvme_ctrl *ctrl, · line 928
Quoted 10 lines
drivers/nvme/host/auth.c +10
-0+kpp_request_set_output (req, &dst, chap->host_key_len); +kpp_request_set_callback (req,CRYPTO_TFM_REQ_MAY_BACKLOG , + crypto_req_done, &wait); + + ret =crypto_wait_req (crypto_kpp_generate_public_key (req), &wait); +if (ret == -EOVERFLOW ) { +dev_dbg (ctrl->dev, +"public key buffer too small, wants %d is %d\n" , +crypto_kpp_maxsize (chap->dh_tfm), chap->host_key_len); + goto out_free_host;
Is this a specific retcode of intereset? Why did you specifically add special casing here?
Quoted 79 lines
drivers/nvme/host/auth.c +57
-0+ }else if (ret) { +dev_dbg (ctrl->dev, +"failed to generate public key, error %d\n" , ret); + goto out_free_host; + } + + chap->sess_key =kmalloc (chap->sess_key_len,GFP_KERNEL ); +if (!chap->sess_key) + goto out_free_host; + +crypto_init_wait (&wait); +sg_init_one (&src, chap->ctrl_key, chap->ctrl_key_len); +kpp_request_set_input (req, &src, chap->ctrl_key_len); +sg_init_one (&dst, chap->sess_key, chap->sess_key_len); +kpp_request_set_output (req, &dst, chap->sess_key_len); +kpp_request_set_callback (req,CRYPTO_TFM_REQ_MAY_BACKLOG , + crypto_req_done, &wait); + + ret =crypto_wait_req (crypto_kpp_compute_shared_secret (req), &wait); +if (ret) { +dev_dbg (ctrl->dev, +"failed to generate shared secret, error %d\n" , ret); +kfree_sensitive (chap->sess_key); + chap->sess_key =NULL ; + chap->sess_key_len =0 ; + }else +dev_dbg (ctrl->dev,"shared secret %*ph\n" , + (int )chap->sess_key_len, chap->sess_key); +out_free_host: +if (ret) { +kfree (chap->host_key); + chap->host_key =NULL ; + chap->host_key_len =0 ; + } +out_free_req: +kpp_request_free (req); +out_free_exp: +kfree_sensitive (pkey); +if (ret) + chap->status =NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; +return ret; +} +void nvme_auth_free (struct nvme_dhchap_context *chap) {if (chap->shash_tfm)crypto_free_shash (chap->shash_tfm); +if (chap->digest_tfm) +crypto_free_shash (chap->digest_tfm); +if (chap->dh_tfm) +crypto_free_kpp (chap->dh_tfm);if (chap->key)kfree (chap->key);if (chap->ctrl_key)@@ -732,6 +1061,15 @@ int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)if (ret) goto fail2; +if (chap->ctrl_key_len) { +dev_dbg (ctrl->device, +"%s: qid %d DH-HMAC-DHAP DH exponential\n" , + __func__, qid); + ret =nvme_auth_dhchap_exponential (ctrl, chap); +if (ret) + goto fail2; + } +dev_dbg (ctrl->device,"%s: qid %d DH-HMAC-CHAP host response\n" , __func__, qid); ret =nvme_auth_dhchap_host_response (ctrl, chap);@@ -806,6 +1144,7 @@ int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid) ret = -EPROTO ;if (!ret) { ctrl->dhchap_hash = chap->hash_id; + ctrl->dhchap_dhgroup = chap->dhgroup_id; }kfree (buf);nvme_auth_free (chap);
Hannes Reinecke
maintainerRe: [PATCH 07/11] nvme-auth: augmented challenge support
Quoted 17 lines · Sagi Grimberg
Quoted 1 line · Hannes Reinecke
Implement support for augmented challenge using FFDHE groups.
Please some more info for the change log...
Quoted 10 lines
Signed-off-by Hannes Reinecke <[email protected]>---
drivers/nvme/host/auth.c| 403 +++++++++++++++++++++++++++++++++++----1 file changed, 371 insertions(+), 32 deletions(-)drivers/nvme/host/auth.c +0
-0
[ .. ]
Quoted 19 lines
Quoted 19 lines
drivers/nvme/host/auth.c +9
-4@@ -290,10 +382,24 @@ static int nvme_auth_dhchap_challenge(structnvme_ctrl *ctrl, return -EPROTO; } if (data->dhgid != NVME_AUTH_DHCHAP_DHGROUP_NULL) { - chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; - return -EPROTO; - } - if (data->dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen != 0) { + if (data->dhvlen == 0) { + dev_warn(ctrl->device, + "qid %d: DH-HMAC-CHAP: empty DH value\n", + chap->qid); + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; + return -EPROTO; + } + chap->dh_tfm = crypto_alloc_kpp(gid_name, 0, 0); + if (IS_ERR(chap->dh_tfm)) {
Quoted 9 lines
drivers/nvme/host/auth.c static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl, · line 399
Quoted 6 lines
drivers/nvme/host/auth.c +6
-0+ dev_warn(ctrl->device, + "qid %d: DH-HMAC-CHAP: failed to initialize %s\n", + chap->qid, gid_name); + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; + chap->dh_tfm = NULL; + return -EPROTO;Why not propogate the error?
Will be doing so.
Quoted 31 lines
Quoted 31 lines
drivers/nvme/host/auth.c +3
-0+ } + chap->dhgroup_id = data->dhgid; + } else if (data->dhvlen != 0) { dev_warn(ctrl->device,"qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n",
chap->qid);drivers/nvme/host/auth.c +12
-1@@ -313,6 +419,16 @@ static int nvme_auth_dhchap_challenge(structnvme_ctrl *ctrl, chap->hash_len = data->hl; chap->s1 = le32_to_cpu(data->seqnum); memcpy(chap->c1, data->cval, chap->hash_len); + if (data->dhvlen) { + chap->ctrl_key = kmalloc(data->dhvlen, GFP_KERNEL); + if (!chap->ctrl_key) + return -ENOMEM; + chap->ctrl_key_len = data->dhvlen; + memcpy(chap->ctrl_key, data->cval + chap->hash_len, + data->dhvlen); + dev_dbg(ctrl->device, "ctrl public key %*ph\n", + (int)chap->ctrl_key_len, chap->ctrl_key); + } return 0; }@@ -353,10 +469,13 @@ static int nvme_auth_dhchap_reply(structnvme_ctrl *ctrl, memcpy(data->rval + chap->hash_len, chap->c2, chap->hash_len); } - if (chap->host_key_len) + if (chap->host_key_len) { + dev_dbg(ctrl->device, "%s: qid %d host public key %*ph\n",
Quoted 9 lines
drivers/nvme/host/auth.c static int nvme_auth_dhchap_reply(struct nvme_ctrl *ctrl, · line 478
Quoted 6 lines
drivers/nvme/host/auth.c +3
-1+ __func__, chap->qid, + chap->host_key_len, chap->host_key); memcpy(data->rval + 2 * chap->hash_len, chap->host_key, chap->host_key_len); - + }Is this change only adding the debug print?
Might. I'll check.
Quoted 50 lines
Quoted 50 lines
drivers/nvme/host/auth.c +0
-0return size; }@@ -440,23 +559,10 @@ static int nvme_auth_dhchap_failure2(struct
nvme_ctrl*ctrl,
intnvme_auth_select_hash(struct nvme_ctrl *ctrl, struct nvme_dhchap_context *chap)
{
- char*hash_name;
+ const char*hash_name,*digest_name;
int ret;
- switch (chap->hash_id) {
- caseNVME_AUTH_DHCHAP_HASH_SHA256:
-hash_name= "hmac(sha256)";
- break;
- caseNVME_AUTH_DHCHAP_HASH_SHA384:
-hash_name= "hmac(sha384)";
- break;
- caseNVME_AUTH_DHCHAP_HASH_SHA512:
-hash_name= "hmac(sha512)";
- break;
- default:
-hash_name= NULL;
- break;
- }
+hash_name=nvme_auth_hmac_name(chap->hash_id);
if (!hash_name) {
chap->status=NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE; return-EPROTO;drivers/nvme/host/auth.c +14
-0@@ -468,26 +574,100 @@ int nvme_auth_select_hash(struct nvme_ctrl *ctrl, chap->shash_tfm = NULL; return -EPROTO; } + digest_name = nvme_auth_digest_name(chap->hash_id); + if (!digest_name) { + crypto_free_shash(chap->shash_tfm); + chap->shash_tfm = NULL; + return -EPROTO; + } + chap->digest_tfm = crypto_alloc_shash(digest_name, 0, 0); + if (IS_ERR(chap->digest_tfm)) { + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE; + crypto_free_shash(chap->shash_tfm); + chap->shash_tfm = NULL; + chap->digest_tfm = NULL; + return -EPROTO; + } if (!chap->key) { dev_warn(ctrl->device, "qid %d: cannot select hash, no key\n", chap->qid); chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
Quoted 9 lines
drivers/nvme/host/auth.c int nvme_auth_select_hash(struct nvme_ctrl *ctrl, · line 599
Quoted 5 lines
drivers/nvme/host/auth.c +2
-0+ crypto_free_shash(chap->digest_tfm); crypto_free_shash(chap->shash_tfm); chap->shash_tfm = NULL; + chap->digest_tfm = NULL; return -EINVAL;Please have a structured goto targets in reverse order, this repeated cleanup is a mess...
Already done.
Quoted 131 lines
Quoted 26 lines
drivers/nvme/host/auth.c +4
-2} ret = crypto_shash_setkey(chap->shash_tfm, chap->key, chap->key_len); if (ret) { chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE; + crypto_free_shash(chap->digest_tfm); crypto_free_shash(chap->shash_tfm); chap->shash_tfm = NULL; + chap->digest_tfm = NULL; return ret; } - dev_info(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n", - chap->qid, hash_name); + dev_dbg(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n", + chap->qid, hash_name); return 0; }+static int nvme_auth_augmented_challenge(struct
nvme_dhchap_context*chap,
+ u8 *challenge, u8 *aug)drivers/nvme/host/auth.c +6
-0+{ + struct crypto_shash *tfm; + struct shash_desc *desc; + u8 *hashed_key; + const char *hash_name; + int ret;drivers/nvme/host/auth.c int nvme_auth_select_hash(struct nvme_ctrl *ctrl, · line 632
Quoted 10 lines
drivers/nvme/host/auth.c +10
-0+ + hashed_key = kmalloc(chap->hash_len, GFP_KERNEL); + if (!hashed_key) + return -ENOMEM; + + ret = crypto_shash_tfm_digest(chap->digest_tfm, chap->sess_key, + chap->sess_key_len, hashed_key); + if (ret < 0) { + pr_debug("failed to hash session key, err %d\n", ret); + kfree(hashed_key);Same here...
Quoted 2 lines
+ return ret;
+ }Spaces between if conditions please?
Quoted 87 lines
+
hash_name=crypto_shash_alg_name(chap->shash_tfm); + if (!hash_name) {
+pr_debug("Invalid hash algoritm\n");
+ return-EINVAL;
+ }
+ tfm =crypto_alloc_shash(hash_name, 0, 0); + if (IS_ERR(tfm)) {
+ ret =PTR_ERR(tfm);
+ gotoout_free_key;
+ }
+ desc = kmalloc(sizeof(structshash_desc) +crypto_shash_descsize(tfm),
+GFP_KERNEL);
+ if (!desc) {
+ ret =-ENOMEM;
+ gotoout_free_hash;
+ }
+desc->tfm= tfm;drivers/nvme/host/auth.c +19
-0+ + ret = crypto_shash_setkey(tfm, hashed_key, chap->hash_len); + if (ret) + goto out_free_desc; + ret = crypto_shash_init(desc); + if (ret) + goto out_free_desc; + crypto_shash_update(desc, challenge, chap->hash_len); + crypto_shash_final(desc, aug); + +out_free_desc: + kfree_sensitive(desc); +out_free_hash: + crypto_free_shash(tfm); +out_free_key: + kfree(hashed_key); + return ret; +} +static int
nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl, struct nvme_dhchap_context *chap){drivers/nvme/host/auth.c +0
-0@@ -497,6 +677,16 @@ static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction %d\n",
__func__,chap->qid,chap->s1,chap->transaction); + if (chap->dh_tfm) {
+ challenge =kmalloc(chap->hash_len, GFP_KERNEL);
+ if (!challenge) {
+ ret =-ENOMEM;
+ goto out;
+ }
+ ret =nvme_auth_augmented_challenge(chap, chap->c1, challenge); + if (ret)
+ goto out;
+ }
shash->tfm=chap->shash_tfm;
ret =crypto_shash_init(shash);
if (ret)drivers/nvme/host/auth.c +0
-0@@ -532,6 +722,8 @@ static int nvme_auth_dhchap_host_response(structnvme_ctrl *ctrl, goto out; ret = crypto_shash_final(shash, chap->response);out:
+ if (challenge !=chap->c1)
+ kfree(challenge);
return ret;
}
@@ -542,6 +734,17 @@ static int nvme_auth_dhchap_ctrl_response(structnvme_ctrl*ctrl, u8 buf[4], *challenge =chap->c2;
int ret;
+ if (chap->dh_tfm) {
+ challenge =kmalloc(chap->hash_len, GFP_KERNEL); + if (!challenge) {
+ ret =-ENOMEM;
+ goto out;
+ }
+ ret =nvme_auth_augmented_challenge(chap, chap->c2, + challenge);
+ if (ret)
+ goto out;
+ }
dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction %d\n",
__func__,chap->qid,chap->s2,chap->transaction); dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n",drivers/nvme/host/auth.c +0
-0@@ -585,6 +788,8 @@ static int nvme_auth_dhchap_ctrl_response(struct
Quoted 9 lines
Quoted 6 lines
drivers/nvme/host/auth.c +0
-0nvme_ctrl *ctrl, goto out; ret = crypto_shash_final(shash, chap->response);out:
+ if (challenge !=chap->c2)
+ kfree(challenge);Just free ?! what about failing?
This is not an error condition, but rather the case when we need to construct an augmented challenge; in that case we'll allocate a temporary buffer in 'challenge', and copy it over into 'c2'.
Quoted 79 lines
Quoted 79 lines
return ret;
}
@@ -644,10 +849,134 @@ int nvme_auth_generate_key(structnvme_ctrl*ctrl,
return 0;
}
+static intnvme_auth_dhchap_exponential(struct nvme_ctrl *ctrl, + struct nvme_dhchap_context *chap)drivers/nvme/host/auth.c +14
-0+{ + struct kpp_request *req; + struct crypto_wait wait; + struct scatterlist src, dst; + u8 *pkey; + int ret, pkey_len; + + if (chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_2048 || + chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_3072 || + chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_4096 || + chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_6144 || + chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_8192) { + struct dh p = {0}; + int pubkey_size =
nvme_auth_dhgroup_pubkey_size(chap->dhgroup_id);drivers/nvme/host/auth.c +56
-0+ + ret = crypto_ffdhe_params(&p, pubkey_size << 3); + if (ret) { + dev_dbg(ctrl->device, + "failed to generate ffdhe params, error %d\n", + ret); + return ret; + } + p.key = chap->key; + p.key_size = chap->key_len; + + pkey_len = crypto_dh_key_len(&p); + pkey = kzalloc(pkey_len, GFP_KERNEL); + + get_random_bytes(pkey, pkey_len); + ret = crypto_dh_encode_key(pkey, pkey_len, &p); + if (ret) { + dev_dbg(ctrl->device, + "failed to encode pkey, error %d\n", ret); + kfree(pkey); + return ret; + } + chap->host_key_len = pubkey_size; + chap->sess_key_len = pubkey_size; + } else { + dev_warn(ctrl->device, "Invalid DH group id %d\n", + chap->dhgroup_id); + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD; + return -EINVAL; + } + + ret = crypto_kpp_set_secret(chap->dh_tfm, pkey, pkey_len); + if (ret) { + dev_dbg(ctrl->dev, "failed to set secret, error %d\n", ret); + kfree(pkey); + return ret; + } + req = kpp_request_alloc(chap->dh_tfm, GFP_KERNEL); + if (!req) { + ret = -ENOMEM; + goto out_free_exp; + } + + chap->host_key = kzalloc(chap->host_key_len, GFP_KERNEL); + if (!chap->host_key) { + ret = -ENOMEM; + goto out_free_req; + } + crypto_init_wait(&wait); + kpp_request_set_input(req, NULL, 0); + sg_init_one(&dst, chap->host_key, chap->host_key_len); + kpp_request_set_output(req, &dst, chap->host_key_len); + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &wait); + + ret = crypto_wait_req(crypto_kpp_generate_public_key(req), &wait);
Quoted 9 lines
Quoted 5 lines
+ if (ret ==
-EOVERFLOW) {
+ dev_dbg(ctrl->dev,
+ "public key buffer too small, wants %d is %d\n", +crypto_kpp_maxsize(chap->dh_tfm),chap->host_key_len); + gotoout_free_host;Is this a specific retcode of intereset? Why did you specifically add special casing here?
Because that's the specific error code from the DH code, indicating that the length isn't correct. And I needed that during development of the FFDHE code.
But yeah, it can be removed.
Cheers,
Hannes
Signature · 4 lines
Dr. Hannes Reinecke Kernel Storage Architect [email protected] +49 911 74053 688 SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg HRB 36809 (AG Nürnberg), GF: Felix Imendörffer
Hannes Reinecke
maintainerPATCH 8/11 nvmet: Parse fabrics commands on all queues
In reply to Hannes ReineckeFabrics commands might be sent to all queues, not just the admin one.
--- drivers/nvme/target/core.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/nvme/target/core.c +2 -0
@@ -942,6 +942,8 @@ bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq,if (unlikely (!req->sq->ctrl))/* will return an error for any non-connect command: */ status =nvmet_parse_connect_cmd (req); +else if (nvme_is_fabrics (req->cmd)) + status =nvmet_parse_fabrics_cmd (req);else if (likely (req->sq->qid !=0 )) status =nvmet_parse_io_cmd (req);else
Signature · 1 line
2.29.2
Re: [PATCH 08/11] nvmet: Parse fabrics commands on all queues
Hannes Reinecke
maintainerPATCH 9/11 nvmet: Implement basic In-Band Authentication
In reply to Hannes ReineckeImplement support for NVMe-oF In-Band authentication. This patch adds two additional configfs entries 'dhchap_key' and 'dhchap_hash' to the 'host' configfs directory. The 'dhchap_key' needs to be specified in the format outlined in the base spec. Augmented challenge support is not implemented, and concatenation with TLS encryption is not supported.
--- drivers/nvme/target/Kconfig | 10 + drivers/nvme/target/Makefile | 1 + drivers/nvme/target/admin-cmd.c | 4 + drivers/nvme/target/auth.c | 352 +++++++++++++++++++ drivers/nvme/target/configfs.c | 71 +++- drivers/nvme/target/core.c | 8 + drivers/nvme/target/fabrics-cmd-auth.c | 460 +++++++++++++++++++++++++ drivers/nvme/target/fabrics-cmd.c | 30 +- drivers/nvme/target/nvmet.h | 71 ++++ 9 files changed, 1004 insertions(+), 3 deletions(-) create mode 100644 drivers/nvme/target/auth.c create mode 100644 drivers/nvme/target/fabrics-cmd-auth.c
drivers/nvme/target/Kconfig +10 -0
@@ -85,3 +85,13 @@ config NVME_TARGET_TCP devices over TCP. If unsure, say N. + +config NVME_TARGET_AUTH + bool "NVMe over Fabrics In-band Authentication support" + depends on NVME_TARGET + select CRYPTO_SHA256 + select CRYPTO_SHA512 + help + This enables support for NVMe over Fabrics In-band Authentication + + If unsure, say N.
drivers/nvme/target/Makefile +1 -0
@@ -13,6 +13,7 @@ nvmet-y += core.o configfs.o admin-cmd.o fabrics-cmd.o \ discovery.o io-cmd-file.o io-cmd-bdev.o nvmet-$(CONFIG_NVME_TARGET_PASSTHRU) += passthru.o nvmet-$(CONFIG_BLK_DEV_ZONED) += zns.o +nvmet-$(CONFIG_NVME_TARGET_AUTH) += fabrics-cmd-auth.o auth.o nvme-loop-y += loop.o nvmet-rdma-y += rdma.o nvmet-fc-y += fc.o
drivers/nvme/target/admin-cmd.c +4 -0
@@ -1008,6 +1008,10 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)if (nvme_is_fabrics (cmd))return nvmet_parse_fabrics_cmd (req); + +if (unlikely (!nvmet_check_auth_status (req))) +return NVME_SC_AUTH_REQUIRED |NVME_SC_DNR ; +if (nvmet_req_subsys (req)->type ==NVME_NQN_DISC )return nvmet_parse_discovery_cmd (req);
drivers/nvme/target/auth.c new file +352 -0
352 lines not on this page · raw message
drivers/nvme/target/configfs.c +70 -1
94 lines not on this page · raw message
drivers/nvme/target/core.c +8 -0
@@ -793,6 +793,7 @@ void nvmet_sq_destroy(struct nvmet_sq *sq)wait_for_completion (&sq->confirm_done);wait_for_completion (&sq->free_done);percpu_ref_exit (&sq->ref); +nvmet_auth_sq_free (sq);if (ctrl) {/*
@@ -1264,6 +1265,11 @@ u16 nvmet_check_ctrl_status(struct nvmet_req *req) req->cmd->common.opcode, req->sq->qid);return NVME_SC_CMD_SEQ_ERROR |NVME_SC_DNR ; } + +if (unlikely (!nvmet_check_auth_status (req))) { +pr_warn ("qid %d not authenticated\n" , req->sq->qid); +return NVME_SC_AUTH_REQUIRED |NVME_SC_DNR ; + }return 0 ; }
@@ -1456,6 +1462,8 @@ static void nvmet_ctrl_free(struct kref *ref)flush_work (&ctrl->async_event_work);cancel_work_sync (&ctrl->fatal_err_work); +nvmet_reset_auth (ctrl); +ida_simple_remove (&cntlid_ida, ctrl->cntlid);nvmet_async_events_free (ctrl);
drivers/nvme/target/fabrics-cmd-auth.c new file +460 -0
460 lines not on this page · raw message
drivers/nvme/target/fabrics-cmd.c +28 -2
@@ -93,6 +93,14 @@ u16 nvmet_parse_fabrics_cmd(struct nvmet_req *req)case nvme_fabrics_type_property_get: req->execute = nvmet_execute_prop_get;break ; +#ifdef CONFIG_NVME_TARGET_AUTH +case nvme_fabrics_type_auth_send: + req->execute = nvmet_execute_auth_send; +break ; +case nvme_fabrics_type_auth_receive: + req->execute = nvmet_execute_auth_receive; +break ; +#endif default :pr_debug ("received unknown capsule type 0x%x\n" , cmd->fabrics.fctype);
@@ -155,6 +163,7 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req)struct nvmf_connect_data *d;struct nvmet_ctrl *ctrl =NULL ;u16 status =0 ; +int ret;if (!nvmet_check_transfer_len (req,sizeof (struct nvmf_connect_data )))return ;
@@ -197,17 +206,31 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req)uuid_copy (&ctrl->hostid, &d->hostid); + ret =nvmet_setup_auth (ctrl, req); +if (ret <0 ) { +pr_err ("Failed to setup authentication, error %d\n" , ret); +nvmet_ctrl_put (ctrl); +if (ret == -EPERM ) + status = (NVME_SC_CONNECT_INVALID_HOST |NVME_SC_DNR ); +else + status =NVME_SC_INTERNAL ; + goto out; + } + status =nvmet_install_queue (ctrl, req);if (status) {nvmet_ctrl_put (ctrl); goto out; } -pr_info ("creating controller %d for subsystem %s for NQN %s%s.\n" , +pr_info ("creating controller %d for subsystem %s for NQN %s%s%s.\n" , ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn, - ctrl->pi_support ?" T10-PI is enabled" :"" ); + ctrl->pi_support ?" T10-PI is enabled" :"" , +nvmet_has_auth (ctrl) ?" with DH-HMAC-CHAP" :"" ); req->cqe->result.u16 =cpu_to_le16 (ctrl->cntlid); +if (nvmet_has_auth (ctrl)) +nvmet_init_auth (ctrl, req); out:kfree (d); complete:
@@ -267,6 +290,9 @@ static void nvmet_execute_io_connect(struct nvmet_req *req) }pr_debug ("adding queue %d to ctrl %d.\n" , qid, ctrl->cntlid); + req->cqe->result.u16 =cpu_to_le16 (ctrl->cntlid); +if (nvmet_has_auth (ctrl)) +nvmet_init_auth (ctrl, req); out:kfree (d);
drivers/nvme/target/nvmet.h +71 -0
93 lines not on this page · raw message
Signature · 1 line
2.29.2
Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication
Am Freitag, 16. Juli 2021, 13:04:26 CEST schrieb Hannes Reinecke:
Hi Hannes,
drivers/nvme/target/Makefile nvmet-y += core.o configfs.o admin-cmd.o
Quoted 45 lines
Implement support for NVMe-oF In-Band authentication. This patch adds two additional configfs entries '
dhchap_key' and 'dhchap_hash' to the 'host' configfs directory. The 'dhchap_key' needs to be specified in the format outlined in the base spec. Augmented challenge support is not implemented, and concatenation with TLS encryption is not supported.Signed-off-by Hannes Reinecke <[email protected]>--- drivers/nvme/target/Kconfig | 10 + drivers/nvme/target/Makefile | 1 + drivers/nvme/target/admin-cmd.c | 4 + drivers/nvme/target/auth.c | 352 +++++++++++++++++++ drivers/nvme/target/configfs.c | 71 +++- drivers/nvme/target/core.c | 8 + drivers/nvme/target/fabrics-cmd-auth.c | 460 +++++++++++++++++++++++++ drivers/nvme/target/fabrics-cmd.c | 30 +- drivers/nvme/target/nvmet.h | 71 ++++ 9 files changed, 1004 insertions(+), 3 deletions(-) create mode 100644 drivers/nvme/target/auth.c create mode 100644 drivers/nvme/target/fabrics-cmd-auth.cdrivers/nvme/target/Kconfig +10
-0@@ -85,3 +85,13 @@ config NVME_TARGET_TCP devices over TCP. If unsure, say N. + +config NVME_TARGET_AUTH + bool "NVMe over Fabrics In-band Authentication support" + depends on NVME_TARGET + select CRYPTO_SHA256 + select CRYPTO_SHA512 + help + This enables support for NVMe over Fabrics In-band Authentication + + If unsure, say N.drivers/nvme/target/Makefile +0
-0@@ -13,6 +13,7 @@ nvmet-y += core.o configfs.o admin-cmd.o
fabrics-cmd.o \
Quoted 65 lines
drivers/nvme/target/Makefile +1
-0discovery.o io-cmd-file.o io-cmd-bdev.o nvmet-$(CONFIG_NVME_TARGET_PASSTHRU) += passthru.o nvmet-$(CONFIG_BLK_DEV_ZONED) += zns.o +nvmet-$(CONFIG_NVME_TARGET_AUTH) += fabrics-cmd-auth.o auth.o nvme-loop-y += loop.o nvmet-rdma-y += rdma.o nvmet-fc-y += fc.o(quoted diff) +0
-0
b/drivers/nvme/target/admin-cmd.cindex0cb98f2bbc8c..320cefc64ee0100644drivers/nvme/target/admin-cmd.c +4
-0@@ -1008,6 +1008,10 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)if (nvme_is_fabrics (cmd))return nvmet_parse_fabrics_cmd (req); + +if (unlikely (!nvmet_check_auth_status (req))) +return NVME_SC_AUTH_REQUIRED |NVME_SC_DNR ; +if (nvmet_req_subsys (req)->type ==NVME_NQN_DISC )return nvmet_parse_discovery_cmd (req);drivers/nvme/target/auth.c new file +37
-0@@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* NVMe over Fabrics DH-HMAC-CHAP authentication. +* Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions. +* All rights reserved. +*/ +#define pr_fmt (fmt) KBUILD_MODNAME ": " fmt +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <crypto/hash.h> +#include <crypto/kpp.h> +#include <crypto/dh.h> +#include <crypto/ffdhe.h> +#include <linux/crc32.h> +#include <linux/base64.h> +#include <linux/ctype.h> +#include <linux/random.h> +#include <asm/unaligned.h> + +#include "nvmet.h" +#include "../host/auth.h" + +int nvmet_auth_set_host_key (struct nvmet_host *host,const char *secret) +{ +if (sscanf (secret,"DHHC-1:%hhd:%*s" , &host->dhchap_key_hash) !=1 ) +return -EINVAL ; +if (host->dhchap_key_hash >3 ) { +pr_warn ("Invalid DH-HMAC-CHAP hash id %d\n" , + host->dhchap_key_hash); +return -EINVAL ; + } +if (host->dhchap_key_hash >0 ) { +/* Validate selected hash algorithm */ +const char *hmac =nvme_auth_hmac_name (host->dhchap_key_hash); +
drivers/nvme/target/auth.c line 47
Quoted 10 lines
drivers/nvme/target/auth.c new file +10
-0+if (!crypto_has_shash (hmac,0 ,0 )) { +pr_warn ("DH-HMAC-CHAP hash %s unsupported\n" , hmac); + host->dhchap_key_hash =-1 ; +return -EAGAIN ; + } +/* Use this hash as default */ +if (!host->dhchap_hash_id) + host->dhchap_hash_id = host->dhchap_key_hash; + } + host->dhchap_secret =kstrdup (secret,GFP_KERNEL );
Just like before - are you sure that the secret is an ASCII string and no binary blob?
Quoted 87 lines
drivers/nvme/target/auth.c new file +87
-0+if (!host->dhchap_secret) +return -ENOMEM ; +/* Default to SHA256 */ +if (!host->dhchap_hash_id) + host->dhchap_hash_id =NVME_AUTH_DHCHAP_HASH_SHA256 ; + +pr_debug ("Using hash %s\n" , +nvme_auth_hmac_name (host->dhchap_hash_id)); +return 0 ; +} + +int nvmet_setup_dhgroup (struct nvmet_ctrl *ctrl,int dhgroup_id) +{ +int ret = -ENOTSUPP ; + +if (dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_NULL ) +return 0 ; + +return ret; +} + +int nvmet_setup_auth (struct nvmet_ctrl *ctrl,struct nvmet_req *req) +{ +int ret =0 ; +struct nvmet_host_link *p; +struct nvmet_host *host =NULL ; +const char *hash_name; + +down_read (&nvmet_config_sem); +if (ctrl->subsys->type ==NVME_NQN_DISC ) + goto out_unlock; + +list_for_each_entry (p, &ctrl->subsys->hosts, entry) { +pr_debug ("check %s\n" ,nvmet_host_name (p->host)); +if (strcmp (nvmet_host_name (p->host), ctrl->hostnqn)) +continue ; + host = p->host; +break ; + } +if (!host) { +pr_debug ("host %s not found\n" , ctrl->hostnqn); + ret = -EPERM ; + goto out_unlock; + } +if (!host->dhchap_secret) { +pr_debug ("No authentication provided\n" ); + goto out_unlock; + } + + hash_name =nvme_auth_hmac_name (host->dhchap_hash_id); +if (!hash_name) { +pr_debug ("Hash ID %d invalid\n" , host->dhchap_hash_id); + ret = -EINVAL ; + goto out_unlock; + } + ctrl->shash_tfm =crypto_alloc_shash (hash_name,0 , +CRYPTO_ALG_ALLOCATES_MEMORY ); +if (IS_ERR (ctrl->shash_tfm)) { +pr_debug ("failed to allocate shash %s\n" , hash_name); + ret =PTR_ERR (ctrl->shash_tfm); + ctrl->shash_tfm =NULL ; + goto out_unlock; + } + + ctrl->dhchap_key =nvme_auth_extract_secret (host->dhchap_secret, + &ctrl->dhchap_key_len); +if (IS_ERR (ctrl->dhchap_key)) { +pr_debug ("failed to extract host key, error %d\n" , ret); + ret =PTR_ERR (ctrl->dhchap_key); + ctrl->dhchap_key =NULL ; + goto out_free_hash; + } +if (host->dhchap_key_hash) { +struct crypto_shash *key_tfm; + + hash_name =nvme_auth_hmac_name (host->dhchap_key_hash); + key_tfm =crypto_alloc_shash (hash_name,0 ,0 ); +if (IS_ERR (key_tfm)) { + ret =PTR_ERR (key_tfm); + goto out_free_hash; + }else { +SHASH_DESC_ON_STACK (shash, key_tfm); + + shash->tfm = key_tfm; + ret =crypto_shash_setkey (key_tfm, ctrl->dhchap_key, + ctrl->dhchap_key_len); +crypto_shash_init (shash);
drivers/nvme/target/auth.c line 144
Quoted 10 lines
drivers/nvme/target/auth.c new file +10
-0+crypto_shash_update (shash, ctrl->subsys->subsysnqn, +strlen (ctrl->subsys->subsysnqn)); +crypto_shash_update (shash,"NVMe-over-Fabrics" ,17 ); +crypto_shash_final (shash, ctrl->dhchap_key); +crypto_free_shash (key_tfm); + } + } +pr_debug ("%s: using key %*ph\n" , __func__, + (int )ctrl->dhchap_key_len, ctrl->dhchap_key); + ret = crypto_shash_setkey(ctrl->shash_tfm, ctrl->dhchap_key,
Is it truly necessary to keep the key around in ctrl->dhchap_key? It looks to me that this buffer is only used here and thus could be turned into a local variable. Keys flying around in memory is not a good idea. :-)
drivers/nvme/target/auth.c line 149
Quoted 5 lines
drivers/nvme/target/auth.c new file +5
-0+ ctrl->dhchap_key_len); +out_free_hash: +if (ret) { +if (ctrl->dhchap_key) { +kfree (ctrl->dhchap_key);
kfree_sensitive?
Quoted 9 lines
drivers/nvme/target/auth.c new file +9
-0+ ctrl->dhchap_key =NULL ; + } +crypto_free_shash (ctrl->shash_tfm); + ctrl->shash_tfm =NULL ; + } +out_unlock: +up_read (&nvmet_config_sem); + +return ret;
drivers/nvme/target/auth.c line 168
Quoted 10 lines
drivers/nvme/target/auth.c new file +10
-0+} + +void nvmet_auth_sq_free (struct nvmet_sq *sq) +{ +if (sq->dhchap_c1) +kfree (sq->dhchap_c1); +if (sq->dhchap_c2) +kfree (sq->dhchap_c2); +if (sq->dhchap_skey) +kfree (sq->dhchap_skey);
kfree_sensitive?
Quoted 4 lines
drivers/nvme/target/auth.c new file +4
-0+} + +void nvmet_reset_auth (struct nvmet_ctrl *ctrl) +{
drivers/nvme/target/auth.c line 182
Quoted 10 lines
drivers/nvme/target/auth.c new file +10
-0+if (ctrl->shash_tfm) { +crypto_free_shash (ctrl->shash_tfm); + ctrl->shash_tfm =NULL ; + } +if (ctrl->dh_tfm) { +crypto_free_kpp (ctrl->dh_tfm); + ctrl->dh_tfm =NULL ; + } +if (ctrl->dhchap_key) { +kfree (ctrl->dhchap_key);
kfree_sensitive?
Quoted 406 lines
drivers/nvme/target/auth.c new file +170
-0+ ctrl->dhchap_key =NULL ; + } +} + +bool nvmet_check_auth_status (struct nvmet_req *req) +{ +if (req->sq->ctrl->shash_tfm && + !req->sq->authenticated) +return false; +return true; +} + +int nvmet_auth_host_hash (struct nvmet_req *req,u8 *response, +unsigned int shash_len) +{ +struct nvmet_ctrl *ctrl = req->sq->ctrl; +SHASH_DESC_ON_STACK (shash, ctrl->shash_tfm); +u8 *challenge = req->sq->dhchap_c1; +u8 buf[4 ]; +int ret; + +if (ctrl->dh_gid !=NVME_AUTH_DHCHAP_DHGROUP_NULL ) { + ret = -ENOTSUPP ; + goto out; + } + + shash->tfm = ctrl->shash_tfm; + ret =crypto_shash_init (shash); +if (ret) + goto out; + ret =crypto_shash_update (shash, challenge, shash_len); +if (ret) + goto out; +put_unaligned_le32 (req->sq->dhchap_s1, buf); + ret =crypto_shash_update (shash, buf,4 ); +if (ret) + goto out; +put_unaligned_le16 (req->sq->dhchap_tid, buf); + ret =crypto_shash_update (shash, buf,2 ); +if (ret) + goto out; +memset (buf,0 ,4 ); + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash,"HostHost" ,8 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->hostnqn,strlen (ctrl->hostnqn)); +if (ret) + goto out; + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->subsysnqn, +strlen (ctrl->subsysnqn)); +if (ret) + goto out; + ret =crypto_shash_final (shash, response); +out: +if (challenge != req->sq->dhchap_c1) +kfree (challenge); +return 0 ; +} + +int nvmet_auth_ctrl_hash (struct nvmet_req *req,u8 *response, +unsigned int shash_len) +{ +struct nvmet_ctrl *ctrl = req->sq->ctrl; +SHASH_DESC_ON_STACK (shash, ctrl->shash_tfm); +u8 *challenge = req->sq->dhchap_c2; +u8 buf[4 ]; +int ret; + +pr_debug ("%s: ctrl %d hash seq %d transaction %u\n" , __func__, + ctrl->cntlid, req->sq->dhchap_s2, req->sq->dhchap_tid); +pr_debug ("%s: ctrl %d challenge %*ph\n" , __func__, + ctrl->cntlid, shash_len, req->sq->dhchap_c2); +pr_debug ("%s: ctrl %d subsysnqn %s\n" , __func__, + ctrl->cntlid, ctrl->subsysnqn); +pr_debug ("%s: ctrl %d hostnqn %s\n" , __func__, + ctrl->cntlid, ctrl->hostnqn); + +if (ctrl->dh_gid !=NVME_AUTH_DHCHAP_DHGROUP_NULL ) { + ret = -ENOTSUPP ; + goto out; + } + + shash->tfm = ctrl->shash_tfm; + ret =crypto_shash_init (shash); +if (ret) + goto out; + ret =crypto_shash_update (shash, challenge, shash_len); +if (ret) + goto out; +put_unaligned_le32 (req->sq->dhchap_s2, buf); + ret =crypto_shash_update (shash, buf,4 ); +if (ret) + goto out; +put_unaligned_le16 (req->sq->dhchap_tid, buf); + ret =crypto_shash_update (shash, buf,2 ); +if (ret) + goto out; +memset (buf,0 ,4 ); + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash,"Controller" ,10 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->subsysnqn, +strlen (ctrl->subsysnqn)); +if (ret) + goto out; + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->hostnqn,strlen (ctrl->hostnqn)); +if (ret) + goto out; + ret =crypto_shash_final (shash, response); +out: +if (challenge != req->sq->dhchap_c2) +kfree (challenge); +return 0 ; +} + +int nvmet_auth_ctrl_sesskey (struct nvmet_req *req, +u8 *pkey,int pkey_size) +{ +struct nvmet_ctrl *ctrl = req->sq->ctrl; +struct kpp_request *kpp_req; +struct crypto_wait wait; +struct scatterlist src, dst; +int ret; + + req->sq->dhchap_skey_len = +nvme_auth_dhgroup_privkey_size (ctrl->dh_gid); + req->sq->dhchap_skey =kzalloc (req->sq->dhchap_skey_len,GFP_KERNEL ); +if (!req->sq->dhchap_skey) +return -ENOMEM ; + kpp_req =kpp_request_alloc (ctrl->dh_tfm,GFP_KERNEL ); +if (!kpp_req) { +kfree (req->sq->dhchap_skey); + req->sq->dhchap_skey =NULL ; +return -ENOMEM ; + } + +pr_debug ("%s: host public key %*ph\n" , __func__, + (int )pkey_size, pkey); +crypto_init_wait (&wait); +sg_init_one (&src, pkey, pkey_size); +kpp_request_set_input (kpp_req, &src, pkey_size); +sg_init_one (&dst, req->sq->dhchap_skey, + req->sq->dhchap_skey_len); +kpp_request_set_output (kpp_req, &dst, req->sq->dhchap_skey_len); +kpp_request_set_callback (kpp_req,CRYPTO_TFM_REQ_MAY_BACKLOG , + crypto_req_done, &wait); + + ret =crypto_wait_req (crypto_kpp_compute_shared_secret (kpp_req), &wait); +kpp_request_free (kpp_req); +if (ret) +pr_debug ("failed to compute shared secred, err %d\n" , ret); +else +pr_debug ("%s: shared secret %*ph\n" , __func__, + (int )req->sq->dhchap_skey_len, + req->sq->dhchap_skey); + +return ret; +}drivers/nvme/target/configfs.c +70
-1@@ -11,8 +11,13 @@#include <linux/ctype.h> #include <linux/pci.h> #include <linux/pci-p2pdma.h> +#include <crypto/hash.h> +#include <crypto/kpp.h> #include "nvmet.h" +#ifdef CONFIG_NVME_TARGET_AUTH +#include "../host/auth.h" +#endif static const struct config_item_type nvmet_host_type;static const struct config_item_type nvmet_subsys_type;@@ -1656,10 +1661,71 @@ static const struct config_item_type nvmet_ports_type = { static struct config_group nvmet_subsystems_group;static struct config_group nvmet_ports_group; -static void nvmet_host_release (struct config_item *item) +#ifdef CONFIG_NVME_TARGET_AUTH +static ssize_t nvmet_host_dhchap_key_show (struct config_item *item, +char *page) +{ +u8 *dhchap_secret =to_host (item)->dhchap_secret; + +if (!dhchap_secret) +return sprintf (page,"\n" ); +return sprintf (page,"%s\n" , dhchap_secret); +} + +static ssize_t nvmet_host_dhchap_key_store (struct config_item *item, +const char *page,size_t count) {struct nvmet_host *host =to_host (item); +int ret; + ret =nvmet_auth_set_host_key (host, page); +if (ret <0 ) +return ret; +return count; +} + +CONFIGFS_ATTR (nvmet_host_, dhchap_key); + +static ssize_t nvmet_host_dhchap_hash_show (struct config_item *item, +char *page) +{ +struct nvmet_host *host =to_host (item); +const char *hash_name =nvme_auth_hmac_name (host->dhchap_hash_id); + +return sprintf (page,"%s\n" , hash_name ? hash_name :"none" ); +} + +static ssize_t nvmet_host_dhchap_hash_store (struct config_item *item, +const char *page,size_t count) +{ +struct nvmet_host *host =to_host (item); +int hmac_id; + + hmac_id =nvme_auth_hmac_id (page); +if (hmac_id <0 ) +return -EINVAL ; +if (!crypto_has_shash (nvme_auth_hmac_name (hmac_id),0 ,0 )) +return -ENOTSUPP ; + host->dhchap_hash_id = hmac_id; +return count; +} + +CONFIGFS_ATTR (nvmet_host_, dhchap_hash); + +static struct configfs_attribute *nvmet_host_attrs[] = { + &nvmet_host_attr_dhchap_key, + &nvmet_host_attr_dhchap_hash, +NULL , +}; +#endif /* CONFIG_NVME_TARGET_AUTH */ + +static void nvmet_host_release (struct config_item *item) +{ +struct nvmet_host *host =to_host (item); +#ifdef CONFIG_NVME_TARGET_AUTH +if (host->dhchap_secret) +kfree (host->dhchap_secret); +#endif kfree (host); }@@ -1669,6 +1735,9 @@ static struct configfs_item_operations nvmet_host_item_ops = {static const struct config_item_type nvmet_host_type = { .ct_item_ops = &nvmet_host_item_ops, +#ifdef CONFIG_NVME_TARGET_AUTH + .ct_attrs = nvmet_host_attrs, +#endif .ct_owner =THIS_MODULE , };drivers/nvme/target/core.c +8
-0@@ -793,6 +793,7 @@ void nvmet_sq_destroy(struct nvmet_sq *sq)wait_for_completion (&sq->confirm_done);wait_for_completion (&sq->free_done);percpu_ref_exit (&sq->ref); +nvmet_auth_sq_free (sq);if (ctrl) {/* @@ -1264,6 +1265,11 @@ u16 nvmet_check_ctrl_status(struct nvmet_req *req) req->cmd->common.opcode, req->sq->qid);return NVME_SC_CMD_SEQ_ERROR |NVME_SC_DNR ; } + +if (unlikely (!nvmet_check_auth_status (req))) { +pr_warn ("qid %d not authenticated\n" , req->sq->qid); +return NVME_SC_AUTH_REQUIRED |NVME_SC_DNR ; + }return 0 ; }@@ -1456,6 +1462,8 @@ static void nvmet_ctrl_free(struct kref *ref)flush_work (&ctrl->async_event_work);cancel_work_sync (&ctrl->fatal_err_work); +nvmet_reset_auth (ctrl); +ida_simple_remove (&cntlid_ida, ctrl->cntlid);nvmet_async_events_free (ctrl);(quoted diff) +0
-0
b/drivers/nvme/target/fabrics-cmd-auth.cnew file mode 100644 index 000000000000..962f9f5e9d89drivers/nvme/target/fabrics-cmd-auth.c new file +29
-0@@ -0,0 +1,460 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* NVMe over Fabrics DH-HMAC-CHAP authentication command handling. +* Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions. +* All rights reserved. +*/ +#define pr_fmt (fmt) KBUILD_MODNAME ": " fmt +#include <linux/blkdev.h> +#include <linux/random.h> +#include <crypto/hash.h> +#include <crypto/kpp.h> +#include "nvmet.h" +#include "../host/auth.h" + +void nvmet_init_auth (struct nvmet_ctrl *ctrl,struct nvmet_req *req) +{ +/* Initialize in-band authentication */ + req->sq->authenticated = false; + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE ; + req->cqe->result.u32 |=0x2 <<16 ; +} + +static u16 nvmet_auth_negotiate (struct nvmet_req *req,void *d) +{ +struct nvmet_ctrl *ctrl = req->sq->ctrl; +struct nvmf_auth_dhchap_negotiate_data *data = d; +int i, hash_id, null_dh =-1 ; + + pr_debug("%s: ctrl %d qid %d: data sc_d %d napd %d authid %d halen %ddhlen %d\n", +
__func__,ctrl->cntlid,req->sq->qid,drivers/nvme/target/fabrics-cmd-auth.c new file +22
-0+ data->sc_c, data->napd, data->auth_protocol[0 ].dhchap.authid, + data->auth_protocol[0 ].dhchap.halen, + data->auth_protocol[0].dhchap.dhlen); + req->sq->dhchap_tid =le16_to_cpu (data->t_id); +if (data->sc_c) +return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH ; + +if (data->napd !=1 ) +return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ; + +if (data->auth_protocol[0 ].dhchap.authid !=0x01 ) +return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; + + hash_id =nvme_auth_hmac_id (crypto_shash_alg_name (ctrl->shash_tfm)); +for (i =0 ; i < data->auth_protocol[0 ].dhchap.halen; i++) { +pr_debug ("%s: ctrl %d qid %d checking hash %d for %d\n" , + __func__, ctrl->cntlid, req->sq->qid, + data->auth_protocol[0 ].dhchap.idlist[i], hash_id); +if (hash_id != data->auth_protocol[0 ].dhchap.idlist[i]) +continue ; + req->sq->dhchap_hash_id = hash_id; + req->sq->dhchap_hash_len = crypto_shash_digestsize(ctrl-
shash_tfm);drivers/nvme/target/fabrics-cmd-auth.c new file +42
-0+break ; + } +if (req->sq->dhchap_hash_id ==0 ) { +pr_debug ("%s: ctrl %d qid %d: no usable hash found\n" , + __func__, ctrl->cntlid, req->sq->qid); +return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ; + } + +for (i = data->auth_protocol[0 ].dhchap.halen; + i < data->auth_protocol[0 ].dhchap.halen + + data->auth_protocol[0 ].dhchap.dhlen; i++) { +int dhgid = data->auth_protocol[0 ].dhchap.idlist[i]; + +if (dhgid ==NVME_AUTH_DHCHAP_DHGROUP_NULL ) { + null_dh = dhgid; +continue ; + } +if (nvmet_setup_dhgroup (ctrl, dhgid) ==0 ) +break ; + } +if (!ctrl->dh_tfm && null_dh <0 ) { +pr_debug ("%s: ctrl %d qid %d: no DH group selected\n" , + __func__, ctrl->cntlid, req->sq->qid); +return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; + } +if (ctrl->dh_gid ==-1 ) { + ctrl->dh_gid = null_dh; + ctrl->dh_tfm =NULL ; + } +pr_debug ("%s: ctrl %d qid %d: DH group %s (%d)\n" , + __func__, ctrl->cntlid, req->sq->qid, +nvme_auth_dhgroup_name (ctrl->dh_gid), ctrl->dh_gid); +return 0 ; +} + +static u16 nvmet_auth_reply (struct nvmet_req *req,void *d) +{ +struct nvmet_ctrl *ctrl = req->sq->ctrl; +struct nvmf_auth_dhchap_reply_data *data = d; +u8 *response; + + pr_debug("%s: ctrl %d qid %d: data hl %d cvalid %d dhvlen %d\n",
drivers/nvme/target/fabrics-cmd-auth.c line 104
Quoted 10 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +10
-0+ __func__, ctrl->cntlid, req->sq->qid, + data->hl, data->cvalid, data->dhvlen); +if (data->hl != req->sq->dhchap_hash_len) +return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; + +if (data->dhvlen) { +return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; + } + + response =kmalloc (data->hl,GFP_KERNEL );
Again, align to CRYPTO_MINALIGN_ATTR?
Quoted 94 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +94
-0+if (!response) +return NVME_AUTH_DHCHAP_FAILURE_FAILED ; + +if (nvmet_auth_host_hash (req, response, data->hl) <0 ) { +pr_debug ("ctrl %d qid %d DH-HMAC-CHAP hash failed\n" , + ctrl->cntlid, req->sq->qid); +kfree (response); +return NVME_AUTH_DHCHAP_FAILURE_FAILED ; + } + +if (memcmp (data->rval, response, data->hl)) { +pr_info ("ctrl %d qid %d DH-HMAC-CHAP response mismatch\n" , + ctrl->cntlid, req->sq->qid); +kfree (response); +return NVME_AUTH_DHCHAP_FAILURE_FAILED ; + } +kfree (response); +pr_info ("ctrl %d qid %d DH-HMAC-CHAP host authenticated\n" , + ctrl->cntlid, req->sq->qid); +if (data->cvalid) { + req->sq->dhchap_c2 =kmalloc (data->hl,GFP_KERNEL ); +if (!req->sq->dhchap_c2) +return NVME_AUTH_DHCHAP_FAILURE_FAILED ; +memcpy (req->sq->dhchap_c2, data->rval + data->hl, data->hl); + +pr_debug ("ctrl %d qid %d challenge %*ph\n" , + ctrl->cntlid, req->sq->qid, data->hl, + req->sq->dhchap_c2); + req->sq->dhchap_s2 =le32_to_cpu (data->seqnum); + }else + req->sq->dhchap_c2 =NULL ; + +return 0 ; +} + +static u16 nvmet_auth_failure2 (struct nvmet_req *req,void *d) +{ +struct nvmf_auth_dhchap_failure_data *data = d; + +return data->reason_code_explanation; +} + +void nvmet_execute_auth_send (struct nvmet_req *req) +{ +struct nvmet_ctrl *ctrl = req->sq->ctrl; +struct nvmf_auth_dhchap_success2_data *data; +void *d; +u32 tl; +u16 status =0 ; + +if (req->cmd->auth_send.secp !=NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER ) { + status =NVME_SC_INVALID_FIELD |NVME_SC_DNR ; + req->error_loc = + offsetof(struct nvmf_auth_send_command , secp); + goto done; + } +if (req->cmd->auth_send.spsp0 !=0x01 ) { + status =NVME_SC_INVALID_FIELD |NVME_SC_DNR ; + req->error_loc = + offsetof(struct nvmf_auth_send_command , spsp0); + goto done; + } +if (req->cmd->auth_send.spsp1 !=0x01 ) { + status =NVME_SC_INVALID_FIELD |NVME_SC_DNR ; + req->error_loc = + offsetof(struct nvmf_auth_send_command , spsp1); + goto done; + } + tl =le32_to_cpu (req->cmd->auth_send.tl); +if (!tl) { + status =NVME_SC_INVALID_FIELD |NVME_SC_DNR ; + req->error_loc = + offsetof(struct nvmf_auth_send_command , tl); + goto done; + } +if (!nvmet_check_transfer_len (req, tl)) { +pr_debug ("%s: transfer length mismatch (%u)\n" , __func__, tl); +return ; + } + + d =kmalloc (tl,GFP_KERNEL ); +if (!d) { + status =NVME_SC_INTERNAL ; + goto done; + } + + status =nvmet_copy_from_sgl (req,0 , d, tl); +if (status) { +kfree (d); + goto done; + } + + data = d; + pr_debug("%s: ctrl %d qid %d type %d id %d step %x\n", __func__,
drivers/nvme/target/fabrics-cmd-auth.c
Quoted 10 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +10
-0+ ctrl->cntlid, req->sq->qid, data->auth_type, data->auth_id, + req->sq->dhchap_step); +if (data->auth_type !=NVME_AUTH_COMMON_MESSAGES && + data->auth_type !=NVME_AUTH_DHCHAP_MESSAGES ) { + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ; + req->sq->dhchap_status =NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE ; + }else if (data->auth_type ==NVME_AUTH_COMMON_MESSAGES ) { +if (data->auth_id != req->sq->dhchap_step) { + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ; + req->sq->dhchap_status =
NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
drivers/nvme/target/fabrics-cmd-auth.c
Quoted 3 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +3
-0+ }else if (data->auth_id !=NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE ) { + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ; + req->sq->dhchap_status =
NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
drivers/nvme/target/fabrics-cmd-auth.c
Quoted 5 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +5
-0+ }else { +/* Validate negotiation parameters */ + status =nvmet_auth_negotiate (req, d); +if (status ==0 ) + req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
drivers/nvme/target/fabrics-cmd-auth.c
Quoted 2 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +2
-0+else { + req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
drivers/nvme/target/fabrics-cmd-auth.c
Quoted 11 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +11
-0+ req->sq->dhchap_status = status; + status =0 ; + } + } + }else if (data->auth_type ==NVME_AUTH_DHCHAP_MESSAGES ) { +if (data->auth_id != req->sq->dhchap_step) { +pr_debug ("%s: ctrl %d qid %d step mismatch (%d != %d)\n" , + __func__, ctrl->cntlid, req->sq->qid, + data->auth_id, req->sq->dhchap_step); + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ; + req->sq->dhchap_status =
NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
drivers/nvme/target/fabrics-cmd-auth.c
Quoted 2 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +2
-0+ }else if (le16_to_cpu (data->t_id) != req->sq->dhchap_tid) { + pr_debug("%s: ctrl %d qid %d invalid transaction %d
(expected %d)\n",
drivers/nvme/target/fabrics-cmd-auth.c
Quoted 5 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +5
-0+ __func__, ctrl->cntlid, req->sq->qid, +le16_to_cpu (data->t_id), + req->sq->dhchap_tid); + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ; + req->sq->dhchap_status =
NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
drivers/nvme/target/fabrics-cmd-auth.c
Quoted 6 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +6
-0+ }else { +switch (data->auth_id) { +case NVME_AUTH_DHCHAP_MESSAGE_REPLY : + status =nvmet_auth_reply (req, d); +if (status ==0 ) + req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1;
drivers/nvme/target/fabrics-cmd-auth.c
Quoted 2 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +2
-0+else { + req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
drivers/nvme/target/fabrics-cmd-auth.c
Quoted 13 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +13
-0+ req->sq->dhchap_status = status; + status =0 ; + } +break ; +case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 : + req->sq->authenticated = true; +pr_debug ("%s: ctrl %d qid %d authenticated\n" , + __func__, ctrl->cntlid, req->sq->qid); +break ; +case NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 : + status =nvmet_auth_failure2 (req, d); +if (status) { + pr_warn("ctrl %d qid %d: DH-HMAC-CHAP
negotiation failed (%d)\n",
drivers/nvme/target/fabrics-cmd-auth.c
Quoted 8 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +8
-0+ ctrl->cntlid, req->sq->qid, + status); + req->sq->dhchap_status = status; + status =0 ; + } +break ; +default : + req->sq->dhchap_status =
NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
drivers/nvme/target/fabrics-cmd-auth.c
Quoted 1 line
drivers/nvme/target/fabrics-cmd-auth.c new file +1
-0+ req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
Quoted 90 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +90
-0+break ; + } + } + }else { + req->sq->dhchap_status =NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE ; + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 ; + } +kfree (d); +done: +pr_debug ("%s: ctrl %d qid %d dhchap status %x step %x\n" , __func__, + ctrl->cntlid, req->sq->qid, + req->sq->dhchap_status, req->sq->dhchap_step); +if (status) +pr_debug ("%s: ctrl %d qid %d nvme status %x error loc %d\n" , + __func__, ctrl->cntlid, req->sq->qid, + status, req->error_loc); + req->cqe->result.u64 =0 ; +nvmet_req_complete (req, status); +if (req->sq->dhchap_step !=NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 && + req->sq->dhchap_step !=NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 ) +return ; +/* Final states, clear up variables */ +kfree (req->sq->dhchap_c1); +kfree (req->sq->dhchap_c2); +if (req->sq->dhchap_step ==NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 ) +nvmet_ctrl_fatal_error (ctrl); +} + +static int nvmet_auth_challenge (struct nvmet_req *req,void *d,int al) +{ +struct nvmf_auth_dhchap_challenge_data *data = d; +struct nvmet_ctrl *ctrl = req->sq->ctrl; +int ret =0 ; +int data_size =sizeof (*d) + req->sq->dhchap_hash_len; + +if (al < data_size) { +pr_debug ("%s: buffer too small (al %d need %d)\n" , __func__, + al, data_size); +return -EINVAL ; + } +memset (data,0 , data_size); + req->sq->dhchap_s1 = ctrl->dhchap_seqnum++; + data->auth_type =NVME_AUTH_DHCHAP_MESSAGES ; + data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE ; + data->t_id =cpu_to_le16 (req->sq->dhchap_tid); + data->hashid = req->sq->dhchap_hash_id; + data->hl = req->sq->dhchap_hash_len; + data->seqnum =cpu_to_le32 (req->sq->dhchap_s1); + req->sq->dhchap_c1 =kmalloc (data->hl,GFP_KERNEL ); +if (!req->sq->dhchap_c1) +return -ENOMEM ; +get_random_bytes (req->sq->dhchap_c1, data->hl); +memcpy (data->cval, req->sq->dhchap_c1, data->hl); +pr_debug ("%s: ctrl %d qid %d seq %d transaction %d hl %d dhvlen %d\n" , + __func__, ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1, + req->sq->dhchap_tid, data->hl, data->dhvlen); +return ret; +} + +static int nvmet_auth_success1 (struct nvmet_req *req,void *d,int al) +{ +struct nvmf_auth_dhchap_success1_data *data = d; +struct nvmet_ctrl *ctrl = req->sq->ctrl; + +WARN_ON (al <sizeof (*data)); +memset (data,0 ,sizeof (*data)); + data->auth_type =NVME_AUTH_DHCHAP_MESSAGES ; + data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1 ; + data->t_id =cpu_to_le16 (req->sq->dhchap_tid); + data->hl = req->sq->dhchap_hash_len; +if (req->sq->dhchap_c2) { +if (nvmet_auth_ctrl_hash (req, data->rval, data->hl)) +return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ; + data->rvalid =1 ; +pr_debug ("ctrl %d qid %d response %*ph\n" , + ctrl->cntlid, req->sq->qid, data->hl, data->rval); + } +return 0 ; +} + +static void nvmet_auth_failure1 (struct nvmet_req *req,void *d,int al) +{ +struct nvmf_auth_dhchap_failure_data *data = d; + +WARN_ON (al <sizeof (*data)); + data->auth_type =NVME_AUTH_COMMON_MESSAGES ; + data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ; + data->t_id =cpu_to_le32 (req->sq->dhchap_tid); + data->reason_code =NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED ; + data->reason_code_explanation = req->sq->dhchap_status;
drivers/nvme/target/fabrics-cmd-auth.c
Quoted 10 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +10
-0+} + +void nvmet_execute_auth_receive (struct nvmet_req *req) +{ +struct nvmet_ctrl *ctrl = req->sq->ctrl; +void *d; +u32 al; +u16 status =0 ; + +if (req->cmd->auth_receive.secp !=NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER )
{
Quoted 265 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +94
-0+ status =NVME_SC_INVALID_FIELD |NVME_SC_DNR ; + req->error_loc = + offsetof(struct nvmf_auth_receive_command , secp); + goto done; + } +if (req->cmd->auth_receive.spsp0 !=0x01 ) { + status =NVME_SC_INVALID_FIELD |NVME_SC_DNR ; + req->error_loc = + offsetof(struct nvmf_auth_receive_command , spsp0); + goto done; + } +if (req->cmd->auth_receive.spsp1 !=0x01 ) { + status =NVME_SC_INVALID_FIELD |NVME_SC_DNR ; + req->error_loc = + offsetof(struct nvmf_auth_receive_command , spsp1); + goto done; + } + al =le32_to_cpu (req->cmd->auth_receive.al); +if (!al) { + status =NVME_SC_INVALID_FIELD |NVME_SC_DNR ; + req->error_loc = + offsetof(struct nvmf_auth_receive_command , al); + goto done; + } +if (!nvmet_check_transfer_len (req, al)) { +pr_debug ("%s: transfer length mismatch (%u)\n" , __func__, al); +return ; + } + + d =kmalloc (al,GFP_KERNEL ); +if (!d) { + status =NVME_SC_INTERNAL ; + goto done; + } +pr_debug ("%s: ctrl %d qid %d step %x\n" , __func__, + ctrl->cntlid, req->sq->qid, req->sq->dhchap_step); +switch (req->sq->dhchap_step) { +case NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE : + status =nvmet_auth_challenge (req, d, al); +if (status <0 ) { +pr_warn ("ctrl %d qid %d: challenge error (%d)\n" , + ctrl->cntlid, req->sq->qid, status); + status =NVME_SC_INTERNAL ; +break ; + } +if (status) { + req->sq->dhchap_status = status; +nvmet_auth_failure1 (req, d, al); +pr_warn ("ctrl %d qid %d: challenge status (%x)\n" , + ctrl->cntlid, req->sq->qid, + req->sq->dhchap_status); + status =0 ; +break ; + } + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_REPLY ; +break ; +case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1 : + status =nvmet_auth_success1 (req, d, al); +if (status) { + req->sq->dhchap_status = status; +nvmet_auth_failure1 (req, d, al); +pr_warn ("ctrl %d qid %d: success1 status (%x)\n" , + ctrl->cntlid, req->sq->qid, + req->sq->dhchap_status); +break ; + } + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 ; +break ; +case NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 : +nvmet_auth_failure1 (req, d, al); +pr_warn ("ctrl %d qid %d failure1 (%x)\n" , + ctrl->cntlid, req->sq->qid, req->sq->dhchap_status); +break ; +default : +pr_warn ("ctrl %d qid %d unhandled step (%d)\n" , + ctrl->cntlid, req->sq->qid, req->sq->dhchap_step); + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ; + req->sq->dhchap_status =NVME_AUTH_DHCHAP_FAILURE_FAILED ; +nvmet_auth_failure1 (req, d, al); + status =0 ; +break ; + } + + status =nvmet_copy_to_sgl (req,0 , d, al); +kfree (d); +done: + req->cqe->result.u64 =0 ; +nvmet_req_complete (req, status); +if (req->sq->dhchap_step ==NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ) { +kfree (req->sq->dhchap_c1); +kfree (req->sq->dhchap_c2); +nvmet_ctrl_fatal_error (ctrl); + } +}(quoted diff) +0
-0
b/drivers/nvme/target/fabrics-cmd.cindex7d0f3523fdab..53fb853cd8fe100644drivers/nvme/target/fabrics-cmd.c +25
-2@@ -93,6 +93,14 @@ u16 nvmet_parse_fabrics_cmd(struct nvmet_req *req)case nvme_fabrics_type_property_get: req->execute = nvmet_execute_prop_get;break ; +#ifdef CONFIG_NVME_TARGET_AUTH +case nvme_fabrics_type_auth_send: + req->execute = nvmet_execute_auth_send; +break ; +case nvme_fabrics_type_auth_receive: + req->execute = nvmet_execute_auth_receive; +break ; +#endif default :pr_debug ("received unknown capsule type 0x%x\n" , cmd->fabrics.fctype);@@ -155,6 +163,7 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req) struct nvmf_connect_data *d;struct nvmet_ctrl *ctrl =NULL ;u16 status =0 ; +int ret;if (!nvmet_check_transfer_len (req,sizeof (struct nvmf_connect_data )))return ;@@ -197,17 +206,31 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req) uuid_copy(&ctrl->hostid, &d->hostid); + ret =nvmet_setup_auth (ctrl, req); +if (ret <0 ) { +pr_err ("Failed to setup authentication, error %d\n" , ret); +nvmet_ctrl_put (ctrl); +if (ret == -EPERM ) + status = (NVME_SC_CONNECT_INVALID_HOST |NVME_SC_DNR ); +else + status =NVME_SC_INTERNAL ; + goto out; + } + status =nvmet_install_queue (ctrl, req);if (status) {nvmet_ctrl_put (ctrl); goto out; } -pr_info ("creating controller %d for subsystem %s for NQN %s%s.\n" , +pr_info ("creating controller %d for subsystem %s for NQN %s%s%s.\n" , ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn, - ctrl->pi_support ?" T10-PI is enabled" :"" ); + ctrl->pi_support ?" T10-PI is enabled" :"" , +nvmet_has_auth (ctrl) ?" with DH-HMAC-CHAP" :"" ); req->cqe->result.u16 =cpu_to_le16 (ctrl->cntlid); +if (nvmet_has_auth (ctrl)) +nvmet_init_auth (ctrl, req); out:kfree (d);complete:
drivers/nvme/target/fabrics-cmd.c +3
-0@@ -267,6 +290,9 @@ static void nvmet_execute_io_connect(struct nvmet_req *req) }pr_debug ("adding queue %d to ctrl %d.\n" , qid, ctrl->cntlid); + req->cqe->result.u16 =cpu_to_le16 (ctrl->cntlid); +if (nvmet_has_auth (ctrl)) +nvmet_init_auth (ctrl, req); out:kfree (d);drivers/nvme/target/nvmet.h +27
-0@@ -108,6 +108,20 @@ struct nvmet_sq {u16 size;u32 sqhd;bool sqhd_disabled; +#ifdef CONFIG_NVME_TARGET_AUTH +bool authenticated; +u16 dhchap_tid; +u16 dhchap_status; +int dhchap_step; +u8 dhchap_hash_id; +u8 dhchap_hash_len; +u8 *dhchap_c1; +u8 *dhchap_c2; +u32 dhchap_s1; +u32 dhchap_s2; +u8 *dhchap_skey; +int dhchap_skey_len; +#endif struct completion free_done;struct completion confirm_done; };@@ -209,6 +223,15 @@ struct nvmet_ctrl {u64 err_counter;struct nvme_error_slot slots[NVMET_ERROR_LOG_SLOTS ];bool pi_support; +#ifdef CONFIG_NVME_TARGET_AUTH +u32 dhchap_seqnum; +u8 *dhchap_key; +size_t dhchap_key_len; +struct crypto_shash *shash_tfm; +struct crypto_kpp *dh_tfm; +u32 dh_gid; +u32 dh_keysize; +#endif };struct nvmet_subsys {@@ -270,6 +293,10 @@ static inline struct nvmet_subsys *namespaces_to_subsys ( struct nvmet_host {struct config_group group; +u8 *dhchap_secret; +u8 dhchap_key_hash; +u8 dhchap_hash_id; +u8 dhchap_dhgroup_id; };static inline struct
nvmet_host*to_host(structconfig_item*item)drivers/nvme/target/nvmet.h +42
-0@@ -659,4 +686,48 @@ static inline void nvmet_req_bio_put(struct nvmet_req *req, struct bio *bio) bio_put(bio); } +#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_host_key (struct nvmet_host *host,const char *secret); +int nvmet_auth_set_host_hash (struct nvmet_host *host,const char *hash); +int nvmet_setup_auth (struct nvmet_ctrl *ctrl,struct nvmet_req *req); +void nvmet_init_auth (struct nvmet_ctrl *ctrl,struct nvmet_req *req); +void nvmet_reset_auth (struct nvmet_ctrl *ctrl); +void nvmet_auth_sq_free (struct nvmet_sq *sq); +int nvmet_setup_dhgroup (struct nvmet_ctrl *ctrl,int dhgroup_id); +bool nvmet_check_auth_status (struct nvmet_req *req); +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) +{ +return ctrl->shash_tfm !=NULL ; +} +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); +#else +static inline int nvmet_setup_auth (struct nvmet_ctrl *ctrl, +struct nvmet_req *req) +{ +return 0 ; +} +static inline void nvmet_init_auth (struct nvmet_ctrl *ctrl, +struct nvmet_req *req) {}; +static inline void nvmet_reset_auth (struct nvmet_ctrl *ctrl) {}; +static inline void nvmet_auth_sq_free (struct nvmet_sq *sq) {}; +static inline bool nvmet_check_auth_status (struct nvmet_req *req) +{ +return true; +} +static inline bool nvmet_has_auth (struct nvmet_ctrl *ctrl) +{ +return false; +} +static inline const char *nvmet_dhchap_dhgroup_name (int dhgid) {return
Quoted 3 lines
NULL; } +#endif
+
#endif /*_NVMET_H*/
Ciao
Stephan
Hannes Reinecke
maintainerRe: [PATCH 09/11] nvmet: Implement basic In-Band Authentication
Quoted 120 lines · Stephan Müller
Am Freitag, 16. Juli 2021, 13:04:26 CEST schrieb Hannes Reinecke:
Hi Hannes,
drivers/nvme/target/Makefile nvmet-y += core.o configfs.o admin-cmd.o
Quoted 45 lines
Implement support for NVMe-oF In-Band authentication. This patch adds two additional configfs entries '
dhchap_key' and 'dhchap_hash' to the 'host' configfs directory. The 'dhchap_key' needs to be specified in the format outlined in the base spec. Augmented challenge support is not implemented, and concatenation with TLS encryption is not supported.Signed-off-by Hannes Reinecke <[email protected]>--- drivers/nvme/target/Kconfig | 10 + drivers/nvme/target/Makefile | 1 + drivers/nvme/target/admin-cmd.c | 4 + drivers/nvme/target/auth.c | 352 +++++++++++++++++++ drivers/nvme/target/configfs.c | 71 +++- drivers/nvme/target/core.c | 8 + drivers/nvme/target/fabrics-cmd-auth.c | 460 +++++++++++++++++++++++++ drivers/nvme/target/fabrics-cmd.c | 30 +- drivers/nvme/target/nvmet.h | 71 ++++ 9 files changed, 1004 insertions(+), 3 deletions(-)create mode 100644
drivers/nvme/target/auth.ccreate mode 100644drivers/nvme/target/fabrics-cmd-auth.cdrivers/nvme/target/Kconfig +10
-0@@ -85,3 +85,13 @@ config NVME_TARGET_TCP devices over TCP. If unsure, say N. + +config NVME_TARGET_AUTH + bool "NVMe over Fabrics In-band Authentication support" + depends on NVME_TARGET + select CRYPTO_SHA256 + select CRYPTO_SHA512 + help + This enables support for NVMe over Fabrics In-band Authentication + + If unsure, say N.drivers/nvme/target/Makefile +0
-0@@ -13,6 +13,7 @@ nvmet-y += core.o configfs.o admin-cmd.ofabrics-cmd.o \
Quoted 70 lines
drivers/nvme/target/Makefile +1
-0discovery.o io-cmd-file.o io-cmd-bdev.o nvmet-$(CONFIG_NVME_TARGET_PASSTHRU) += passthru.o nvmet-$(CONFIG_BLK_DEV_ZONED) += zns.o +nvmet-$(CONFIG_NVME_TARGET_AUTH) += fabrics-cmd-auth.o auth.o nvme-loop-y += loop.o nvmet-rdma-y += rdma.o nvmet-fc-y += fc.o(quoted diff) +0
-0
b/drivers/nvme/target/admin-cmd.cindex0cb98f2bbc8c..320cefc64ee0100644drivers/nvme/target/admin-cmd.c +4
-0@@ -1008,6 +1008,10 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)if (nvme_is_fabrics (cmd))return nvmet_parse_fabrics_cmd (req); + +if (unlikely (!nvmet_check_auth_status (req))) +return NVME_SC_AUTH_REQUIRED |NVME_SC_DNR ; +if (nvmet_req_subsys (req)->type ==NVME_NQN_DISC )return nvmet_parse_discovery_cmd (req);drivers/nvme/target/auth.c new file +42
-0@@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* NVMe over Fabrics DH-HMAC-CHAP authentication. +* Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions. +* All rights reserved. +*/ +#define pr_fmt (fmt) KBUILD_MODNAME ": " fmt +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <crypto/hash.h> +#include <crypto/kpp.h> +#include <crypto/dh.h> +#include <crypto/ffdhe.h> +#include <linux/crc32.h> +#include <linux/base64.h> +#include <linux/ctype.h> +#include <linux/random.h> +#include <asm/unaligned.h> + +#include "nvmet.h" +#include "../host/auth.h" + +int nvmet_auth_set_host_key (struct nvmet_host *host,const char *secret) +{ +if (sscanf (secret,"DHHC-1:%hhd:%*s" , &host->dhchap_key_hash) !=1 ) +return -EINVAL ; +if (host->dhchap_key_hash >3 ) { +pr_warn ("Invalid DH-HMAC-CHAP hash id %d\n" , + host->dhchap_key_hash); +return -EINVAL ; + } +if (host->dhchap_key_hash >0 ) { +/* Validate selected hash algorithm */ +const char *hmac =nvme_auth_hmac_name (host->dhchap_key_hash); + +if (!crypto_has_shash (hmac,0 ,0 )) { +pr_warn ("DH-HMAC-CHAP hash %s unsupported\n" , hmac); + host->dhchap_key_hash =-1 ; +return -EAGAIN ; + }
Quoted 9 lines
drivers/nvme/target/auth.c line 47
Quoted 5 lines
drivers/nvme/target/auth.c new file +5
-0+/* Use this hash as default */ +if (!host->dhchap_hash_id) + host->dhchap_hash_id = host->dhchap_key_hash; + } + host->dhchap_secret =kstrdup (secret,GFP_KERNEL );Just like before - are you sure that the secret is an ASCII string and no binary blob?
That is ensured by the transport encoding (cf NVMe Base Specification version 2.0). Also, this information is being passed in via the configfs interface, so it's bounded by PAGE_SIZE. But yes, we should be inserting a terminating 'NULL' character at the end of the page to ensure we don't incur an buffer overrun. Any other failure will be checked for during base64 decoding.
Quoted 93 lines
Quoted 93 lines
drivers/nvme/target/auth.c new file +93
-0+if (!host->dhchap_secret) +return -ENOMEM ; +/* Default to SHA256 */ +if (!host->dhchap_hash_id) + host->dhchap_hash_id =NVME_AUTH_DHCHAP_HASH_SHA256 ; + +pr_debug ("Using hash %s\n" , +nvme_auth_hmac_name (host->dhchap_hash_id)); +return 0 ; +} + +int nvmet_setup_dhgroup (struct nvmet_ctrl *ctrl,int dhgroup_id) +{ +int ret = -ENOTSUPP ; + +if (dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_NULL ) +return 0 ; + +return ret; +} + +int nvmet_setup_auth (struct nvmet_ctrl *ctrl,struct nvmet_req *req) +{ +int ret =0 ; +struct nvmet_host_link *p; +struct nvmet_host *host =NULL ; +const char *hash_name; + +down_read (&nvmet_config_sem); +if (ctrl->subsys->type ==NVME_NQN_DISC ) + goto out_unlock; + +list_for_each_entry (p, &ctrl->subsys->hosts, entry) { +pr_debug ("check %s\n" ,nvmet_host_name (p->host)); +if (strcmp (nvmet_host_name (p->host), ctrl->hostnqn)) +continue ; + host = p->host; +break ; + } +if (!host) { +pr_debug ("host %s not found\n" , ctrl->hostnqn); + ret = -EPERM ; + goto out_unlock; + } +if (!host->dhchap_secret) { +pr_debug ("No authentication provided\n" ); + goto out_unlock; + } + + hash_name =nvme_auth_hmac_name (host->dhchap_hash_id); +if (!hash_name) { +pr_debug ("Hash ID %d invalid\n" , host->dhchap_hash_id); + ret = -EINVAL ; + goto out_unlock; + } + ctrl->shash_tfm =crypto_alloc_shash (hash_name,0 , +CRYPTO_ALG_ALLOCATES_MEMORY ); +if (IS_ERR (ctrl->shash_tfm)) { +pr_debug ("failed to allocate shash %s\n" , hash_name); + ret =PTR_ERR (ctrl->shash_tfm); + ctrl->shash_tfm =NULL ; + goto out_unlock; + } + + ctrl->dhchap_key =nvme_auth_extract_secret (host->dhchap_secret, + &ctrl->dhchap_key_len); +if (IS_ERR (ctrl->dhchap_key)) { +pr_debug ("failed to extract host key, error %d\n" , ret); + ret =PTR_ERR (ctrl->dhchap_key); + ctrl->dhchap_key =NULL ; + goto out_free_hash; + } +if (host->dhchap_key_hash) { +struct crypto_shash *key_tfm; + + hash_name =nvme_auth_hmac_name (host->dhchap_key_hash); + key_tfm =crypto_alloc_shash (hash_name,0 ,0 ); +if (IS_ERR (key_tfm)) { + ret =PTR_ERR (key_tfm); + goto out_free_hash; + }else { +SHASH_DESC_ON_STACK (shash, key_tfm); + + shash->tfm = key_tfm; + ret =crypto_shash_setkey (key_tfm, ctrl->dhchap_key, + ctrl->dhchap_key_len); +crypto_shash_init (shash); +crypto_shash_update (shash, ctrl->subsys->subsysnqn, +strlen (ctrl->subsys->subsysnqn)); +crypto_shash_update (shash,"NVMe-over-Fabrics" ,17 ); +crypto_shash_final (shash, ctrl->dhchap_key); +crypto_free_shash (key_tfm); + }
Quoted 9 lines
drivers/nvme/target/auth.c line 144
Quoted 4 lines
drivers/nvme/target/auth.c new file +4
-0+ } +pr_debug ("%s: using key %*ph\n" , __func__, + (int )ctrl->dhchap_key_len, ctrl->dhchap_key); + ret = crypto_shash_setkey(ctrl->shash_tfm, ctrl->dhchap_key,Is it truly necessary to keep the key around in
ctrl->dhchap_key? It looks to me that this buffer is only used here and thus could be turned into a local variable. Keys flying around in memory is not a good idea. :-)
The key is also used when using the ffdhe algorithm.
Note: I _think_ that I need to use this key for the ffdhe algorithm, because the implementation I came up with is essentially plain DH with pre-defined 'p', 'q' and 'g' values. But the DH implementation also requires a 'key', and for that I'm using this key here.
It might be that I'm completely off, and don't need to use a key for our DH implementation. In that case you are correct. (And that's why I said I'll need a review of the FFDHE implementation).
But for now I'll need the key for FFDHE.
Quoted 8 lines
drivers/nvme/target/auth.c line 149
Quoted 5 lines
drivers/nvme/target/auth.c new file +5
-0+ ctrl->dhchap_key_len); +out_free_hash: +if (ret) { +if (ctrl->dhchap_key) { +kfree (ctrl->dhchap_key);
kfree_sensitive?
Yes, will be fixing it.
Quoted 13 lines
Quoted 13 lines
drivers/nvme/target/auth.c new file +13
-0+ ctrl->dhchap_key =NULL ; + } +crypto_free_shash (ctrl->shash_tfm); + ctrl->shash_tfm =NULL ; + } +out_unlock: +up_read (&nvmet_config_sem); + +return ret; +} + +void nvmet_auth_sq_free (struct nvmet_sq *sq) +{
Quoted 9 lines
drivers/nvme/target/auth.c line 168
Quoted 6 lines
drivers/nvme/target/auth.c new file +6
-0+if (sq->dhchap_c1) +kfree (sq->dhchap_c1); +if (sq->dhchap_c2) +kfree (sq->dhchap_c2); +if (sq->dhchap_skey) +kfree (sq->dhchap_skey);
kfree_sensitive?
Yes.
Quoted 8 lines
Quoted 8 lines
drivers/nvme/target/auth.c new file +8
-0+} + +void nvmet_reset_auth (struct nvmet_ctrl *ctrl) +{ +if (ctrl->shash_tfm) { +crypto_free_shash (ctrl->shash_tfm); + ctrl->shash_tfm =NULL ; + }
Quoted 9 lines
drivers/nvme/target/auth.c line 182
Quoted 6 lines
drivers/nvme/target/auth.c new file +6
-0+if (ctrl->dh_tfm) { +crypto_free_kpp (ctrl->dh_tfm); + ctrl->dh_tfm =NULL ; + } +if (ctrl->dhchap_key) { +kfree (ctrl->dhchap_key);
kfree_sensitive?
Yes.
Quoted 410 lines
Quoted 410 lines
drivers/nvme/target/auth.c new file +170
-0+ ctrl->dhchap_key =NULL ; + } +} + +bool nvmet_check_auth_status (struct nvmet_req *req) +{ +if (req->sq->ctrl->shash_tfm && + !req->sq->authenticated) +return false; +return true; +} + +int nvmet_auth_host_hash (struct nvmet_req *req,u8 *response, +unsigned int shash_len) +{ +struct nvmet_ctrl *ctrl = req->sq->ctrl; +SHASH_DESC_ON_STACK (shash, ctrl->shash_tfm); +u8 *challenge = req->sq->dhchap_c1; +u8 buf[4 ]; +int ret; + +if (ctrl->dh_gid !=NVME_AUTH_DHCHAP_DHGROUP_NULL ) { + ret = -ENOTSUPP ; + goto out; + } + + shash->tfm = ctrl->shash_tfm; + ret =crypto_shash_init (shash); +if (ret) + goto out; + ret =crypto_shash_update (shash, challenge, shash_len); +if (ret) + goto out; +put_unaligned_le32 (req->sq->dhchap_s1, buf); + ret =crypto_shash_update (shash, buf,4 ); +if (ret) + goto out; +put_unaligned_le16 (req->sq->dhchap_tid, buf); + ret =crypto_shash_update (shash, buf,2 ); +if (ret) + goto out; +memset (buf,0 ,4 ); + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash,"HostHost" ,8 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->hostnqn,strlen (ctrl->hostnqn)); +if (ret) + goto out; + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->subsysnqn, +strlen (ctrl->subsysnqn)); +if (ret) + goto out; + ret =crypto_shash_final (shash, response); +out: +if (challenge != req->sq->dhchap_c1) +kfree (challenge); +return 0 ; +} + +int nvmet_auth_ctrl_hash (struct nvmet_req *req,u8 *response, +unsigned int shash_len) +{ +struct nvmet_ctrl *ctrl = req->sq->ctrl; +SHASH_DESC_ON_STACK (shash, ctrl->shash_tfm); +u8 *challenge = req->sq->dhchap_c2; +u8 buf[4 ]; +int ret; + +pr_debug ("%s: ctrl %d hash seq %d transaction %u\n" , __func__, + ctrl->cntlid, req->sq->dhchap_s2, req->sq->dhchap_tid); +pr_debug ("%s: ctrl %d challenge %*ph\n" , __func__, + ctrl->cntlid, shash_len, req->sq->dhchap_c2); +pr_debug ("%s: ctrl %d subsysnqn %s\n" , __func__, + ctrl->cntlid, ctrl->subsysnqn); +pr_debug ("%s: ctrl %d hostnqn %s\n" , __func__, + ctrl->cntlid, ctrl->hostnqn); + +if (ctrl->dh_gid !=NVME_AUTH_DHCHAP_DHGROUP_NULL ) { + ret = -ENOTSUPP ; + goto out; + } + + shash->tfm = ctrl->shash_tfm; + ret =crypto_shash_init (shash); +if (ret) + goto out; + ret =crypto_shash_update (shash, challenge, shash_len); +if (ret) + goto out; +put_unaligned_le32 (req->sq->dhchap_s2, buf); + ret =crypto_shash_update (shash, buf,4 ); +if (ret) + goto out; +put_unaligned_le16 (req->sq->dhchap_tid, buf); + ret =crypto_shash_update (shash, buf,2 ); +if (ret) + goto out; +memset (buf,0 ,4 ); + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash,"Controller" ,10 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->subsysnqn, +strlen (ctrl->subsysnqn)); +if (ret) + goto out; + ret =crypto_shash_update (shash, buf,1 ); +if (ret) + goto out; + ret =crypto_shash_update (shash, ctrl->hostnqn,strlen (ctrl->hostnqn)); +if (ret) + goto out; + ret =crypto_shash_final (shash, response); +out: +if (challenge != req->sq->dhchap_c2) +kfree (challenge); +return 0 ; +} + +int nvmet_auth_ctrl_sesskey (struct nvmet_req *req, +u8 *pkey,int pkey_size) +{ +struct nvmet_ctrl *ctrl = req->sq->ctrl; +struct kpp_request *kpp_req; +struct crypto_wait wait; +struct scatterlist src, dst; +int ret; + + req->sq->dhchap_skey_len = +nvme_auth_dhgroup_privkey_size (ctrl->dh_gid); + req->sq->dhchap_skey =kzalloc (req->sq->dhchap_skey_len,GFP_KERNEL ); +if (!req->sq->dhchap_skey) +return -ENOMEM ; + kpp_req =kpp_request_alloc (ctrl->dh_tfm,GFP_KERNEL ); +if (!kpp_req) { +kfree (req->sq->dhchap_skey); + req->sq->dhchap_skey =NULL ; +return -ENOMEM ; + } + +pr_debug ("%s: host public key %*ph\n" , __func__, + (int )pkey_size, pkey); +crypto_init_wait (&wait); +sg_init_one (&src, pkey, pkey_size); +kpp_request_set_input (kpp_req, &src, pkey_size); +sg_init_one (&dst, req->sq->dhchap_skey, + req->sq->dhchap_skey_len); +kpp_request_set_output (kpp_req, &dst, req->sq->dhchap_skey_len); +kpp_request_set_callback (kpp_req,CRYPTO_TFM_REQ_MAY_BACKLOG , + crypto_req_done, &wait); + + ret =crypto_wait_req (crypto_kpp_compute_shared_secret (kpp_req), &wait); +kpp_request_free (kpp_req); +if (ret) +pr_debug ("failed to compute shared secred, err %d\n" , ret); +else +pr_debug ("%s: shared secret %*ph\n" , __func__, + (int )req->sq->dhchap_skey_len, + req->sq->dhchap_skey); + +return ret; +}drivers/nvme/target/configfs.c +70
-1@@ -11,8 +11,13 @@#include <linux/ctype.h> #include <linux/pci.h> #include <linux/pci-p2pdma.h> +#include <crypto/hash.h> +#include <crypto/kpp.h> #include "nvmet.h" +#ifdef CONFIG_NVME_TARGET_AUTH +#include "../host/auth.h" +#endif static const struct config_item_type nvmet_host_type;static const struct config_item_type nvmet_subsys_type;@@ -1656,10 +1661,71 @@ static const struct config_item_type nvmet_ports_type = { static struct config_group nvmet_subsystems_group;static struct config_group nvmet_ports_group; -static void nvmet_host_release (struct config_item *item) +#ifdef CONFIG_NVME_TARGET_AUTH +static ssize_t nvmet_host_dhchap_key_show (struct config_item *item, +char *page) +{ +u8 *dhchap_secret =to_host (item)->dhchap_secret; + +if (!dhchap_secret) +return sprintf (page,"\n" ); +return sprintf (page,"%s\n" , dhchap_secret); +} + +static ssize_t nvmet_host_dhchap_key_store (struct config_item *item, +const char *page,size_t count) {struct nvmet_host *host =to_host (item); +int ret; + ret =nvmet_auth_set_host_key (host, page); +if (ret <0 ) +return ret; +return count; +} + +CONFIGFS_ATTR (nvmet_host_, dhchap_key); + +static ssize_t nvmet_host_dhchap_hash_show (struct config_item *item, +char *page) +{ +struct nvmet_host *host =to_host (item); +const char *hash_name =nvme_auth_hmac_name (host->dhchap_hash_id); + +return sprintf (page,"%s\n" , hash_name ? hash_name :"none" ); +} + +static ssize_t nvmet_host_dhchap_hash_store (struct config_item *item, +const char *page,size_t count) +{ +struct nvmet_host *host =to_host (item); +int hmac_id; + + hmac_id =nvme_auth_hmac_id (page); +if (hmac_id <0 ) +return -EINVAL ; +if (!crypto_has_shash (nvme_auth_hmac_name (hmac_id),0 ,0 )) +return -ENOTSUPP ; + host->dhchap_hash_id = hmac_id; +return count; +} + +CONFIGFS_ATTR (nvmet_host_, dhchap_hash); + +static struct configfs_attribute *nvmet_host_attrs[] = { + &nvmet_host_attr_dhchap_key, + &nvmet_host_attr_dhchap_hash, +NULL , +}; +#endif /* CONFIG_NVME_TARGET_AUTH */ + +static void nvmet_host_release (struct config_item *item) +{ +struct nvmet_host *host =to_host (item); +#ifdef CONFIG_NVME_TARGET_AUTH +if (host->dhchap_secret) +kfree (host->dhchap_secret); +#endif kfree (host); }@@ -1669,6 +1735,9 @@ static struct configfs_item_operations nvmet_host_item_ops = {static const struct config_item_type nvmet_host_type = { .ct_item_ops = &nvmet_host_item_ops, +#ifdef CONFIG_NVME_TARGET_AUTH + .ct_attrs = nvmet_host_attrs, +#endif .ct_owner =THIS_MODULE , };drivers/nvme/target/core.c +8
-0@@ -793,6 +793,7 @@ void nvmet_sq_destroy(struct nvmet_sq *sq)wait_for_completion (&sq->confirm_done);wait_for_completion (&sq->free_done);percpu_ref_exit (&sq->ref); +nvmet_auth_sq_free (sq);if (ctrl) {/* @@ -1264,6 +1265,11 @@ u16 nvmet_check_ctrl_status(struct nvmet_req *req) req->cmd->common.opcode, req->sq->qid);return NVME_SC_CMD_SEQ_ERROR |NVME_SC_DNR ; } + +if (unlikely (!nvmet_check_auth_status (req))) { +pr_warn ("qid %d not authenticated\n" , req->sq->qid); +return NVME_SC_AUTH_REQUIRED |NVME_SC_DNR ; + }return 0 ; }@@ -1456,6 +1462,8 @@ static void nvmet_ctrl_free(struct kref *ref)flush_work (&ctrl->async_event_work);cancel_work_sync (&ctrl->fatal_err_work); +nvmet_reset_auth (ctrl); +ida_simple_remove (&cntlid_ida, ctrl->cntlid);nvmet_async_events_free (ctrl);(quoted diff) +0
-0
b/drivers/nvme/target/fabrics-cmd-auth.cnew file mode 100644 index 000000000000..962f9f5e9d89drivers/nvme/target/fabrics-cmd-auth.c new file +29
-0@@ -0,0 +1,460 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* NVMe over Fabrics DH-HMAC-CHAP authentication command handling. +* Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions. +* All rights reserved. +*/ +#define pr_fmt (fmt) KBUILD_MODNAME ": " fmt +#include <linux/blkdev.h> +#include <linux/random.h> +#include <crypto/hash.h> +#include <crypto/kpp.h> +#include "nvmet.h" +#include "../host/auth.h" + +void nvmet_init_auth (struct nvmet_ctrl *ctrl,struct nvmet_req *req) +{ +/* Initialize in-band authentication */ + req->sq->authenticated = false; + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE ; + req->cqe->result.u32 |=0x2 <<16 ; +} + +static u16 nvmet_auth_negotiate (struct nvmet_req *req,void *d) +{ +struct nvmet_ctrl *ctrl = req->sq->ctrl; +struct nvmf_auth_dhchap_negotiate_data *data = d; +int i, hash_id, null_dh =-1 ; + + pr_debug("%s: ctrl %d qid %d: data sc_d %d napd %d authid %d halen %ddhlen %d\n", +
__func__,ctrl->cntlid,req->sq->qid,drivers/nvme/target/fabrics-cmd-auth.c new file +22
-0+ data->sc_c, data->napd, data->auth_protocol[0 ].dhchap.authid, + data->auth_protocol[0 ].dhchap.halen, + data->auth_protocol[0].dhchap.dhlen); + req->sq->dhchap_tid =le16_to_cpu (data->t_id); +if (data->sc_c) +return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH ; + +if (data->napd !=1 ) +return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ; + +if (data->auth_protocol[0 ].dhchap.authid !=0x01 ) +return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; + + hash_id =nvme_auth_hmac_id (crypto_shash_alg_name (ctrl->shash_tfm)); +for (i =0 ; i < data->auth_protocol[0 ].dhchap.halen; i++) { +pr_debug ("%s: ctrl %d qid %d checking hash %d for %d\n" , + __func__, ctrl->cntlid, req->sq->qid, + data->auth_protocol[0 ].dhchap.idlist[i], hash_id); +if (hash_id != data->auth_protocol[0 ].dhchap.idlist[i]) +continue ; + req->sq->dhchap_hash_id = hash_id; + req->sq->dhchap_hash_len = crypto_shash_digestsize(ctrl-
shash_tfm);drivers/nvme/target/fabrics-cmd-auth.c new file +46
-0+break ; + } +if (req->sq->dhchap_hash_id ==0 ) { +pr_debug ("%s: ctrl %d qid %d: no usable hash found\n" , + __func__, ctrl->cntlid, req->sq->qid); +return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ; + } + +for (i = data->auth_protocol[0 ].dhchap.halen; + i < data->auth_protocol[0 ].dhchap.halen + + data->auth_protocol[0 ].dhchap.dhlen; i++) { +int dhgid = data->auth_protocol[0 ].dhchap.idlist[i]; + +if (dhgid ==NVME_AUTH_DHCHAP_DHGROUP_NULL ) { + null_dh = dhgid; +continue ; + } +if (nvmet_setup_dhgroup (ctrl, dhgid) ==0 ) +break ; + } +if (!ctrl->dh_tfm && null_dh <0 ) { +pr_debug ("%s: ctrl %d qid %d: no DH group selected\n" , + __func__, ctrl->cntlid, req->sq->qid); +return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; + } +if (ctrl->dh_gid ==-1 ) { + ctrl->dh_gid = null_dh; + ctrl->dh_tfm =NULL ; + } +pr_debug ("%s: ctrl %d qid %d: DH group %s (%d)\n" , + __func__, ctrl->cntlid, req->sq->qid, +nvme_auth_dhgroup_name (ctrl->dh_gid), ctrl->dh_gid); +return 0 ; +} + +static u16 nvmet_auth_reply (struct nvmet_req *req,void *d) +{ +struct nvmet_ctrl *ctrl = req->sq->ctrl; +struct nvmf_auth_dhchap_reply_data *data = d; +u8 *response; + +pr_debug ("%s: ctrl %d qid %d: data hl %d cvalid %d dhvlen %d\n" , + __func__, ctrl->cntlid, req->sq->qid, + data->hl, data->cvalid, data->dhvlen); +if (data->hl != req->sq->dhchap_hash_len) +return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ;
Quoted 9 lines
drivers/nvme/target/fabrics-cmd-auth.c line 104
Quoted 6 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +6
-0+ +if (data->dhvlen) { +return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD ; + } + + response =kmalloc (data->hl,GFP_KERNEL );Again, align to
CRYPTO_MINALIGN_ATTR?
Ah, _that_ alignment.
Wasn't aware that I need to align to anything.
But if that's required, sure, I'll be fixing it.
Quoted 535 lines
Quoted 94 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +94
-0+if (!response) +return NVME_AUTH_DHCHAP_FAILURE_FAILED ; + +if (nvmet_auth_host_hash (req, response, data->hl) <0 ) { +pr_debug ("ctrl %d qid %d DH-HMAC-CHAP hash failed\n" , + ctrl->cntlid, req->sq->qid); +kfree (response); +return NVME_AUTH_DHCHAP_FAILURE_FAILED ; + } + +if (memcmp (data->rval, response, data->hl)) { +pr_info ("ctrl %d qid %d DH-HMAC-CHAP response mismatch\n" , + ctrl->cntlid, req->sq->qid); +kfree (response); +return NVME_AUTH_DHCHAP_FAILURE_FAILED ; + } +kfree (response); +pr_info ("ctrl %d qid %d DH-HMAC-CHAP host authenticated\n" , + ctrl->cntlid, req->sq->qid); +if (data->cvalid) { + req->sq->dhchap_c2 =kmalloc (data->hl,GFP_KERNEL ); +if (!req->sq->dhchap_c2) +return NVME_AUTH_DHCHAP_FAILURE_FAILED ; +memcpy (req->sq->dhchap_c2, data->rval + data->hl, data->hl); + +pr_debug ("ctrl %d qid %d challenge %*ph\n" , + ctrl->cntlid, req->sq->qid, data->hl, + req->sq->dhchap_c2); + req->sq->dhchap_s2 =le32_to_cpu (data->seqnum); + }else + req->sq->dhchap_c2 =NULL ; + +return 0 ; +} + +static u16 nvmet_auth_failure2 (struct nvmet_req *req,void *d) +{ +struct nvmf_auth_dhchap_failure_data *data = d; + +return data->reason_code_explanation; +} + +void nvmet_execute_auth_send (struct nvmet_req *req) +{ +struct nvmet_ctrl *ctrl = req->sq->ctrl; +struct nvmf_auth_dhchap_success2_data *data; +void *d; +u32 tl; +u16 status =0 ; + +if (req->cmd->auth_send.secp !=NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER ) { + status =NVME_SC_INVALID_FIELD |NVME_SC_DNR ; + req->error_loc = + offsetof(struct nvmf_auth_send_command , secp); + goto done; + } +if (req->cmd->auth_send.spsp0 !=0x01 ) { + status =NVME_SC_INVALID_FIELD |NVME_SC_DNR ; + req->error_loc = + offsetof(struct nvmf_auth_send_command , spsp0); + goto done; + } +if (req->cmd->auth_send.spsp1 !=0x01 ) { + status =NVME_SC_INVALID_FIELD |NVME_SC_DNR ; + req->error_loc = + offsetof(struct nvmf_auth_send_command , spsp1); + goto done; + } + tl =le32_to_cpu (req->cmd->auth_send.tl); +if (!tl) { + status =NVME_SC_INVALID_FIELD |NVME_SC_DNR ; + req->error_loc = + offsetof(struct nvmf_auth_send_command , tl); + goto done; + } +if (!nvmet_check_transfer_len (req, tl)) { +pr_debug ("%s: transfer length mismatch (%u)\n" , __func__, tl); +return ; + } + + d =kmalloc (tl,GFP_KERNEL ); +if (!d) { + status =NVME_SC_INTERNAL ; + goto done; + } + + status =nvmet_copy_from_sgl (req,0 , d, tl); +if (status) { +kfree (d); + goto done; + } + + data = d; + pr_debug("%s: ctrl %d qid %d type %d id %d step %x\n", __func__,drivers/nvme/target/fabrics-cmd-auth.c
Quoted 10 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +10
-0+ ctrl->cntlid, req->sq->qid, data->auth_type, data->auth_id, + req->sq->dhchap_step); +if (data->auth_type !=NVME_AUTH_COMMON_MESSAGES && + data->auth_type !=NVME_AUTH_DHCHAP_MESSAGES ) { + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ; + req->sq->dhchap_status =NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE ; + }else if (data->auth_type ==NVME_AUTH_COMMON_MESSAGES ) { +if (data->auth_id != req->sq->dhchap_step) { + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ; + req->sq->dhchap_status =
NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;drivers/nvme/target/fabrics-cmd-auth.c
Quoted 3 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +3
-0+ }else if (data->auth_id !=NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE ) { + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ; + req->sq->dhchap_status =
NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;drivers/nvme/target/fabrics-cmd-auth.c
Quoted 5 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +5
-0+ }else { +/* Validate negotiation parameters */ + status =nvmet_auth_negotiate (req, d); +if (status ==0 ) + req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;drivers/nvme/target/fabrics-cmd-auth.c
Quoted 2 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +2
-0+else { + req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;drivers/nvme/target/fabrics-cmd-auth.c
Quoted 11 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +11
-0+ req->sq->dhchap_status = status; + status =0 ; + } + } + }else if (data->auth_type ==NVME_AUTH_DHCHAP_MESSAGES ) { +if (data->auth_id != req->sq->dhchap_step) { +pr_debug ("%s: ctrl %d qid %d step mismatch (%d != %d)\n" , + __func__, ctrl->cntlid, req->sq->qid, + data->auth_id, req->sq->dhchap_step); + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ; + req->sq->dhchap_status =
NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;drivers/nvme/target/fabrics-cmd-auth.c
Quoted 2 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +2
-0+ }else if (le16_to_cpu (data->t_id) != req->sq->dhchap_tid) { + pr_debug("%s: ctrl %d qid %d invalid transaction %d(expected %d)\n",
drivers/nvme/target/fabrics-cmd-auth.c
Quoted 5 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +5
-0+ __func__, ctrl->cntlid, req->sq->qid, +le16_to_cpu (data->t_id), + req->sq->dhchap_tid); + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ; + req->sq->dhchap_status =
NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;drivers/nvme/target/fabrics-cmd-auth.c
Quoted 6 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +6
-0+ }else { +switch (data->auth_id) { +case NVME_AUTH_DHCHAP_MESSAGE_REPLY : + status =nvmet_auth_reply (req, d); +if (status ==0 ) + req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1;drivers/nvme/target/fabrics-cmd-auth.c
Quoted 2 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +2
-0+else { + req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;drivers/nvme/target/fabrics-cmd-auth.c
Quoted 13 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +13
-0+ req->sq->dhchap_status = status; + status =0 ; + } +break ; +case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 : + req->sq->authenticated = true; +pr_debug ("%s: ctrl %d qid %d authenticated\n" , + __func__, ctrl->cntlid, req->sq->qid); +break ; +case NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 : + status =nvmet_auth_failure2 (req, d); +if (status) { + pr_warn("ctrl %d qid %d: DH-HMAC-CHAPnegotiation failed (%d)\n",
drivers/nvme/target/fabrics-cmd-auth.c
Quoted 8 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +8
-0+ ctrl->cntlid, req->sq->qid, + status); + req->sq->dhchap_status = status; + status =0 ; + } +break ; +default : + req->sq->dhchap_status =
NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;drivers/nvme/target/fabrics-cmd-auth.c
Quoted 1 line
drivers/nvme/target/fabrics-cmd-auth.c new file +1
-0+ req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;Quoted 90 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +90
-0+break ; + } + } + }else { + req->sq->dhchap_status =NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE ; + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 ; + } +kfree (d); +done: +pr_debug ("%s: ctrl %d qid %d dhchap status %x step %x\n" , __func__, + ctrl->cntlid, req->sq->qid, + req->sq->dhchap_status, req->sq->dhchap_step); +if (status) +pr_debug ("%s: ctrl %d qid %d nvme status %x error loc %d\n" , + __func__, ctrl->cntlid, req->sq->qid, + status, req->error_loc); + req->cqe->result.u64 =0 ; +nvmet_req_complete (req, status); +if (req->sq->dhchap_step !=NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 && + req->sq->dhchap_step !=NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 ) +return ; +/* Final states, clear up variables */ +kfree (req->sq->dhchap_c1); +kfree (req->sq->dhchap_c2); +if (req->sq->dhchap_step ==NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 ) +nvmet_ctrl_fatal_error (ctrl); +} + +static int nvmet_auth_challenge (struct nvmet_req *req,void *d,int al) +{ +struct nvmf_auth_dhchap_challenge_data *data = d; +struct nvmet_ctrl *ctrl = req->sq->ctrl; +int ret =0 ; +int data_size =sizeof (*d) + req->sq->dhchap_hash_len; + +if (al < data_size) { +pr_debug ("%s: buffer too small (al %d need %d)\n" , __func__, + al, data_size); +return -EINVAL ; + } +memset (data,0 , data_size); + req->sq->dhchap_s1 = ctrl->dhchap_seqnum++; + data->auth_type =NVME_AUTH_DHCHAP_MESSAGES ; + data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE ; + data->t_id =cpu_to_le16 (req->sq->dhchap_tid); + data->hashid = req->sq->dhchap_hash_id; + data->hl = req->sq->dhchap_hash_len; + data->seqnum =cpu_to_le32 (req->sq->dhchap_s1); + req->sq->dhchap_c1 =kmalloc (data->hl,GFP_KERNEL ); +if (!req->sq->dhchap_c1) +return -ENOMEM ; +get_random_bytes (req->sq->dhchap_c1, data->hl); +memcpy (data->cval, req->sq->dhchap_c1, data->hl); +pr_debug ("%s: ctrl %d qid %d seq %d transaction %d hl %d dhvlen %d\n" , + __func__, ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1, + req->sq->dhchap_tid, data->hl, data->dhvlen); +return ret; +} + +static int nvmet_auth_success1 (struct nvmet_req *req,void *d,int al) +{ +struct nvmf_auth_dhchap_success1_data *data = d; +struct nvmet_ctrl *ctrl = req->sq->ctrl; + +WARN_ON (al <sizeof (*data)); +memset (data,0 ,sizeof (*data)); + data->auth_type =NVME_AUTH_DHCHAP_MESSAGES ; + data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1 ; + data->t_id =cpu_to_le16 (req->sq->dhchap_tid); + data->hl = req->sq->dhchap_hash_len; +if (req->sq->dhchap_c2) { +if (nvmet_auth_ctrl_hash (req, data->rval, data->hl)) +return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE ; + data->rvalid =1 ; +pr_debug ("ctrl %d qid %d response %*ph\n" , + ctrl->cntlid, req->sq->qid, data->hl, data->rval); + } +return 0 ; +} + +static void nvmet_auth_failure1 (struct nvmet_req *req,void *d,int al) +{ +struct nvmf_auth_dhchap_failure_data *data = d; + +WARN_ON (al <sizeof (*data)); + data->auth_type =NVME_AUTH_COMMON_MESSAGES ; + data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ; + data->t_id =cpu_to_le32 (req->sq->dhchap_tid); + data->reason_code =NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED ; + data->reason_code_explanation = req->sq->dhchap_status;drivers/nvme/target/fabrics-cmd-auth.c
Quoted 10 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +10
-0+} + +void nvmet_execute_auth_receive (struct nvmet_req *req) +{ +struct nvmet_ctrl *ctrl = req->sq->ctrl; +void *d; +u32 al; +u16 status =0 ; + +if (req->cmd->auth_receive.secp !=NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER ){
Quoted 260 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +94
-0+ status =NVME_SC_INVALID_FIELD |NVME_SC_DNR ; + req->error_loc = + offsetof(struct nvmf_auth_receive_command , secp); + goto done; + } +if (req->cmd->auth_receive.spsp0 !=0x01 ) { + status =NVME_SC_INVALID_FIELD |NVME_SC_DNR ; + req->error_loc = + offsetof(struct nvmf_auth_receive_command , spsp0); + goto done; + } +if (req->cmd->auth_receive.spsp1 !=0x01 ) { + status =NVME_SC_INVALID_FIELD |NVME_SC_DNR ; + req->error_loc = + offsetof(struct nvmf_auth_receive_command , spsp1); + goto done; + } + al =le32_to_cpu (req->cmd->auth_receive.al); +if (!al) { + status =NVME_SC_INVALID_FIELD |NVME_SC_DNR ; + req->error_loc = + offsetof(struct nvmf_auth_receive_command , al); + goto done; + } +if (!nvmet_check_transfer_len (req, al)) { +pr_debug ("%s: transfer length mismatch (%u)\n" , __func__, al); +return ; + } + + d =kmalloc (al,GFP_KERNEL ); +if (!d) { + status =NVME_SC_INTERNAL ; + goto done; + } +pr_debug ("%s: ctrl %d qid %d step %x\n" , __func__, + ctrl->cntlid, req->sq->qid, req->sq->dhchap_step); +switch (req->sq->dhchap_step) { +case NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE : + status =nvmet_auth_challenge (req, d, al); +if (status <0 ) { +pr_warn ("ctrl %d qid %d: challenge error (%d)\n" , + ctrl->cntlid, req->sq->qid, status); + status =NVME_SC_INTERNAL ; +break ; + } +if (status) { + req->sq->dhchap_status = status; +nvmet_auth_failure1 (req, d, al); +pr_warn ("ctrl %d qid %d: challenge status (%x)\n" , + ctrl->cntlid, req->sq->qid, + req->sq->dhchap_status); + status =0 ; +break ; + } + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_REPLY ; +break ; +case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1 : + status =nvmet_auth_success1 (req, d, al); +if (status) { + req->sq->dhchap_status = status; +nvmet_auth_failure1 (req, d, al); +pr_warn ("ctrl %d qid %d: success1 status (%x)\n" , + ctrl->cntlid, req->sq->qid, + req->sq->dhchap_status); +break ; + } + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 ; +break ; +case NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 : +nvmet_auth_failure1 (req, d, al); +pr_warn ("ctrl %d qid %d failure1 (%x)\n" , + ctrl->cntlid, req->sq->qid, req->sq->dhchap_status); +break ; +default : +pr_warn ("ctrl %d qid %d unhandled step (%d)\n" , + ctrl->cntlid, req->sq->qid, req->sq->dhchap_step); + req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ; + req->sq->dhchap_status =NVME_AUTH_DHCHAP_FAILURE_FAILED ; +nvmet_auth_failure1 (req, d, al); + status =0 ; +break ; + } + + status =nvmet_copy_to_sgl (req,0 , d, al); +kfree (d); +done: + req->cqe->result.u64 =0 ; +nvmet_req_complete (req, status); +if (req->sq->dhchap_step ==NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 ) { +kfree (req->sq->dhchap_c1); +kfree (req->sq->dhchap_c2); +nvmet_ctrl_fatal_error (ctrl); + } +}(quoted diff) +0
-0
b/drivers/nvme/target/fabrics-cmd.cindex7d0f3523fdab..53fb853cd8fe100644drivers/nvme/target/fabrics-cmd.c +25
-2@@ -93,6 +93,14 @@ u16 nvmet_parse_fabrics_cmd(struct nvmet_req *req)case nvme_fabrics_type_property_get: req->execute = nvmet_execute_prop_get;break ; +#ifdef CONFIG_NVME_TARGET_AUTH +case nvme_fabrics_type_auth_send: + req->execute = nvmet_execute_auth_send; +break ; +case nvme_fabrics_type_auth_receive: + req->execute = nvmet_execute_auth_receive; +break ; +#endif default :pr_debug ("received unknown capsule type 0x%x\n" , cmd->fabrics.fctype);@@ -155,6 +163,7 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req) struct nvmf_connect_data *d;struct nvmet_ctrl *ctrl =NULL ;u16 status =0 ; +int ret;if (!nvmet_check_transfer_len (req,sizeof (struct nvmf_connect_data )))return ;@@ -197,17 +206,31 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req) uuid_copy(&ctrl->hostid, &d->hostid); + ret =nvmet_setup_auth (ctrl, req); +if (ret <0 ) { +pr_err ("Failed to setup authentication, error %d\n" , ret); +nvmet_ctrl_put (ctrl); +if (ret == -EPERM ) + status = (NVME_SC_CONNECT_INVALID_HOST |NVME_SC_DNR ); +else + status =NVME_SC_INTERNAL ; + goto out; + } + status =nvmet_install_queue (ctrl, req);if (status) {nvmet_ctrl_put (ctrl); goto out; } -pr_info ("creating controller %d for subsystem %s for NQN %s%s.\n" , +pr_info ("creating controller %d for subsystem %s for NQN %s%s%s.\n" , ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn, - ctrl->pi_support ?" T10-PI is enabled" :"" ); + ctrl->pi_support ?" T10-PI is enabled" :"" , +nvmet_has_auth (ctrl) ?" with DH-HMAC-CHAP" :"" ); req->cqe->result.u16 =cpu_to_le16 (ctrl->cntlid); +if (nvmet_has_auth (ctrl)) +nvmet_init_auth (ctrl, req); out:kfree (d);complete:
drivers/nvme/target/fabrics-cmd.c +3
-0@@ -267,6 +290,9 @@ static void nvmet_execute_io_connect(struct nvmet_req *req) }pr_debug ("adding queue %d to ctrl %d.\n" , qid, ctrl->cntlid); + req->cqe->result.u16 =cpu_to_le16 (ctrl->cntlid); +if (nvmet_has_auth (ctrl)) +nvmet_init_auth (ctrl, req); out:kfree (d);drivers/nvme/target/nvmet.h +27
-0@@ -108,6 +108,20 @@ struct nvmet_sq {u16 size;u32 sqhd;bool sqhd_disabled; +#ifdef CONFIG_NVME_TARGET_AUTH +bool authenticated; +u16 dhchap_tid; +u16 dhchap_status; +int dhchap_step; +u8 dhchap_hash_id; +u8 dhchap_hash_len; +u8 *dhchap_c1; +u8 *dhchap_c2; +u32 dhchap_s1; +u32 dhchap_s2; +u8 *dhchap_skey; +int dhchap_skey_len; +#endif struct completion free_done;struct completion confirm_done; };@@ -209,6 +223,15 @@ struct nvmet_ctrl {u64 err_counter;struct nvme_error_slot slots[NVMET_ERROR_LOG_SLOTS ];bool pi_support; +#ifdef CONFIG_NVME_TARGET_AUTH +u32 dhchap_seqnum; +u8 *dhchap_key; +size_t dhchap_key_len; +struct crypto_shash *shash_tfm; +struct crypto_kpp *dh_tfm; +u32 dh_gid; +u32 dh_keysize; +#endif };struct nvmet_subsys {@@ -270,6 +293,10 @@ static inline struct nvmet_subsys *namespaces_to_subsys ( struct nvmet_host {struct config_group group; +u8 *dhchap_secret; +u8 dhchap_key_hash; +u8 dhchap_hash_id; +u8 dhchap_dhgroup_id; };static inline struct
nvmet_host*to_host(structconfig_item*item)drivers/nvme/target/nvmet.h +37
-0@@ -659,4 +686,48 @@ static inline void nvmet_req_bio_put(struct nvmet_req *req, struct bio *bio) bio_put(bio); } +#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_host_key (struct nvmet_host *host,const char *secret); +int nvmet_auth_set_host_hash (struct nvmet_host *host,const char *hash); +int nvmet_setup_auth (struct nvmet_ctrl *ctrl,struct nvmet_req *req); +void nvmet_init_auth (struct nvmet_ctrl *ctrl,struct nvmet_req *req); +void nvmet_reset_auth (struct nvmet_ctrl *ctrl); +void nvmet_auth_sq_free (struct nvmet_sq *sq); +int nvmet_setup_dhgroup (struct nvmet_ctrl *ctrl,int dhgroup_id); +bool nvmet_check_auth_status (struct nvmet_req *req); +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) +{ +return ctrl->shash_tfm !=NULL ; +} +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); +#else +static inline int nvmet_setup_auth (struct nvmet_ctrl *ctrl, +struct nvmet_req *req) +{ +return 0 ; +} +static inline void nvmet_init_auth (struct nvmet_ctrl *ctrl, +struct nvmet_req *req) {}; +static inline void nvmet_reset_auth (struct nvmet_ctrl *ctrl) {}; +static inline void nvmet_auth_sq_free (struct nvmet_sq *sq) {}; +static inline bool nvmet_check_auth_status (struct nvmet_req *req) +{ +return true; +}
Quoted 8 lines
Quoted 8 lines
drivers/nvme/target/nvmet.h +5
-0+static inline bool nvmet_has_auth (struct nvmet_ctrl *ctrl) +{ +return false; +} +static inline const char *nvmet_dhchap_dhgroup_name (int dhgid) {return NULL; } +#endif
+
#endif /*_NVMET_H*/
Thanks for the review!
Cheers,
Hannes
Signature · 4 lines
Dr. Hannes Reinecke Kernel Storage Architect [email protected] +49 911 74053 688 SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication
Am Sonntag, 18. Juli 2021, 14:37:34 CEST schrieb Hannes Reinecke:
Hi Hannes,
Quoted 258 lines · Stephan Müller
Quoted 133 lines
Am Freitag, 16. Juli 2021, 13:04:26 CEST schrieb Hannes Reinecke:
Hi Hannes,
drivers/nvme/target/Makefile nvmet-y += core.o configfs.o admin-cmd.o
Quoted 48 lines
Implement support for NVMe-oF In-Band authentication. This patch adds two additional configfs entries '
dhchap_key' and 'dhchap_hash' to the 'host' configfs directory. The 'dhchap_key' needs to be specified in the format outlined in the base spec. Augmented challenge support is not implemented, and concatenation with TLS encryption is not supported.Signed-off-by Hannes Reinecke <[email protected]>---
drivers/nvme/target/Kconfig | 10 + drivers/nvme/target/Makefile | 1 + drivers/nvme/target/admin-cmd.c | 4 + drivers/nvme/target/auth.c | 352 +++++++++++++++++++ drivers/nvme/target/configfs.c | 71 +++- drivers/nvme/target/core.c | 8 + drivers/nvme/target/fabrics-cmd-auth.c | 460 +++++++++++++++++++++++++ drivers/nvme/target/fabrics-cmd.c | 30 +- drivers/nvme/target/nvmet.h | 71 ++++ 9 files changed, 1004 insertions(+), 3 deletions(-)create mode 100644
drivers/nvme/target/auth.ccreate mode 100644drivers/nvme/target/fabrics-cmd-auth.cdrivers/nvme/target/Kconfig +10
-0@@ -85,3 +85,13 @@ config NVME_TARGET_TCP devices over TCP. If unsure, say N. + +config NVME_TARGET_AUTH + bool "NVMe over Fabrics In-band Authentication support" + depends on NVME_TARGET + select CRYPTO_SHA256 + select CRYPTO_SHA512 + help + This enables support for NVMe over Fabrics In-band Authentication + + If unsure, say N.drivers/nvme/target/Makefile +0
-0@@ -13,6 +13,7 @@ nvmet-y += core.o configfs.o admin-cmd.ofabrics-cmd.o \
Quoted 78 lines
drivers/nvme/target/Makefile +1
-0discovery.o io-cmd-file.o io-cmd-bdev.o nvmet-$(CONFIG_NVME_TARGET_PASSTHRU) += passthru.o nvmet-$(CONFIG_BLK_DEV_ZONED) += zns.o +nvmet-$(CONFIG_NVME_TARGET_AUTH) += fabrics-cmd-auth.o auth.onvme-loop-y += loop.o
nvmet-rdma-y += rdma.o
nvmet-fc-y += fc.o(quoted diff) +0
-0
b/drivers/nvme/target/admin-cmd.cindex0cb98f2bbc8c..320cefc64ee0100644drivers/nvme/target/admin-cmd.c +4
-0@@ -1008,6 +1008,10 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)if (nvme_is_fabrics (cmd))return nvmet_parse_fabrics_cmd (req); + +if (unlikely (!nvmet_check_auth_status (req))) +return NVME_SC_AUTH_REQUIRED |NVME_SC_DNR ; +if (nvmet_req_subsys(req)->type ==
NVME_NQN_DISC)return nvmet_parse_discovery_cmd(req);drivers/nvme/target/auth.c new file +42
-0@@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* NVMe over Fabrics DH-HMAC-CHAP authentication. +* Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions. +* All rights reserved. +*/ +#define pr_fmt (fmt) KBUILD_MODNAME ": " fmt +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <crypto/hash.h> +#include <crypto/kpp.h> +#include <crypto/dh.h> +#include <crypto/ffdhe.h> +#include <linux/crc32.h> +#include <linux/base64.h> +#include <linux/ctype.h> +#include <linux/random.h> +#include <asm/unaligned.h> + +#include "nvmet.h" +#include "../host/auth.h" + +int nvmet_auth_set_host_key (struct nvmet_host *host,const char *secret) +{ +if (sscanf (secret,"DHHC-1:%hhd:%*s" , &host->dhchap_key_hash) !=1 ) +return -EINVAL ; +if (host->dhchap_key_hash >3 ) { +pr_warn ("Invalid DH-HMAC-CHAP hash id %d\n" , + host->dhchap_key_hash); +return -EINVAL ; + } +if (host->dhchap_key_hash >0 ) { +/* Validate selected hash algorithm */ +const char *hmac =nvme_auth_hmac_name (host->dhchap_key_hash); + +if (!crypto_has_shash (hmac,0 ,0 )) { +pr_warn ("DH-HMAC-CHAP hash %s unsupported\n" , hmac); + host->dhchap_key_hash =-1 ; +return -EAGAIN ; + }Quoted 8 lines
drivers/nvme/target/auth.c line 47
Quoted 5 lines
drivers/nvme/target/auth.c new file +5
-0+/* Use this hash as default */ +if (!host->dhchap_hash_id) + host->dhchap_hash_id = host->dhchap_key_hash; + } + host->dhchap_secret =kstrdup (secret,GFP_KERNEL );Just like before - are you sure that the secret is an ASCII string and no binary blob?
That is ensured by the transport encoding (cf NVMe Base Specification version 2.0). Also, this information is being passed in via the configfs interface, so it's bounded by
PAGE_SIZE. But yes, we should be inserting a terminating 'NULL' character at the end of the page to ensure we don't incur an buffer overrun. Any other failure will be checked for during base64 decoding.Quoted 93 lines
Quoted 93 lines
drivers/nvme/target/auth.c new file +93
-0+if (!host->dhchap_secret) +return -ENOMEM ; +/* Default to SHA256 */ +if (!host->dhchap_hash_id) + host->dhchap_hash_id =NVME_AUTH_DHCHAP_HASH_SHA256 ; + +pr_debug ("Using hash %s\n" , +nvme_auth_hmac_name (host->dhchap_hash_id)); +return 0 ; +} + +int nvmet_setup_dhgroup (struct nvmet_ctrl *ctrl,int dhgroup_id) +{ +int ret = -ENOTSUPP ; + +if (dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_NULL ) +return 0 ; + +return ret; +} + +int nvmet_setup_auth (struct nvmet_ctrl *ctrl,struct nvmet_req *req) +{ +int ret =0 ; +struct nvmet_host_link *p; +struct nvmet_host *host =NULL ; +const char *hash_name; + +down_read (&nvmet_config_sem); +if (ctrl->subsys->type ==NVME_NQN_DISC ) + goto out_unlock; + +list_for_each_entry (p, &ctrl->subsys->hosts, entry) { +pr_debug ("check %s\n" ,nvmet_host_name (p->host)); +if (strcmp (nvmet_host_name (p->host), ctrl->hostnqn)) +continue ; + host = p->host; +break ; + } +if (!host) { +pr_debug ("host %s not found\n" , ctrl->hostnqn); + ret = -EPERM ; + goto out_unlock; + } +if (!host->dhchap_secret) { +pr_debug ("No authentication provided\n" ); + goto out_unlock; + } + + hash_name =nvme_auth_hmac_name (host->dhchap_hash_id); +if (!hash_name) { +pr_debug ("Hash ID %d invalid\n" , host->dhchap_hash_id); + ret = -EINVAL ; + goto out_unlock; + } + ctrl->shash_tfm =crypto_alloc_shash (hash_name,0 , +CRYPTO_ALG_ALLOCATES_MEMORY ); +if (IS_ERR (ctrl->shash_tfm)) { +pr_debug ("failed to allocate shash %s\n" , hash_name); + ret =PTR_ERR (ctrl->shash_tfm); + ctrl->shash_tfm =NULL ; + goto out_unlock; + } + + ctrl->dhchap_key =nvme_auth_extract_secret (host->dhchap_secret, + &ctrl->dhchap_key_len); +if (IS_ERR (ctrl->dhchap_key)) { +pr_debug ("failed to extract host key, error %d\n" , ret); + ret =PTR_ERR (ctrl->dhchap_key); + ctrl->dhchap_key =NULL ; + goto out_free_hash; + } +if (host->dhchap_key_hash) { +struct crypto_shash *key_tfm; + + hash_name =nvme_auth_hmac_name (host->dhchap_key_hash); + key_tfm =crypto_alloc_shash (hash_name,0 ,0 ); +if (IS_ERR (key_tfm)) { + ret =PTR_ERR (key_tfm); + goto out_free_hash; + }else { +SHASH_DESC_ON_STACK (shash, key_tfm); + + shash->tfm = key_tfm; + ret =crypto_shash_setkey (key_tfm, ctrl->dhchap_key, + ctrl->dhchap_key_len); +crypto_shash_init (shash); +crypto_shash_update (shash, ctrl->subsys->subsysnqn, +strlen (ctrl->subsys->subsysnqn)); +crypto_shash_update (shash,"NVMe-over-Fabrics" ,17 ); +crypto_shash_final (shash, ctrl->dhchap_key); +crypto_free_shash (key_tfm); + }Quoted 8 lines
drivers/nvme/target/auth.c line 144
Quoted 4 lines
drivers/nvme/target/auth.c new file +4
-0+ } +pr_debug ("%s: using key %*ph\n" , __func__, + (int )ctrl->dhchap_key_len, ctrl->dhchap_key); + ret = crypto_shash_setkey(ctrl->shash_tfm, ctrl->dhchap_key,Is it truly necessary to keep the key around in
ctrl->dhchap_key? It looks to me that this buffer is only used here and thus could be turned into a local variable. Keys flying around in memory is not a good idea. :-)The key is also used when using the ffdhe algorithm.
Note: I _think_ that I need to use this key for the ffdhe algorithm, because the implementation I came up with is essentially plain DH with pre-defined 'p', 'q' and 'g' values. But the DH implementation also requires a 'key', and for that I'm using this key here.
Quoted 4 lines
It might be that I'm completely off, and don't need to use a key for our DH implementation. In that case you are correct.
(And that's why I said I'll need a review of the FFDHE implementation). But for now I'll need the key for FFDHE.
Do I understand you correctly that the dhchap_key is used as the input to the DH - i.e. it is the remote public key then? It looks strange that this is used for DH but then it is changed here by hashing it together with something else to form a new dhchap_key. Maybe that is what the protocol says. But it sounds strange to me, especially when you think that dhchap_key would be, say, 2048 bits if it is truly the remote public key and then after the hashing it is 256 or 512 bits depending on the HMAC type. This means that after the hashing, this dhchap_key cannot be used for FFC-DH.
Or are you using the dhchap_key for two different purposes?
It seems I miss something here.
Quoted 7 lines
Quoted 3 lines
drivers/nvme/target/auth.c line 145
Quoted 1 line
drivers/nvme/target/auth.c new file +1
-0+ response =kmalloc (data->hl,GFP_KERNEL );Again, align to
CRYPTO_MINALIGN_ATTR?Ah, _that_ alignment.
Wasn't aware that I need to align to anything.
But if that's required, sure, I'll be fixing it.
Again, that only saves you a memcpy in shash_final.
Ciao
Stephan