Skip to contentSkip to conversation

nvme: In-band authentication support

PATCH Superseded Hannes Reinecke 4 participants 48 messages Sep 10, 2021 – Sep 29, 2021

Subsystems (5)

v3 of 18 Superseded Latest is v18 →

lore
Share & tools
lore

Hannes Reinecke

author maintainer
#0 lore raw

v3 cover 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.

Changes to v2:
- Dropped non-standard algorithms
- Reworked base64 based on fs/crypto/fname.c
- Fixup crash with no keys

Changes to the original submission:
- Included reviews from Vladislav
- Included reviews from Sagi
- Implemented re-authentication support
- Fixed up key handling

Hannes Reinecke (12):
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-fabrics: decode 'authentication required' connect error
nvme: Implement In-Band authentication
nvme-auth: Diffie-Hellman key exchange support
nvmet: Parse fabrics commands on all queues
nvmet: Implement basic In-Band Authentication
nvmet-auth: Diffie-Hellman key exchange support
nvmet-auth: expire authentication sessions

 crypto/Kconfig                         |    8 +
 crypto/Makefile                        |    1 +
 crypto/ffdhe_helper.c                  |  880 +++++++++++++++
 crypto/kpp.c                           |    6 +
 crypto/shash.c                         |    6 +
 drivers/nvme/host/Kconfig              |   13 +
 drivers/nvme/host/Makefile             |    1 +
 drivers/nvme/host/auth.c               | 1441 ++++++++++++++++++++++++
 drivers/nvme/host/auth.h               |   33 +
 drivers/nvme/host/core.c               |   79 +-
 drivers/nvme/host/fabrics.c            |   77 +-
 drivers/nvme/host/fabrics.h            |    6 +
 drivers/nvme/host/nvme.h               |   30 +
 drivers/nvme/host/trace.c              |   32 +
 drivers/nvme/target/Kconfig            |   12 +
 drivers/nvme/target/Makefile           |    1 +
 drivers/nvme/target/admin-cmd.c        |    4 +
 drivers/nvme/target/auth.c             |  442 ++++++++
 drivers/nvme/target/configfs.c         |  102 +-
 drivers/nvme/target/core.c             |   10 +
 drivers/nvme/target/fabrics-cmd-auth.c |  506 +++++++++
 drivers/nvme/target/fabrics-cmd.c      |   30 +-
 drivers/nvme/target/nvmet.h            |   70 ++
 include/crypto/ffdhe.h                 |   24 +
 include/crypto/hash.h                  |    2 +
 include/crypto/kpp.h                   |    2 +
 include/linux/base64.h                 |   16 +
 include/linux/nvme.h                   |  186 ++-
 lib/Makefile                           |    2 +-
 lib/base64.c                           |  100 ++
 30 files changed, 4111 insertions(+), 11 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
author maintainer
#1 lore raw

PATCH v3 1/12 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
index 0a0a50cb694f..4c88e63b3350 100644
@@ -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
index f140e4643949..f5841992dc9b 100644
@@ -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
Reviewed-by on v3 1/12
#3 lore raw

Re: [PATCH 01/12] crypto: add crypto_has_shash()

In reply to Hannes Reinecke
Quoted 4 lines · Hannes Reinecke

Add helper function to determine if a given synchronous hash is supported.

Looks good.

_______________________________________________
Linux-nvme mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-nvme

author maintainer
#4 lore raw

PATCH v3 2/12 crypto: add crypto_has_kpp()

In reply to Hannes Reinecke

Add 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
index 313b2c699963..416e8a1a03ee 100644
@@ -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
index cccceadc164b..24d01e9877c1 100644
@@ -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
Reviewed-by on v3 2/12
#6 lore raw

Re: [PATCH 02/12] crypto: add crypto_has_kpp()

In reply to Hannes Reinecke
Quoted 5 lines · Hannes Reinecke

Add helper function to determine if a given key-agreement protocol primitive is supported.

---

Looks good.

_______________________________________________
Linux-nvme mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-nvme

author maintainer
#7 lore raw

PATCH v3 3/12 crypto/ffdhe: Finite Field DH Ephemeral Parameters

In reply to Hannes Reinecke

Add helper functions to generaten Finite Field DH Ephemeral Parameters as specified in RFC 7919.

---
 crypto/Kconfig         |   8 +
 crypto/Makefile        |   1 +
 crypto/ffdhe_helper.c  | 880 +++++++++++++++++++++++++++++++++++++++++
 include/crypto/ffdhe.h |  24 ++
 4 files changed, 913 insertions(+)
 create mode 100644 crypto/ffdhe_helper.c
 create mode 100644 include/crypto/ffdhe.h
crypto/Kconfig +8 -0
index 536df4b6b825..2178649c8128 100644
@@ -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
index c633f15a0481..2b29a35f375a 100644
@@ -176,6 +176,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 +880 -0

880 lines not on this page · raw message

include/crypto/ffdhe.h new file +24 -0
new file mode 100644
index 000000000000..6cb9253ddb34
@@ -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
author maintainer
#8 lore raw

PATCH v3 4/12 lib/base64: RFC4648-compliant base64 encoding

In reply to Hannes Reinecke

Add RFC4648-compliant base64 encoding and decoding routines, based on the base64url encoding in fs/crypto/fname.c.

---
 include/linux/base64.h |  16 +++++++
 lib/Makefile           |   2 +-
 lib/base64.c           | 100 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 117 insertions(+), 1 deletion(-)
 create mode 100644 include/linux/base64.h
 create mode 100644 lib/base64.c
include/linux/base64.h new file +16 -0
new file mode 100644
index 000000000000..660d4cb1ef31
@@ -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
index 5efd1b435a37..ce964f013412 100644
@@ -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 +100 -0

100 lines not on this page · raw message

Signature · 1 line
2.29.2
author maintainer
#9 lore raw

PATCH v3 5/12 nvme: add definitions for NVMe In-Band authentication

In reply to Hannes Reinecke
---
 include/linux/nvme.h | 186 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 185 insertions(+), 1 deletion(-)
include/linux/nvme.h +185 -1
index b7c4c4130b65..e2142e3246eb 100644
@@ -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,183 @@ 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[16];
+};
+
+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[16];
+};
+
+/* 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,
+};
+
+/* Defined hash functions for DH-HMAC-CHAP authentication */
+enum {
+	NVME_AUTH_DHCHAP_SHA256	= 0x01,
+	NVME_AUTH_DHCHAP_SHA384	= 0x02,
+	NVME_AUTH_DHCHAP_SHA512	= 0x03,
+};
+
+/* Defined Diffie-Hellman group identifiers for DH-HMAC-CHAP authentication */
+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;
+	__le16		rsvd;
+	__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;
+	__u16		rsvd1;
+	__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;
+	__le16		rsvd1;
+	__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;
+	__le16		rsvd1;
+	__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;
+	__le16		rsvd1;
+	__le16		t_id;
+	__u8		rsvd2[10];
+};
+
+struct nvmf_auth_dhchap_failure_data {
+	__u8		auth_type;
+	__u8		auth_id;
+	__le16		rsvd1;
+	__le16		t_id;
+	__u8		rescode;
+	__u8		rescode_exp;
+};
+
+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_INCORRECT_PAYLOAD	= 0x06,
+	NVME_AUTH_DHCHAP_FAILURE_INCORRECT_MESSAGE	= 0x07,
+};
+
+
 struct nvme_dbbuf {
 	__u8			opcode;
 	__u8			flags;
@@ -1436,6 +1618,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
#11 lore raw

Re: [PATCH 05/12] nvme: add definitions for NVMe In-Band authentication

In reply to Hannes Reinecke
Quoted 5 lines · Hannes Reinecke
---
  include/linux/nvme.h | 186 ++++++++++++++++++++++++++++++++++++++++++-
  1 file changed, 185 insertions(+), 1 deletion(-)

Probably worth mentioning a TP name here so we can refer later, instead of empty commit message ?

_______________________________________________
Linux-nvme mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-nvme

author maintainer
#12 lore raw

Re: [PATCH 05/12] nvme: add definitions for NVMe In-Band authentication

Quoted 10 lines · Chaitanya Kulkarni
Quoted 5 lines · Hannes Reinecke
---
   include/linux/nvme.h | 186 ++++++++++++++++++++++++++++++++++++++++++-
   1 file changed, 185 insertions(+), 1 deletion(-)

Probably worth mentioning a TP name here so we can refer later, instead of empty commit message ?

Had been thinking about it, but then decided against it. Once the TPAR
is folded into the main spec it's getting really hard to figure out exactly what individual TPARs were referring to, so I prefer to stick with 'In-Band authentication' instead of the TPAR number.
But I can add that to the commit message, sure.

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
author maintainer
#13 lore raw

PATCH v3 6/12 nvme-fabrics: decode 'authentication required' connect error

In reply to Hannes Reinecke

The 'connect' command might fail with NVME_SC_AUTH_REQUIRED, so we should be decoding this error, too.

---
 drivers/nvme/host/fabrics.c | 4 ++++
 1 file changed, 4 insertions(+)
drivers/nvme/host/fabrics.c +4 -0
index 668c6bb7a567..9a8eade7cd23 100644
@@ -332,6 +332,10 @@ static void nvmf_log_connect_error(struct nvme_ctrl *ctrl,
 		dev_err(ctrl->device,
 			"Connect command failed: host path error\n");
 		break;
+	case NVME_SC_AUTH_REQUIRED:
+		dev_err(ctrl->device,
+			"Connect command failed: authentication required\n");
+		break;
 	default:
 		dev_err(ctrl->device,
 			"Connect command failed, error wo/DNR bit: %d\n",
Signature · 1 line
2.29.2
Reviewed-by on v3 6/12
#15 lore raw

Re: [PATCH 06/12] nvme-fabrics: decode 'authentication required' connect error

In reply to Hannes Reinecke
Quoted 4 lines · Hannes Reinecke

The 'connect' command might fail with NVME_SC_AUTH_REQUIRED, so we should be decoding this error, too.

Looks good.

_______________________________________________
Linux-nvme mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-nvme

author maintainer
#16 lore raw

PATCH v3 7/12 nvme: Implement In-Band authentication

In reply to Hannes Reinecke

Implement NVMe-oF In-Band authentication according to NVMe TPAR 8006. This patch adds two new fabric options 'dhchap_secret' to specify the pre-shared key (in ASCII respresentation according to NVMe 2.0 section 8.13.5.8 'Secret representation') and 'dhchap_bidi' to request bi-directional authentication of both the host and the controller. Re-authentication can be triggered by writing the PSK into the new controller sysfs attribute 'dhchap_secret'.

---
 drivers/nvme/host/Kconfig   |   12 +
 drivers/nvme/host/Makefile  |    1 +
 drivers/nvme/host/auth.c    | 1285 +++++++++++++++++++++++++++++++++++
 drivers/nvme/host/auth.h    |   25 +
 drivers/nvme/host/core.c    |   79 ++-
 drivers/nvme/host/fabrics.c |   73 +-
 drivers/nvme/host/fabrics.h |    6 +
 drivers/nvme/host/nvme.h    |   30 +
 drivers/nvme/host/trace.c   |   32 +
 9 files changed, 1537 insertions(+), 6 deletions(-)
 create mode 100644 drivers/nvme/host/auth.c
 create mode 100644 drivers/nvme/host/auth.h
drivers/nvme/host/Kconfig +12 -0
index dc0450ca23a3..97e8412dc42d 100644
@@ -83,3 +83,15 @@ 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_CORE
+	select CRYPTO_HMAC
+	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
index dfaacd472e5d..4bae2a4a8d8c 100644
@@ -15,6 +15,7 @@ nvme-core-$(CONFIG_NVME_MULTIPATH)	+= multipath.o
 nvme-core-$(CONFIG_BLK_DEV_ZONED)	+= zns.o
 nvme-core-$(CONFIG_FAULT_INJECTION_DEBUG_FS)	+= fault_inject.o
 nvme-core-$(CONFIG_NVME_HWMON)		+= hwmon.o
+nvme-core-$(CONFIG_NVME_AUTH)		+= auth.o
 
 nvme-y					+= pci.o
 
drivers/nvme/host/auth.c new file +1285 -0

1,285 lines not on this page · raw message

drivers/nvme/host/auth.h new file +25 -0
new file mode 100644
index 000000000000..cf1255f9db6d
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions
+ */
+
+#ifndef _NVME_AUTH_H
+#define _NVME_AUTH_H
+
+#include <crypto/kpp.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);
+
+unsigned char *nvme_auth_extract_secret(unsigned char *dhchap_secret,
+					size_t *dhchap_key_len);
+u8 *nvme_auth_transform_key(u8 *key, size_t key_len, u8 key_hash, char *nqn);
+
+#endif /* _NVME_AUTH_H */
drivers/nvme/host/core.c +76 -3

154 lines not on this page · raw message

drivers/nvme/host/fabrics.c +70 -3

128 lines not on this page · raw message

drivers/nvme/host/fabrics.h +6 -0
index a146cb903869..27df1aac5736 100644
@@ -67,6 +67,8 @@ 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_BIDI	= 1 << 23,
 };
 
 /**
@@ -96,6 +98,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_bidi: enable DH-HMAC-CHAP bi-directional authentication
  * @disable_sqflow: disable controller sq flow control
  * @hdr_digest: generate/verify header digest (TCP)
  * @data_digest: generate/verify data digest (TCP)
@@ -120,6 +124,8 @@ struct nvmf_ctrl_options {
 	unsigned int		kato;
 	struct nvmf_host	*host;
 	int			max_reconnects;
+	char			*dhchap_secret;
+	bool			dhchap_bidi;
 	bool			disable_sqflow;
 	bool			hdr_digest;
 	bool			data_digest;
drivers/nvme/host/nvme.h +30 -0
index 9871c0c9374c..b0dcb7d79b9e 100644
@@ -318,6 +318,15 @@ struct nvme_ctrl {
 	struct work_struct ana_work;
 #endif
 
+#ifdef CONFIG_NVME_AUTH
+	struct work_struct dhchap_auth_work;
+	struct list_head dhchap_auth_list;
+	struct mutex dhchap_auth_mutex;
+	unsigned char *dhchap_key;
+	size_t dhchap_key_len;
+	u16 transaction;
+#endif
+
 	/* Power saving configuration */
 	u64 ps_max_latency_us;
 	bool apst_enabled;
@@ -885,6 +894,27 @@ static inline bool nvme_ctrl_sgl_supported(struct nvme_ctrl *ctrl)
 	return ctrl->sgls & ((1 << 0) | (1 << 1));
 }
 
+#ifdef CONFIG_NVME_AUTH
+void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl);
+void nvme_auth_stop(struct nvme_ctrl *ctrl);
+int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid);
+int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid);
+void nvme_auth_free(struct nvme_ctrl *ctrl);
+int nvme_auth_generate_key(struct nvme_ctrl *ctrl);
+#else
+static inline void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl) {};
+static inline void nvme_auth_stop(struct nvme_ctrl *ctrl) {};
+static inline int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
+{
+	return -EPROTONOSUPPORT;
+}
+static inline int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid)
+{
+	return NVME_SC_AUTH_REQUIRED;
+}
+static inline void nvme_auth_free(struct nvme_ctrl *ctrl) {};
+#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
index 2a89c5aa0790..1c36fcedea20 100644
@@ -287,6 +287,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);
@@ -306,6 +334,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
maintainer
#17 lore raw

Re: [PATCH 07/12] nvme: Implement In-Band authentication

Quoted 31 lines · Hannes Reinecke

Implement NVMe-oF In-Band authentication according to NVMe TPAR 8006. This patch adds two new fabric options 'dhchap_secret' to specify the pre-shared key (in ASCII respresentation according to NVMe 2.0 section 8.13.5.8 'Secret representation') and 'dhchap_bidi' to request bi-directional authentication of both the host and the controller. Re-authentication can be triggered by writing the PSK into the new controller sysfs attribute 'dhchap_secret'.

---
  drivers/nvme/host/Kconfig   |   12 +
  drivers/nvme/host/Makefile  |    1 +
  drivers/nvme/host/auth.c    | 1285 +++++++++++++++++++++++++++++++++++
  drivers/nvme/host/auth.h    |   25 +
  drivers/nvme/host/core.c    |   79 ++-
  drivers/nvme/host/fabrics.c |   73 +-
  drivers/nvme/host/fabrics.h |    6 +
  drivers/nvme/host/nvme.h    |   30 +
  drivers/nvme/host/trace.c   |   32 +
  9 files changed, 1537 insertions(+), 6 deletions(-)

create mode 100644 drivers/nvme/host/auth.c
create mode 100644 drivers/nvme/host/auth.h

drivers/nvme/host/Kconfig +0 -0
index dc0450ca23a3..97e8412dc42d 100644
@@ -83,3 +83,15 @@ config NVME_TCP
  	  from https://github.com/linux-nvme/nvme-cli.
 
  	  If unsure, say N.

drivers/nvme/host/Kconfig config NVME_TCP · line 95

Quoted 10 lines
drivers/nvme/host/Kconfig +10 -0
+
+config NVME_AUTH
+	bool "NVM Express over Fabrics In-Band Authentication"
+	depends on NVME_CORE
+	select CRYPTO_HMAC
+	select CRYPTO_SHA256
+	select CRYPTO_SHA512
+	help
+	  This provides support for NVMe over Fabrics In-Band Authentication
+	  for the NVMe over TCP transport.

Not tcp specific...

Quoted 45 lines
drivers/nvme/host/auth.c new file +39 -0
new file mode 100644
index 000000000000..5393ac16a002
@@ -0,0 +1,1285 @@
+// 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/dh.h>
+#include <crypto/ffdhe.h>
+#include "nvme.h"
+#include "fabrics.h"
+#include "auth.h"
+
+static u32 nvme_dhchap_seqnum;
+
+struct nvme_dhchap_queue_context {
+	struct list_head entry;
+	struct work_struct auth_work;
+	struct nvme_ctrl *ctrl;
+	struct crypto_shash *shash_tfm;
+	struct crypto_kpp *dh_tfm;
+	void *buf;
+	size_t buf_size;
+	int qid;
+	int error;
+	u32 s1;
+	u32 s2;
+	u16 transaction;
+	u8 status;
+	u8 hash_id;
+	u8 hash_len;
+	u8 dhgroup_id;
+	u8 c1[64];
+	u8 c2[64];
+	u8 response[64];
+	u8 *host_response;
+};

drivers/nvme/host/auth.c line 49

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+
+static 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",

Nit, no need for all-caps, can do "null"

Quoted 127 lines
drivers/nvme/host/auth.c new file +127 -0
+	  .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);
+
+static struct nvme_dhchap_hash_map {
+	int id;
+	const char hmac[15];
+	const char digest[15];
+} hash_map[] = {
+	{.id = NVME_AUTH_DHCHAP_SHA256,
+	 .hmac = "hmac(sha256)", .digest = "sha256" },
+	{.id = NVME_AUTH_DHCHAP_SHA384,
+	 .hmac = "hmac(sha384)", .digest = "sha384" },
+	{.id = NVME_AUTH_DHCHAP_SHA512,
+	 .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_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;
+}

drivers/nvme/host/auth.c line 186

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+EXPORT_SYMBOL_GPL(nvme_auth_hmac_id);
+
+unsigned char *nvme_auth_extract_secret(unsigned char *secret, size_t *out_len)
+{
+	unsigned char *key;
+	u32 crc;
+	int key_len;
+	size_t allocated_len;
+
+	allocated_len = strlen(secret);

Can move to declaration initializer.

Quoted 39 lines
drivers/nvme/host/auth.c new file +39 -0
+	key = kzalloc(allocated_len, GFP_KERNEL);
+	if (!key)
+		return ERR_PTR(-ENOMEM);
+
+	key_len = base64_decode(secret, allocated_len, key);
+	if (key_len != 36 && key_len != 52 &&
+	    key_len != 68) {
+		pr_debug("Invalid DH-HMAC-CHAP key len %d\n",
+			 key_len);
+		kfree_sensitive(key);
+		return ERR_PTR(-EINVAL);
+	}
+
+	/* 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, key, key_len);
+
+	if (get_unaligned_le32(key + key_len) != crc) {
+		pr_debug("DH-HMAC-CHAP key crc mismatch (key %08x, crc %08x)\n",
+		       get_unaligned_le32(key + key_len), crc);
+		kfree_sensitive(key);
+		return ERR_PTR(-EKEYREJECTED);
+	}
+	*out_len = key_len;
+	return key;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_extract_secret);
+
+u8 *nvme_auth_transform_key(u8 *key, size_t key_len, u8 key_hash, char *nqn)
+{
+	const char *hmac_name = nvme_auth_hmac_name(key_hash);
+	struct crypto_shash *key_tfm;
+	struct shash_desc *shash;
+	u8 *transformed_key;
+	int ret;

drivers/nvme/host/auth.c line 235

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+
+	/* No key transformation required */
+	if (key_hash == 0)
+		return 0;
+
+	hmac_name = nvme_auth_hmac_name(key_hash);
+	if (!hmac_name) {
+		pr_warn("Invalid key hash id %d\n", key_hash);
+		return ERR_PTR(-EKEYREJECTED);
+	}

newline here.

drivers/nvme/host/auth.c line 246

Quoted 11 lines
drivers/nvme/host/auth.c new file +11 -0
+	key_tfm = crypto_alloc_shash(hmac_name, 0, 0);
+	if (IS_ERR(key_tfm))
+		return (u8 *)key_tfm;
+
+	shash = kmalloc(sizeof(struct shash_desc) +
+			crypto_shash_descsize(key_tfm),
+			GFP_KERNEL);
+	if (!shash) {
+		crypto_free_shash(key_tfm);
+		return ERR_PTR(-ENOMEM);
+	}

newline here.

Quoted 17 lines
drivers/nvme/host/auth.c new file +17 -0
+	transformed_key = kzalloc(crypto_shash_digestsize(key_tfm), GFP_KERNEL);
+	if (!transformed_key) {
+		ret = -ENOMEM;
+		goto out_free_shash;
+	}
+
+	shash->tfm = key_tfm;
+	ret = crypto_shash_setkey(key_tfm, key, key_len);
+	if (ret < 0)
+		goto out_free_shash;
+	ret = crypto_shash_init(shash);
+	if (ret < 0)
+		goto out_free_shash;
+	ret = crypto_shash_update(shash, nqn, strlen(nqn));
+	if (ret < 0)
+		goto out_free_shash;
+	ret = crypto_shash_update(shash, "NVMe-over-Fabrics", 17);

drivers/nvme/host/auth.c line 273

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	if (ret < 0)
+		goto out_free_shash;
+	ret = crypto_shash_final(shash, transformed_key);
+out_free_shash:
+	kfree(shash);
+	crypto_free_shash(key_tfm);
+	if (ret < 0) {
+		kfree_sensitive(transformed_key);
+		return ERR_PTR(ret);
+	}

Any reason why this is not a reverse cleanup with goto call-sites standard style?

Quoted 44 lines
drivers/nvme/host/auth.c new file +44 -0
+	return transformed_key;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_transform_key);
+
+static int nvme_auth_hash_skey(int hmac_id, u8 *skey, size_t skey_len, u8 *hkey)
+{
+	const char *digest_name;
+	struct crypto_shash *tfm;
+	int ret;
+
+	digest_name = nvme_auth_digest_name(hmac_id);
+	if (!digest_name) {
+		pr_debug("%s: failed to get digest for %d\n", __func__,
+			 hmac_id);
+		return -EINVAL;
+	}
+	tfm = crypto_alloc_shash(digest_name, 0, 0);
+	if (IS_ERR(tfm))
+		return -ENOMEM;
+
+	ret = crypto_shash_tfm_digest(tfm, skey, skey_len, hkey);
+	if (ret < 0)
+		pr_debug("%s: Failed to hash digest len %zu\n", __func__,
+			 skey_len);
+
+	crypto_free_shash(tfm);
+	return ret;
+}
+
+int nvme_auth_augmented_challenge(u8 hmac_id, u8 *skey, size_t skey_len,
+		u8 *challenge, u8 *aug, size_t hlen)
+{
+	struct crypto_shash *tfm;
+	struct shash_desc *desc;
+	u8 *hashed_key;
+	const char *hmac_name;
+	int ret;
+
+	hashed_key = kmalloc(hlen, GFP_KERNEL);
+	if (!hashed_key)
+		return -ENOMEM;
+
+	ret = nvme_auth_hash_skey(hmac_id, skey,
+				  skey_len, hashed_key);

drivers/nvme/host/auth.c line 327

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	if (ret < 0)
+		goto out_free_key;
+
+	hmac_name = nvme_auth_hmac_name(hmac_id);
+	if (!hmac_name) {
+		pr_warn("%s: invalid hash algoritm %d\n",
+			__func__, hmac_id);
+		ret = -EINVAL;
+		goto out_free_key;
+	}

newline.

drivers/nvme/host/auth.c line 332

Quoted 5 lines
drivers/nvme/host/auth.c new file +5 -0
+	tfm = crypto_alloc_shash(hmac_name, 0, 0);
+	if (IS_ERR(tfm)) {
+		ret = PTR_ERR(tfm);
+		goto out_free_key;
+	}

newline

Quoted 87 lines
drivers/nvme/host/auth.c new file +87 -0
+	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, hlen);
+	if (ret)
+		goto out_free_desc;
+
+	ret = crypto_shash_init(desc);
+	if (ret)
+		goto out_free_desc;
+
+	ret = crypto_shash_update(desc, challenge, hlen);
+	if (ret)
+		goto out_free_desc;
+
+	ret = crypto_shash_final(desc, aug);
+out_free_desc:
+	kfree_sensitive(desc);
+out_free_hash:
+	crypto_free_shash(tfm);
+out_free_key:
+	kfree_sensitive(hashed_key);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_augmented_challenge);
+
+int nvme_auth_gen_privkey(struct crypto_kpp *dh_tfm, int dh_gid)
+{
+	char *pkey;
+	int ret, pkey_len;
+
+	if (dh_gid == NVME_AUTH_DHCHAP_DHGROUP_2048 ||
+	    dh_gid == NVME_AUTH_DHCHAP_DHGROUP_3072 ||
+	    dh_gid == NVME_AUTH_DHCHAP_DHGROUP_4096 ||
+	    dh_gid == NVME_AUTH_DHCHAP_DHGROUP_6144 ||
+	    dh_gid == NVME_AUTH_DHCHAP_DHGROUP_8192) {
+		struct dh p = {0};
+		int bits = nvme_auth_dhgroup_pubkey_size(dh_gid) << 3;
+		int dh_secret_len = 64;
+		u8 *dh_secret = kzalloc(dh_secret_len, GFP_KERNEL);
+
+		if (!dh_secret)
+			return -ENOMEM;
+
+		/*
+		 * NVMe base spec v2.0: The DH value shall be set to the value
+		 * of g^x mod p, where 'x' is a random number selected by the
+		 * host that shall be at least 256 bits long.
+		 *
+		 * We will be using a 512 bit random number as private key.
+		 * This is large enough to provide adequate security, but
+		 * small enough such that we can trivially conform to
+		 * NIST SB800-56A section 5.6.1.1.4 if
+		 * we guarantee that the random number is not either
+		 * all 0xff or all 0x00. But that should be guaranteed
+		 * by the in-kernel RNG anyway.
+		 */
+		get_random_bytes(dh_secret, dh_secret_len);
+
+		ret = crypto_ffdhe_params(&p, bits);
+		if (ret) {
+			kfree_sensitive(dh_secret);
+			return ret;
+		}
+
+		p.key = dh_secret;
+		p.key_size = dh_secret_len;
+
+		pkey_len = crypto_dh_key_len(&p);
+		pkey = kmalloc(pkey_len, GFP_KERNEL);
+		if (!pkey) {
+			kfree_sensitive(dh_secret);
+			return -ENOMEM;
+		}
+
+		get_random_bytes(pkey, pkey_len);
+		ret = crypto_dh_encode_key(pkey, pkey_len, &p);
+		if (ret) {
+			pr_debug("failed to encode private key, error %d\n",
+				 ret);
+			kfree_sensitive(dh_secret);
+			goto out;

drivers/nvme/host/auth.c line 429

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+		}
+	} else {
+		pr_warn("invalid dh group %d\n", dh_gid);
+		return -EINVAL;
+	}
+	ret = crypto_kpp_set_secret(dh_tfm, pkey, pkey_len);
+	if (ret)
+		pr_debug("failed to set private key, error %d\n", ret);
+out:
+	kfree_sensitive(pkey);

pkey can be unset here.

Quoted 15 lines
drivers/nvme/host/auth.c new file +15 -0
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_gen_privkey);
+
+int nvme_auth_gen_pubkey(struct crypto_kpp *dh_tfm,
+		u8 *host_key, size_t host_key_len)
+{
+	struct kpp_request *req;
+	struct crypto_wait wait;
+	struct scatterlist dst;
+	int ret;
+
+	req = kpp_request_alloc(dh_tfm, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;

drivers/nvme/host/auth.c line 454

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+
+	crypto_init_wait(&wait);
+	kpp_request_set_input(req, NULL, 0);
+	sg_init_one(&dst, host_key, host_key_len);
+	kpp_request_set_output(req, &dst, 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);
+

no need for this newline

Quoted 74 lines
drivers/nvme/host/auth.c new file +74 -0
+	kpp_request_free(req);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_gen_pubkey);
+
+int nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm,
+		u8 *ctrl_key, size_t ctrl_key_len,
+		u8 *sess_key, size_t sess_key_len)
+{
+	struct kpp_request *req;
+	struct crypto_wait wait;
+	struct scatterlist src, dst;
+	int ret;
+
+	req = kpp_request_alloc(dh_tfm, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	crypto_init_wait(&wait);
+	sg_init_one(&src, ctrl_key, ctrl_key_len);
+	kpp_request_set_input(req, &src, ctrl_key_len);
+	sg_init_one(&dst, sess_key, sess_key_len);
+	kpp_request_set_output(req, &dst, 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);
+
+	kpp_request_free(req);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_gen_shared_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 > 0)
+		dev_dbg(ctrl->device,
+			"%s: qid %d nvme status %d\n", __func__, qid, ret);
+	else if (ret < 0)
+		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)
+{
+	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_receive.opcode = nvme_fabrics_command;
+	cmd.auth_receive.fctype = nvme_fabrics_type_auth_receive;
+	cmd.auth_receive.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER;

drivers/nvme/host/auth.c line 538

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	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;

Why EIO?

drivers/nvme/host/auth.c line 544

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+	}
+	if (ret < 0) {
+		dev_dbg(ctrl->device, "%s: qid %d error %d\n",
+			__func__, qid, ret);
+		return ret;
+	}

Why did you choose to do these error conditionals differently for the send and receive functions?

Quoted 29 lines
drivers/nvme/host/auth.c new file +29 -0
+
+	return 0;
+}
+
+static int nvme_auth_receive_validate(struct nvme_ctrl *ctrl, int qid,
+		struct nvmf_auth_dhchap_failure_data *data,
+		u16 transaction, u8 expected_msg)
+{
+	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->rescode_exp;
+	}
+	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_INCORRECT_MESSAGE;
+	}
+	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_INCORRECT_MESSAGE;
+	}
+	return 0;

drivers/nvme/host/auth.c line 583

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+}
+
+static int nvme_auth_set_dhchap_negotiate_data(struct nvme_ctrl *ctrl,
+		struct nvme_dhchap_queue_context *chap)
+{
+	struct nvmf_auth_dhchap_negotiate_data *data = chap->buf;
+	size_t size = sizeof(*data) + sizeof(union nvmf_auth_protocol);
+
+	if (chap->buf_size < size) {
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;

Is this an internal error? not sure I understand setting of this status

Quoted 42 lines
drivers/nvme/host/auth.c new file +42 -0
+		return -EINVAL;
+	}
+	memset((u8 *)chap->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 = 6;
+	data->auth_protocol[0].dhchap.idlist[0] = NVME_AUTH_DHCHAP_SHA256;
+	data->auth_protocol[0].dhchap.idlist[1] = NVME_AUTH_DHCHAP_SHA384;
+	data->auth_protocol[0].dhchap.idlist[2] = NVME_AUTH_DHCHAP_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;
+}
+
+static int nvme_auth_process_dhchap_challenge(struct nvme_ctrl *ctrl,
+		struct nvme_dhchap_queue_context *chap)
+{
+	struct nvmf_auth_dhchap_challenge_data *data = chap->buf;
+	size_t size = sizeof(*data) + data->hl + data->dhvlen;
+	const char *hmac_name;
+
+	if (chap->buf_size < size) {
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+		return NVME_SC_INVALID_FIELD;
+	}
+
+	hmac_name = nvme_auth_hmac_name(data->hashid);
+	if (!hmac_name) {
+		dev_warn(ctrl->device,
+			 "qid %d: invalid HASH ID %d\n",
+			 chap->qid, data->hashid);
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;

drivers/nvme/host/auth.c line 635

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+		return -EPROTO;
+	}
+	if (chap->hash_id == data->hashid && chap->shash_tfm &&
+	    !strcmp(crypto_shash_alg_name(chap->shash_tfm), hmac_name) &&
+	    crypto_shash_digestsize(chap->shash_tfm) == data->hl) {
+		dev_dbg(ctrl->device,
+			"qid %d: reuse existing hash %s\n",
+			chap->qid, hmac_name);
+		goto select_kpp;
+	}

newline

drivers/nvme/host/auth.c line 640

Quoted 5 lines
drivers/nvme/host/auth.c new file +5 -0
+	if (chap->shash_tfm) {
+		crypto_free_shash(chap->shash_tfm);
+		chap->hash_id = 0;
+		chap->hash_len = 0;
+	}

newline

drivers/nvme/host/auth.c line 650

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	chap->shash_tfm = crypto_alloc_shash(hmac_name, 0,
+					     CRYPTO_ALG_ALLOCATES_MEMORY);
+	if (IS_ERR(chap->shash_tfm)) {
+		dev_warn(ctrl->device,
+			 "qid %d: failed to allocate hash %s, error %ld\n",
+			 chap->qid, hmac_name, PTR_ERR(chap->shash_tfm));
+		chap->shash_tfm = NULL;
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
+		return NVME_SC_AUTH_REQUIRED;
+	}

newline

drivers/nvme/host/auth.c line 659

Quoted 9 lines
drivers/nvme/host/auth.c new file +9 -0
+	if (crypto_shash_digestsize(chap->shash_tfm) != data->hl) {
+		dev_warn(ctrl->device,
+			 "qid %d: invalid hash length %d\n",
+			 chap->qid, data->hl);
+		crypto_free_shash(chap->shash_tfm);
+		chap->shash_tfm = NULL;
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
+		return NVME_SC_AUTH_REQUIRED;
+	}

newline

drivers/nvme/host/auth.c line 661

Quoted 2 lines
drivers/nvme/host/auth.c new file +2 -0
+	if (chap->hash_id != data->hashid) {
+		kfree(chap->host_response);

kfree_sensitive? also why is is freed here? where was it allocated?

Quoted 4 lines
drivers/nvme/host/auth.c new file +4 -0
+		chap->host_response = NULL;
+	}
+	chap->hash_id = data->hashid;
+	chap->hash_len = data->hl;

drivers/nvme/host/auth.c line 675

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	dev_dbg(ctrl->device, "qid %d: selected hash %s\n",
+		chap->qid, hmac_name);
+
+	gid_name = nvme_auth_dhgroup_kpp(data->dhgid);
+	if (!gid_name) {
+		dev_warn(ctrl->device,
+			 "qid %d: invalid DH group id %d\n",
+			 chap->qid, data->dhgid);
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
+		return -EPROTO;

No need for all the previous frees?
Maybe we can rework these such that we first do all the checks and then go and allocate stuff?

Quoted 39 lines
drivers/nvme/host/auth.c new file +39 -0
+	}
+
+	if (data->dhgid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
+		if (data->dhvlen == 0) {
+			dev_warn(ctrl->device,
+				 "qid %d: 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)) {
+			int ret = PTR_ERR(chap->dh_tfm);
+
+			dev_warn(ctrl->device,
+				 "qid %d: failed to initialize %s\n",
+				 chap->qid, gid_name);
+			chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
+			chap->dh_tfm = NULL;
+			return ret;
+		}
+		chap->dhgroup_id = data->dhgid;
+	} else if (data->dhvlen != 0) {
+		dev_warn(ctrl->device,
+			 "qid %d: invalid DH value for NULL DH\n",
+			chap->qid);
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
+		return -EPROTO;
+	}
+	dev_dbg(ctrl->device, "qid %d: selected DH group %s\n",
+		chap->qid, gid_name);
+
+select_kpp:
+	chap->s1 = le32_to_cpu(data->seqnum);
+	memcpy(chap->c1, data->cval, chap->hash_len);
+
+	return 0;
+}
+

drivers/nvme/host/auth.c line 724

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+static int nvme_auth_set_dhchap_reply_data(struct nvme_ctrl *ctrl,
+		struct nvme_dhchap_queue_context *chap)
+{
+	struct nvmf_auth_dhchap_reply_data *data = chap->buf;
+	size_t size = sizeof(*data);
+
+	size += 2 * chap->hash_len;
+	if (ctrl->opts->dhchap_bidi) {
+		get_random_bytes(chap->c2, chap->hash_len);
+		chap->s2 = nvme_dhchap_seqnum++;

Any serialization needed on nvme_dhchap_seqnum?

Quoted 7 lines
drivers/nvme/host/auth.c new file +7 -0
+	} else
+		memset(chap->c2, 0, chap->hash_len);
+
+
+	if (chap->buf_size < size) {
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+		return -EINVAL;

drivers/nvme/host/auth.c line 741

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	}
+	memset(chap->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 = 0;
+	data->seqnum = cpu_to_le32(chap->s2);
+	memcpy(data->rval, chap->response, chap->hash_len);
+	if (ctrl->opts->dhchap_bidi) {

Can we unite the "if (ctrl->opts->dhchap_bidi)"
conditionals?

Quoted 432 lines
drivers/nvme/host/auth.c new file +432 -0
+		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);
+	}
+	return size;
+}
+
+static int nvme_auth_process_dhchap_success1(struct nvme_ctrl *ctrl,
+		struct nvme_dhchap_queue_context *chap)
+{
+	struct nvmf_auth_dhchap_success1_data *data = chap->buf;
+	size_t size = sizeof(*data);
+
+	if (ctrl->opts->dhchap_bidi)
+		size += chap->hash_len;
+
+
+	if (chap->buf_size < size) {
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+		return NVME_SC_INVALID_FIELD;
+	}
+
+	if (data->hl != chap->hash_len) {
+		dev_warn(ctrl->device,
+			 "qid %d: invalid hash length %d\n",
+			 chap->qid, data->hl);
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
+		return NVME_SC_INVALID_FIELD;
+	}
+
+	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: controller authentication failed\n",
+			 chap->qid);
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
+		return NVME_SC_AUTH_REQUIRED;
+	}
+	dev_info(ctrl->device,
+		 "qid %d: controller authenticated\n",
+		chap->qid);
+	return 0;
+}
+
+static int nvme_auth_set_dhchap_success2_data(struct nvme_ctrl *ctrl,
+		struct nvme_dhchap_queue_context *chap)
+{
+	struct nvmf_auth_dhchap_success2_data *data = chap->buf;
+	size_t size = sizeof(*data);
+
+	memset(chap->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_set_dhchap_failure2_data(struct nvme_ctrl *ctrl,
+		struct nvme_dhchap_queue_context *chap)
+{
+	struct nvmf_auth_dhchap_failure_data *data = chap->buf;
+	size_t size = sizeof(*data);
+
+	memset(chap->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->rescode = NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED;
+	data->rescode_exp = chap->status;
+
+	return size;
+}
+
+static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
+		struct nvme_dhchap_queue_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);
+	if (chap->dh_tfm) {
+		challenge = kmalloc(chap->hash_len, GFP_KERNEL);
+		if (!challenge) {
+			ret = -ENOMEM;
+			goto out;
+		}
+		ret = nvme_auth_augmented_challenge(chap->hash_id,
+						    chap->sess_key,
+						    chap->sess_key_len,
+						    chap->c1, challenge,
+						    chap->hash_len);
+		if (ret)
+			goto out;
+	}
+	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:
+	if (challenge != chap->c1)
+		kfree(challenge);
+	return ret;
+}
+
+static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl,
+		struct nvme_dhchap_queue_context *chap)
+{
+	SHASH_DESC_ON_STACK(shash, chap->shash_tfm);
+	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->hash_id,
+						    chap->sess_key,
+						    chap->sess_key_len,
+						    chap->c2, challenge,
+						    chap->hash_len);
+		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",
+		__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:
+	if (challenge != chap->c2)
+		kfree(challenge);
+	return ret;
+}
+
+int nvme_auth_generate_key(struct nvme_ctrl *ctrl)
+{
+	int ret;
+	u8 key_hash;
+
+	if (!ctrl->opts->dhchap_secret)
+		return 0;
+
+	if (ctrl->dhchap_key && ctrl->dhchap_key_len)
+		/* Key already set */
+		return 0;
+
+	if (sscanf(ctrl->opts->dhchap_secret, "DHHC-1:%hhd:%*s:",
+		   &key_hash) != 1)
+		return -EINVAL;
+
+	/* Pass in the secret without the 'DHHC-1:XX:' prefix */
+	ctrl->dhchap_key = nvme_auth_extract_secret(ctrl->opts->dhchap_secret + 10,
+					     &ctrl->dhchap_key_len);
+	if (IS_ERR(ctrl->dhchap_key)) {
+		ret = PTR_ERR(ctrl->dhchap_key);
+		ctrl->dhchap_key = NULL;
+		return ret;
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_generate_key);
+
+static void nvme_auth_reset(struct nvme_dhchap_queue_context *chap)
+{
+	chap->status = 0;
+	chap->error = 0;
+	chap->s1 = 0;
+	chap->s2 = 0;
+	chap->transaction = 0;
+	memset(chap->c1, 0, sizeof(chap->c1));
+	memset(chap->c2, 0, sizeof(chap->c2));
+}
+
+static void __nvme_auth_free(struct nvme_dhchap_queue_context *chap)
+{
+	if (chap->shash_tfm)
+		crypto_free_shash(chap->shash_tfm);
+	kfree_sensitive(chap->host_response);
+	kfree(chap->buf);
+	kfree(chap);
+}
+
+static void __nvme_auth_work(struct work_struct *work)
+{
+	struct nvme_dhchap_queue_context *chap =
+		container_of(work, struct nvme_dhchap_queue_context, auth_work);
+	struct nvme_ctrl *ctrl = chap->ctrl;
+	size_t tl;
+	int ret = 0;
+
+	chap->transaction = ctrl->transaction++;
+
+	/* DH-HMAC-CHAP Step 1: send negotiate */
+	dev_dbg(ctrl->device, "%s: qid %d send negotiate\n",
+		__func__, chap->qid);
+	ret = nvme_auth_set_dhchap_negotiate_data(ctrl, chap);
+	if (ret < 0) {
+		chap->error = ret;
+		return;
+	}
+	tl = ret;
+	ret = nvme_auth_send(ctrl, chap->qid, chap->buf, tl);
+	if (ret) {
+		chap->error = ret;
+		return;
+	}
+
+	/* DH-HMAC-CHAP Step 2: receive challenge */
+	dev_dbg(ctrl->device, "%s: qid %d receive challenge\n",
+		__func__, chap->qid);
+
+	memset(chap->buf, 0, chap->buf_size);
+	ret = nvme_auth_receive(ctrl, chap->qid, chap->buf, chap->buf_size);
+	if (ret) {
+		dev_warn(ctrl->device,
+			 "qid %d failed to receive challenge, %s %d\n",
+			 chap->qid, ret < 0 ? "error" : "nvme status", ret);
+		chap->error = ret;
+		return;
+	}
+	ret = nvme_auth_receive_validate(ctrl, chap->qid, chap->buf, chap->transaction,
+					 NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE);
+	if (ret) {
+		chap->status = ret;
+		chap->error = NVME_SC_AUTH_REQUIRED;
+		return;
+	}
+
+	ret = nvme_auth_process_dhchap_challenge(ctrl, chap);
+	if (ret) {
+		/* Invalid challenge parameters */
+		goto fail2;
+	}
+
+	if (chap->ctrl_key_len) {
+		dev_dbg(ctrl->device,
+			"%s: qid %d DH exponential\n",
+			__func__, chap->qid);
+		ret = nvme_auth_dhchap_exponential(ctrl, chap);
+		if (ret)
+			goto fail2;
+	}
+
+	dev_dbg(ctrl->device, "%s: qid %d host response\n",
+		__func__, chap->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 send reply\n",
+		__func__, chap->qid);
+	ret = nvme_auth_set_dhchap_reply_data(ctrl, chap);
+	if (ret < 0)
+		goto fail2;
+
+	tl = ret;
+	ret = nvme_auth_send(ctrl, chap->qid, chap->buf, tl);
+	if (ret)
+		goto fail2;
+
+	/* DH-HMAC-CHAP Step 4: receive success1 */
+	dev_dbg(ctrl->device, "%s: qid %d receive success1\n",
+		__func__, chap->qid);
+
+	memset(chap->buf, 0, chap->buf_size);
+	ret = nvme_auth_receive(ctrl, chap->qid, chap->buf, chap->buf_size);
+	if (ret) {
+		dev_warn(ctrl->device,
+			 "qid %d failed to receive success1, %s %d\n",
+			 chap->qid, ret < 0 ? "error" : "nvme status", ret);
+		chap->error = ret;
+		return;
+	}
+	ret = nvme_auth_receive_validate(ctrl, chap->qid,
+					 chap->buf, chap->transaction,
+					 NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1);
+	if (ret) {
+		chap->status = ret;
+		chap->error = NVME_SC_AUTH_REQUIRED;
+		return;
+	}
+
+	if (ctrl->opts->dhchap_bidi) {
+		dev_dbg(ctrl->device,
+			"%s: qid %d controller response\n",
+			__func__, chap->qid);
+		ret = nvme_auth_dhchap_ctrl_response(ctrl, chap);
+		if (ret)
+			goto fail2;
+	}
+
+	ret = nvme_auth_process_dhchap_success1(ctrl, chap);
+	if (ret < 0) {
+		/* Controller authentication failed */
+		goto fail2;
+	}
+
+	/* DH-HMAC-CHAP Step 5: send success2 */
+	dev_dbg(ctrl->device, "%s: qid %d send success2\n",
+		__func__, chap->qid);
+	tl = nvme_auth_set_dhchap_success2_data(ctrl, chap);
+	ret = nvme_auth_send(ctrl, chap->qid, chap->buf, tl);
+	if (!ret) {
+		chap->error = 0;
+		return;
+	}
+
+fail2:
+	dev_dbg(ctrl->device, "%s: qid %d send failure2, status %x\n",
+		__func__, chap->qid, chap->status);
+	tl = nvme_auth_set_dhchap_failure2_data(ctrl, chap);
+	ret = nvme_auth_send(ctrl, chap->qid, chap->buf, tl);
+	if (!ret)
+		ret = -EPROTO;
+	chap->error = ret;
+}
+
+int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
+{
+	struct nvme_dhchap_queue_context *chap;
+
+	if (!ctrl->dhchap_key || !ctrl->dhchap_key_len) {
+		dev_warn(ctrl->device, "qid %d: no key\n", qid);
+		return -ENOKEY;
+	}
+
+	mutex_lock(&ctrl->dhchap_auth_mutex);
+	/* Check if the context is already queued */
+	list_for_each_entry(chap, &ctrl->dhchap_auth_list, entry) {
+		if (chap->qid == qid) {
+			mutex_unlock(&ctrl->dhchap_auth_mutex);
+			queue_work(nvme_wq, &chap->auth_work);
+			return 0;
+		}
+	}
+	chap = kzalloc(sizeof(*chap), GFP_KERNEL);
+	if (!chap) {
+		mutex_unlock(&ctrl->dhchap_auth_mutex);
+		return -ENOMEM;
+	}
+	chap->qid = qid;
+	chap->ctrl = ctrl;
+
+	/*
+	 * Allocate a large enough buffer for the entire negotiation:
+	 * 4k should be enough to ffdhe8192.
+	 */
+	chap->buf_size = 4096;
+	chap->buf = kzalloc(chap->buf_size, GFP_KERNEL);

drivers/nvme/host/auth.c line 1183

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	if (!chap->buf) {
+		mutex_unlock(&ctrl->dhchap_auth_mutex);
+		kfree(chap);
+		return -ENOMEM;
+	}
+
+	INIT_WORK(&chap->auth_work, __nvme_auth_work);
+	list_add(&chap->entry, &ctrl->dhchap_auth_list);
+	mutex_unlock(&ctrl->dhchap_auth_mutex);
+	queue_work(nvme_wq, &chap->auth_work);

Why is the auth in a work? e.g. it won't fail the connect?

Quoted 180 lines
drivers/nvme/host/auth.c new file +102 -0
+	return 0;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_negotiate);
+
+int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid)
+{
+	struct nvme_dhchap_queue_context *chap;
+	int ret;
+
+	mutex_lock(&ctrl->dhchap_auth_mutex);
+	list_for_each_entry(chap, &ctrl->dhchap_auth_list, entry) {
+		if (chap->qid != qid)
+			continue;
+		mutex_unlock(&ctrl->dhchap_auth_mutex);
+		flush_work(&chap->auth_work);
+		ret = chap->error;
+		nvme_auth_reset(chap);
+		return ret;
+	}
+	mutex_unlock(&ctrl->dhchap_auth_mutex);
+	return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_wait);
+
+/* Assumes that the controller is in state RESETTING */
+static void nvme_dhchap_auth_work(struct work_struct *work)
+{
+	struct nvme_ctrl *ctrl =
+		container_of(work, struct nvme_ctrl, dhchap_auth_work);
+	int ret, q;
+
+	nvme_stop_queues(ctrl);
+	/* Authenticate admin queue first */
+	ret = nvme_auth_negotiate(ctrl, NVME_QID_ANY);
+	if (ret) {
+		dev_warn(ctrl->device,
+			 "qid 0: error %d setting up authentication\n", ret);
+		goto out;
+	}
+	ret = nvme_auth_wait(ctrl, NVME_QID_ANY);
+	if (ret) {
+		dev_warn(ctrl->device,
+			 "qid 0: authentication failed\n");
+		goto out;
+	}
+	dev_info(ctrl->device, "qid 0: authenticated\n");
+
+	for (q = 1; q < ctrl->queue_count; q++) {
+		ret = nvme_auth_negotiate(ctrl, q);
+		if (ret) {
+			dev_warn(ctrl->device,
+				 "qid %d: error %d setting up authentication\n",
+				 q, ret);
+			goto out;
+		}
+	}
+out:
+	/*
+	 * Failure is a soft-state; credentials remain valid until
+	 * the controller terminates the connection.
+	 */
+	if (nvme_change_ctrl_state(ctrl, NVME_CTRL_LIVE))
+		nvme_start_queues(ctrl);
+}
+
+void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl)
+{
+	INIT_LIST_HEAD(&ctrl->dhchap_auth_list);
+	INIT_WORK(&ctrl->dhchap_auth_work, nvme_dhchap_auth_work);
+	mutex_init(&ctrl->dhchap_auth_mutex);
+	nvme_auth_generate_key(ctrl);
+}
+EXPORT_SYMBOL_GPL(nvme_auth_init_ctrl);
+
+void nvme_auth_stop(struct nvme_ctrl *ctrl)
+{
+	struct nvme_dhchap_queue_context *chap = NULL, *tmp;
+
+	cancel_work_sync(&ctrl->dhchap_auth_work);
+	mutex_lock(&ctrl->dhchap_auth_mutex);
+	list_for_each_entry_safe(chap, tmp, &ctrl->dhchap_auth_list, entry)
+		cancel_work_sync(&chap->auth_work);
+	mutex_unlock(&ctrl->dhchap_auth_mutex);
+}
+EXPORT_SYMBOL_GPL(nvme_auth_stop);
+
+void nvme_auth_free(struct nvme_ctrl *ctrl)
+{
+	struct nvme_dhchap_queue_context *chap = NULL, *tmp;
+
+	mutex_lock(&ctrl->dhchap_auth_mutex);
+	list_for_each_entry_safe(chap, tmp, &ctrl->dhchap_auth_list, entry) {
+		list_del_init(&chap->entry);
+		flush_work(&chap->auth_work);
+		__nvme_auth_free(chap);
+	}
+	mutex_unlock(&ctrl->dhchap_auth_mutex);
+	kfree(ctrl->dhchap_key);
+	ctrl->dhchap_key = NULL;
+	ctrl->dhchap_key_len = 0;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_free);
drivers/nvme/host/auth.h new file +25 -0
new file mode 100644
index 000000000000..cf1255f9db6d
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions
+ */
+
+#ifndef _NVME_AUTH_H
+#define _NVME_AUTH_H
+
+#include <crypto/kpp.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);
+
+unsigned char *nvme_auth_extract_secret(unsigned char *dhchap_secret,
+					size_t *dhchap_key_len);
+u8 *nvme_auth_transform_key(u8 *key, size_t key_len, u8 key_hash, char *nqn);
+
+#endif /* _NVME_AUTH_H */
drivers/nvme/host/core.c +9 -2
index 7efb31b87f37..f669b054790b 100644
@@ -24,6 +24,7 @@
 
  #include "nvme.h"
  #include "fabrics.h"
+#include "auth.h"
 
  #define CREATE_TRACE_POINTS
  #include "trace.h"
@@ -322,6 +323,7 @@ enum nvme_disposition {
  	COMPLETE,
  	RETRY,
  	FAILOVER,
+	AUTHENTICATE,
  };
 
  static inline enum nvme_disposition nvme_decide_disposition(struct request *req)
@@ -329,6 +331,9 @@ static inline enum nvme_disposition nvme_decide_disposition(struct request *req)
  	if (likely(nvme_req(req)->status == 0))
  		return COMPLETE;
 
+	if ((nvme_req(req)->status & 0x7ff) == NVME_SC_AUTH_REQUIRED)
+		return AUTHENTICATE;
+
  	if (blk_noretry_request(req) ||
  	    (nvme_req(req)->status & NVME_SC_DNR) ||
  	    nvme_req(req)->retries >= nvme_max_retries)
@@ -361,11 +366,13 @@ static inline void nvme_end_req(struct request *req)
 
  void nvme_complete_rq(struct request *req)
  {
+	struct nvme_ctrl *ctrl = nvme_req(req)->ctrl;
+
  	trace_nvme_complete_rq(req);
  	nvme_cleanup_cmd(req);
 
-	if (nvme_req(req)->ctrl->kas)
-		nvme_req(req)->ctrl->comp_seen = true;
+	if (ctrl->kas)
+		ctrl->comp_seen = true;
 
  	switch (nvme_decide_disposition(req)) {
  	case COMPLETE:

drivers/nvme/host/core.c void nvme_complete_rq(struct request *req) · line 390

Quoted 7 lines
drivers/nvme/host/core.c +4 -0
@@ -377,6 +384,15 @@ void nvme_complete_rq(struct request *req)
  	case FAILOVER:
  		nvme_failover_req(req);
  		return;
+	case AUTHENTICATE:
+#ifdef CONFIG_NVME_AUTH
+		if (nvme_change_ctrl_state(ctrl, NVME_CTRL_RESETTING))
+			queue_work(nvme_wq, &ctrl->dhchap_auth_work);

Why is the state change here and not in nvme_dhchap_auth_work?

Quoted 9 lines
drivers/nvme/host/core.c +5 -0
+		nvme_retry_req(req);
+#else
+		nvme_end_req(req);
+#endif
+		return;
  	}
  }
  EXPORT_SYMBOL_GPL(nvme_complete_rq);

drivers/nvme/host/core.c bool __nvme_check_ready(struct nvme_ctrl *ctrl, struct request *rq, · line 728

Quoted 7 lines
drivers/nvme/host/core.c +3 -1
@@ -707,7 +723,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))

What happens if the auth command comes before the connect (say in case of ctrl reset when auth was already queued but not yet executed?

Quoted 7 lines
drivers/nvme/host/core.c +0 -0
  				return true;
  			break;
  		default:
@@ -3458,6 +3476,51 @@ 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);
 

drivers/nvme/host/core.c static ssize_t nvme_ctrl_fast_io_fail_tmo_store(struct device *dev, · line 3488

Quoted 10 lines
drivers/nvme/host/core.c +10 -0
+#ifdef CONFIG_NVME_AUTH
+static ssize_t nvme_ctrl_dhchap_secret_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+	struct nvmf_ctrl_options *opts = ctrl->opts;
+
+	if (!opts->dhchap_secret)
+		return sysfs_emit(buf, "none\n");
+	return sysfs_emit(buf, "%s\n", opts->dhchap_secret);

Should we actually show this? don't know enough how much the secret should be kept a secret...

Quoted 17 lines
drivers/nvme/host/core.c +17 -0
+}
+
+static ssize_t nvme_ctrl_dhchap_secret_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+	struct nvmf_ctrl_options *opts = ctrl->opts;
+	char *dhchap_secret;
+
+	if (!ctrl->opts->dhchap_secret)
+		return -EINVAL;
+	if (count < 7)
+		return -EINVAL;
+	if (memcmp(buf, "DHHC-1:", 7))
+		return -EINVAL;
+
+	dhchap_secret = kzalloc(count + 1, GFP_KERNEL);

drivers/nvme/host/core.c static ssize_t nvme_ctrl_fast_io_fail_tmo_store(struct device *dev, · line 3515

Quoted 10 lines
drivers/nvme/host/core.c +10 -0
+	if (!dhchap_secret)
+		return -ENOMEM;
+	memcpy(dhchap_secret, buf, count);
+	if (strcmp(dhchap_secret, opts->dhchap_secret)) {
+		kfree(opts->dhchap_secret);
+		opts->dhchap_secret = dhchap_secret;
+		/* Key has changed; reset authentication data */
+		nvme_auth_free(ctrl);
+		nvme_auth_generate_key(ctrl);
+	}

Nice, worth a comment "/* Re-authentication with new secret */"

Quoted 82 lines
drivers/nvme/host/core.c +18 -0
+	if (nvme_change_ctrl_state(ctrl, NVME_CTRL_RESETTING))
+		queue_work(nvme_wq, &ctrl->dhchap_auth_work);
+	return count;
+}
+DEVICE_ATTR(dhchap_secret, S_IRUGO | S_IWUSR,
+	nvme_ctrl_dhchap_secret_show, nvme_ctrl_dhchap_secret_store);
+#endif
+
  static struct attribute *nvme_dev_attrs[] = {
  	&dev_attr_reset_controller.attr,
  	&dev_attr_rescan_controller.attr,
@@ -3479,6 +3542,9 @@ 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_secret.attr,
+#endif
  	NULL
  };
 
@@ -3502,6 +3568,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_secret.attr && !ctrl->opts)
+		return 0;
+#endif
 
  	return a->mode;
  }
@@ -4312,6 +4382,7 @@ EXPORT_SYMBOL_GPL(nvme_complete_async_event);
  void nvme_stop_ctrl(struct nvme_ctrl *ctrl)
  {
  	nvme_mpath_stop(ctrl);
+	nvme_auth_stop(ctrl);
  	nvme_stop_keep_alive(ctrl);
  	nvme_stop_failfast_work(ctrl);
  	flush_work(&ctrl->async_event_work);
@@ -4366,6 +4437,7 @@ static void nvme_free_ctrl(struct device *dev)
 
  	nvme_free_cels(ctrl);
  	nvme_mpath_uninit(ctrl);
+	nvme_auth_free(ctrl);
  	__free_page(ctrl->discard_page);
 
  	if (subsys) {
@@ -4456,6 +4528,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
 
  	nvme_fault_inject_init(&ctrl->fault_inject, dev_name(ctrl->device));
  	nvme_mpath_init_ctrl(ctrl);
+	nvme_auth_init_ctrl(ctrl);
 
  	return 0;
  out_free_name:
drivers/nvme/host/fabrics.c +9 -2
index 9a8eade7cd23..ee6058c24743 100644
@@ -370,6 +370,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;
@@ -402,8 +403,25 @@ 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: failed to setup authentication\n");

drivers/nvme/host/fabrics.c int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) · line 423

Quoted 10 lines
drivers/nvme/host/fabrics.c +10 -0
+			ret = NVME_SC_AUTH_REQUIRED;
+			goto out_free_data;
+		}
+		ret = nvme_auth_wait(ctrl, NVME_QID_ANY);
+		if (ret)
+			dev_warn(ctrl->device,
+				 "qid 0: authentication failed\n");
+		else
+			dev_info(ctrl->device,
+				 "qid 0: authenticated\n");

OK, so the auth work is serialized via nvme_auth_wait here... got it..

Quoted 225 lines
drivers/nvme/host/fabrics.c +51 -1
+	}
  out_free_data:
  	kfree(data);
  	return ret;
@@ -436,6 +454,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;
@@ -461,6 +480,24 @@ 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 %d: failed to setup authentication\n", qid);
+			ret = NVME_SC_AUTH_REQUIRED;
+		} else {
+			ret = nvme_auth_wait(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;
  }
@@ -552,6 +589,8 @@ 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_BIDI,		"dhchap_bidi"		},
  	{ NVMF_OPT_ERR,			NULL			}
  };
 
@@ -827,6 +866,23 @@ 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 (strlen(p) < 11 || strncmp(p, "DHHC-1:", 7)) {
+				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_BIDI:
+			opts->dhchap_bidi = true;
+			break;
  		default:
  			pr_warn("unknown parameter or missing value '%s' in ctrl creation request\n",
  				p);
@@ -945,6 +1001,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);
@@ -954,7 +1011,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_BIDI)
 
  static struct nvme_ctrl *
  nvmf_create_ctrl(struct device *dev, const char *buf)
@@ -1171,7 +1231,14 @@ static void __exit nvmf_exit(void)
  	BUILD_BUG_ON(sizeof(struct nvmf_connect_command) != 64);
  	BUILD_BUG_ON(sizeof(struct nvmf_property_get_command) != 64);
  	BUILD_BUG_ON(sizeof(struct nvmf_property_set_command) != 64);
+	BUILD_BUG_ON(sizeof(struct nvmf_auth_send_command) != 64);
+	BUILD_BUG_ON(sizeof(struct nvmf_auth_receive_command) != 64);
  	BUILD_BUG_ON(sizeof(struct nvmf_connect_data) != 1024);
+	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);
  }
 
  MODULE_LICENSE("GPL v2");
drivers/nvme/host/fabrics.h +6 -0
index a146cb903869..27df1aac5736 100644
@@ -67,6 +67,8 @@ 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_BIDI	= 1 << 23,
  };
 
  /**
@@ -96,6 +98,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_bidi: enable DH-HMAC-CHAP bi-directional authentication
   * @disable_sqflow: disable controller sq flow control
   * @hdr_digest: generate/verify header digest (TCP)
   * @data_digest: generate/verify data digest (TCP)
@@ -120,6 +124,8 @@ struct nvmf_ctrl_options {
  	unsigned int		kato;
  	struct nvmf_host	*host;
  	int			max_reconnects;
+	char			*dhchap_secret;
+	bool			dhchap_bidi;
  	bool			disable_sqflow;
  	bool			hdr_digest;
  	bool			data_digest;
drivers/nvme/host/nvme.h +30 -0
index 9871c0c9374c..b0dcb7d79b9e 100644
@@ -318,6 +318,15 @@ struct nvme_ctrl {
  	struct work_struct ana_work;
  #endif
 
+#ifdef CONFIG_NVME_AUTH
+	struct work_struct dhchap_auth_work;
+	struct list_head dhchap_auth_list;
+	struct mutex dhchap_auth_mutex;
+	unsigned char *dhchap_key;
+	size_t dhchap_key_len;
+	u16 transaction;
+#endif
+
  	/* Power saving configuration */
  	u64 ps_max_latency_us;
  	bool apst_enabled;
@@ -885,6 +894,27 @@ static inline bool nvme_ctrl_sgl_supported(struct nvme_ctrl *ctrl)
  	return ctrl->sgls & ((1 << 0) | (1 << 1));
  }
 
+#ifdef CONFIG_NVME_AUTH
+void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl);
+void nvme_auth_stop(struct nvme_ctrl *ctrl);
+int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid);
+int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid);
+void nvme_auth_free(struct nvme_ctrl *ctrl);
+int nvme_auth_generate_key(struct nvme_ctrl *ctrl);
+#else
+static inline void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl) {};
+static inline void nvme_auth_stop(struct nvme_ctrl *ctrl) {};
+static inline int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
+{
+	return -EPROTONOSUPPORT;
+}
+static inline int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid)
+{
+	return NVME_SC_AUTH_REQUIRED;
+}
+static inline void nvme_auth_free(struct nvme_ctrl *ctrl) {};
+#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 +28 -0
index 2a89c5aa0790..1c36fcedea20 100644
@@ -287,6 +287,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 trace_seq *p, · line 343

Quoted 10 lines
drivers/nvme/host/trace.c +4 -0
@@ -306,6 +334,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);
  	}

_______________________________________________
Linux-nvme mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-nvme

author maintainer
#18 lore raw

Re: [PATCH 07/12] nvme: Implement In-Band authentication

Quoted 96 lines · Sagi Grimberg
Quoted 31 lines · Hannes Reinecke

Implement NVMe-oF In-Band authentication according to NVMe TPAR 8006. This patch adds two new fabric options 'dhchap_secret' to specify the pre-shared key (in ASCII respresentation according to NVMe 2.0 section 8.13.5.8 'Secret representation') and 'dhchap_bidi' to request bi-directional
authentication of both the host and the controller. Re-authentication can be triggered by writing the PSK into the new controller sysfs attribute 'dhchap_secret'.

---
  drivers/nvme/host/Kconfig   |   12 +
  drivers/nvme/host/Makefile  |    1 +

  drivers/nvme/host/auth.c    | 1285 +++++++++++++++++++++++++++++++++++
  drivers/nvme/host/auth.h    |   25 +
  drivers/nvme/host/core.c    |   79 ++-

  drivers/nvme/host/fabrics.c |   73 +-
  drivers/nvme/host/fabrics.h |    6 +
  drivers/nvme/host/nvme.h    |   30 +
  drivers/nvme/host/trace.c   |   32 +

  9 files changed, 1537 insertions(+), 6 deletions(-)

  create mode 100644 drivers/nvme/host/auth.c
  create mode 100644 drivers/nvme/host/auth.h

drivers/nvme/host/Kconfig +0 -0
index dc0450ca23a3..97e8412dc42d 100644
@@ -83,3 +83,15 @@ config NVME_TCP

        from https://github.com/linux-nvme/nvme-cli.
          If unsure, say N.

drivers/nvme/host/Kconfig config NVME_TCP · line 95

Quoted 10 lines
drivers/nvme/host/Kconfig +10 -0
+
+config NVME_AUTH
+    bool "NVM Express over Fabrics In-Band Authentication"
+    depends on NVME_CORE
+    select CRYPTO_HMAC
+    select CRYPTO_SHA256
+    select CRYPTO_SHA512
+    help
+      This provides support for NVMe over Fabrics In-Band Authentication
+      for the NVMe over TCP transport.

Not tcp specific...

Quoted 49 lines
drivers/nvme/host/auth.c new file +43 -0
new file mode 100644
index 000000000000..5393ac16a002
@@ -0,0 +1,1285 @@
+// 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/dh.h>
+#include <crypto/ffdhe.h>
+#include "nvme.h"
+#include "fabrics.h"
+#include "auth.h"
+
+static u32 nvme_dhchap_seqnum;
+
+struct nvme_dhchap_queue_context {
+    struct list_head entry;
+    struct work_struct auth_work;
+    struct nvme_ctrl *ctrl;
+    struct crypto_shash *shash_tfm;
+    struct crypto_kpp *dh_tfm;
+    void *buf;
+    size_t buf_size;
+    int qid;
+    int error;
+    u32 s1;
+    u32 s2;
+    u16 transaction;
+    u8 status;
+    u8 hash_id;
+    u8 hash_len;
+    u8 dhgroup_id;
+    u8 c1[64];
+    u8 c2[64];
+    u8 response[64];
+    u8 *host_response;
+};
+
+static struct nvme_auth_dhgroup_map {
+    int id;
+    const char name[16];
Quoted 9 lines

drivers/nvme/host/auth.c line 49

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+    const char kpp[16];
+    int privkey_size;
+    int pubkey_size;
+} dhgroup_map[] = {
+    { .id = NVME_AUTH_DHCHAP_DHGROUP_NULL,
+      .name = "NULL", .kpp = "NULL",

Nit, no need for all-caps, can do "null"

Right. Will be doing so.

[ .. ]

Quoted 12 lines

drivers/nvme/host/auth.c line 186

Quoted 9 lines
drivers/nvme/host/auth.c new file +1 -0
+unsigned char *nvme_auth_extract_secret(unsigned char *secret, size_t

*out_len)

drivers/nvme/host/auth.c new file +7 -0
+{
+    unsigned char *key;
+    u32 crc;
+    int key_len;
+    size_t allocated_len;
+
+    allocated_len = strlen(secret);

Can move to declaration initializer.

Sure.

Quoted 90 lines
Quoted 40 lines

+    key = kzalloc(allocated_len, GFP_KERNEL);
+    if (!key)
+        return ERR_PTR(-ENOMEM);

drivers/nvme/host/auth.c new file +30 -0
+
+    key_len = base64_decode(secret, allocated_len, key);
+    if (key_len != 36 && key_len != 52 &&
+        key_len != 68) {
+        pr_debug("Invalid DH-HMAC-CHAP key len %d\n",
+             key_len);
+        kfree_sensitive(key);
+        return ERR_PTR(-EINVAL);
+    }
+
+    /* 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, key, key_len);
+
+    if (get_unaligned_le32(key + key_len) != crc) {
+        pr_debug("DH-HMAC-CHAP key crc mismatch (key %08x, crc %08x)\n",
+               get_unaligned_le32(key + key_len), crc);
+        kfree_sensitive(key);
+        return ERR_PTR(-EKEYREJECTED);
+    }
+    *out_len = key_len;
+    return key;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_extract_secret);
+
+u8 *nvme_auth_transform_key(u8 *key, size_t key_len, u8 key_hash,

char *nqn)

drivers/nvme/host/auth.c new file +6 -0
+{
+    const char *hmac_name = nvme_auth_hmac_name(key_hash);
+    struct crypto_shash *key_tfm;
+    struct shash_desc *shash;
+    u8 *transformed_key;
+    int ret;

drivers/nvme/host/auth.c line 235

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+
+    /* No key transformation required */
+    if (key_hash == 0)
+        return 0;
+
+    hmac_name = nvme_auth_hmac_name(key_hash);
+    if (!hmac_name) {
+        pr_warn("Invalid key hash id %d\n", key_hash);
+        return ERR_PTR(-EKEYREJECTED);
+    }

newline here.

Quoted 11 lines

+    key_tfm = crypto_alloc_shash(hmac_name, 0, 0); +    if (IS_ERR(key_tfm))
+        return (u8 *)key_tfm;
+
+    shash = kmalloc(sizeof(struct shash_desc) + +            crypto_shash_descsize(key_tfm), +            GFP_KERNEL);
+    if (!shash) {
+        crypto_free_shash(key_tfm);
+        return ERR_PTR(-ENOMEM);
+    }

newline here.

Quoted 23 lines

+    transformed_key = kzalloc(crypto_shash_digestsize(key_tfm), GFP_KERNEL);
+    if (!transformed_key) {
+        ret = -ENOMEM;
+        goto out_free_shash;
+    }

drivers/nvme/host/auth.c new file +17 -0
+
+    shash->tfm = key_tfm;
+    ret = crypto_shash_setkey(key_tfm, key, key_len);
+    if (ret < 0)
+        goto out_free_shash;
+    ret = crypto_shash_init(shash);
+    if (ret < 0)
+        goto out_free_shash;
+    ret = crypto_shash_update(shash, nqn, strlen(nqn));
+    if (ret < 0)
+        goto out_free_shash;
+    ret = crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
+    if (ret < 0)
+        goto out_free_shash;
+    ret = crypto_shash_final(shash, transformed_key);
+out_free_shash:
+    kfree(shash);
Quoted 9 lines
Quoted 5 lines

+    crypto_free_shash(key_tfm);
+    if (ret < 0) {
+        kfree_sensitive(transformed_key);
+        return ERR_PTR(ret);
+    }

Any reason why this is not a reverse cleanup with goto call-sites standard style?

None in particular.
Will be doing so.

Quoted 158 lines
Quoted 45 lines

+    return transformed_key;

drivers/nvme/host/auth.c new file +4 -0
+}
+EXPORT_SYMBOL_GPL(nvme_auth_transform_key);
+
+static int nvme_auth_hash_skey(int hmac_id, u8 *skey, size_t

skey_len, u8 *hkey)

drivers/nvme/host/auth.c new file +39 -0
+{
+    const char *digest_name;
+    struct crypto_shash *tfm;
+    int ret;
+
+    digest_name = nvme_auth_digest_name(hmac_id);
+    if (!digest_name) {
+        pr_debug("%s: failed to get digest for %d\n", __func__,
+             hmac_id);
+        return -EINVAL;
+    }
+    tfm = crypto_alloc_shash(digest_name, 0, 0);
+    if (IS_ERR(tfm))
+        return -ENOMEM;
+
+    ret = crypto_shash_tfm_digest(tfm, skey, skey_len, hkey);
+    if (ret < 0)
+        pr_debug("%s: Failed to hash digest len %zu\n", __func__,
+             skey_len);
+
+    crypto_free_shash(tfm);
+    return ret;
+}
+
+int nvme_auth_augmented_challenge(u8 hmac_id, u8 *skey, size_t skey_len,
+        u8 *challenge, u8 *aug, size_t hlen)
+{
+    struct crypto_shash *tfm;
+    struct shash_desc *desc;
+    u8 *hashed_key;
+    const char *hmac_name;
+    int ret;
+
+    hashed_key = kmalloc(hlen, GFP_KERNEL);
+    if (!hashed_key)
+        return -ENOMEM;
+
+    ret = nvme_auth_hash_skey(hmac_id, skey,
+                  skey_len, hashed_key);

drivers/nvme/host/auth.c line 327

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+    if (ret < 0)
+        goto out_free_key;
+
+    hmac_name = nvme_auth_hmac_name(hmac_id);
+    if (!hmac_name) {
+        pr_warn("%s: invalid hash algoritm %d\n",
+            __func__, hmac_id);
+        ret = -EINVAL;
+        goto out_free_key;
+    }

newline.

Quoted 5 lines

+    tfm = crypto_alloc_shash(hmac_name, 0, 0);
+    if (IS_ERR(tfm)) {
+        ret = PTR_ERR(tfm);
+        goto out_free_key;
+    }

newline

Quoted 92 lines

+    desc = kmalloc(sizeof(struct shash_desc) +
crypto_shash_descsize(tfm),
+               GFP_KERNEL);
+    if (!desc) {
+        ret = -ENOMEM;
+        goto out_free_hash;
+    }
+    desc->tfm = tfm;

drivers/nvme/host/auth.c new file +84 -0
+
+    ret = crypto_shash_setkey(tfm, hashed_key, hlen);
+    if (ret)
+        goto out_free_desc;
+
+    ret = crypto_shash_init(desc);
+    if (ret)
+        goto out_free_desc;
+
+    ret = crypto_shash_update(desc, challenge, hlen);
+    if (ret)
+        goto out_free_desc;
+
+    ret = crypto_shash_final(desc, aug);
+out_free_desc:
+    kfree_sensitive(desc);
+out_free_hash:
+    crypto_free_shash(tfm);
+out_free_key:
+    kfree_sensitive(hashed_key);
+    return ret;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_augmented_challenge);
+
+int nvme_auth_gen_privkey(struct crypto_kpp *dh_tfm, int dh_gid)
+{
+    char *pkey;
+    int ret, pkey_len;
+
+    if (dh_gid == NVME_AUTH_DHCHAP_DHGROUP_2048 ||
+        dh_gid == NVME_AUTH_DHCHAP_DHGROUP_3072 ||
+        dh_gid == NVME_AUTH_DHCHAP_DHGROUP_4096 ||
+        dh_gid == NVME_AUTH_DHCHAP_DHGROUP_6144 ||
+        dh_gid == NVME_AUTH_DHCHAP_DHGROUP_8192) {
+        struct dh p = {0};
+        int bits = nvme_auth_dhgroup_pubkey_size(dh_gid) << 3;
+        int dh_secret_len = 64;
+        u8 *dh_secret = kzalloc(dh_secret_len, GFP_KERNEL);
+
+        if (!dh_secret)
+            return -ENOMEM;
+
+        /*
+         * NVMe base spec v2.0: The DH value shall be set to the value
+         * of g^x mod p, where 'x' is a random number selected by the
+         * host that shall be at least 256 bits long.
+         *
+         * We will be using a 512 bit random number as private key.
+         * This is large enough to provide adequate security, but
+         * small enough such that we can trivially conform to
+         * NIST SB800-56A section 5.6.1.1.4 if
+         * we guarantee that the random number is not either
+         * all 0xff or all 0x00. But that should be guaranteed
+         * by the in-kernel RNG anyway.
+         */
+        get_random_bytes(dh_secret, dh_secret_len);
+
+        ret = crypto_ffdhe_params(&p, bits);
+        if (ret) {
+            kfree_sensitive(dh_secret);
+            return ret;
+        }
+
+        p.key = dh_secret;
+        p.key_size = dh_secret_len;
+
+        pkey_len = crypto_dh_key_len(&p);
+        pkey = kmalloc(pkey_len, GFP_KERNEL);
+        if (!pkey) {
+            kfree_sensitive(dh_secret);
+            return -ENOMEM;
+        }
+
+        get_random_bytes(pkey, pkey_len);
+        ret = crypto_dh_encode_key(pkey, pkey_len, &p);
+        if (ret) {
+            pr_debug("failed to encode private key, error %d\n",
+                 ret);
+            kfree_sensitive(dh_secret);
+            goto out;
+        }
+    } else {
+        pr_warn("invalid dh group %d\n", dh_gid);
+        return -EINVAL;
Quoted 9 lines
Quoted 6 lines

+    }
+    ret = crypto_kpp_set_secret(dh_tfm, pkey, pkey_len); +    if (ret)
+        pr_debug("failed to set private key, error %d\n", ret); +out:
+    kfree_sensitive(pkey);

pkey can be unset here.

Okay.

Quoted 106 lines
Quoted 15 lines

+    return ret;

drivers/nvme/host/auth.c new file +14 -0
+}
+EXPORT_SYMBOL_GPL(nvme_auth_gen_privkey);
+
+int nvme_auth_gen_pubkey(struct crypto_kpp *dh_tfm,
+        u8 *host_key, size_t host_key_len)
+{
+    struct kpp_request *req;
+    struct crypto_wait wait;
+    struct scatterlist dst;
+    int ret;
+
+    req = kpp_request_alloc(dh_tfm, GFP_KERNEL);
+    if (!req)
+        return -ENOMEM;

drivers/nvme/host/auth.c line 454

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+
+    crypto_init_wait(&wait);
+    kpp_request_set_input(req, NULL, 0);
+    sg_init_one(&dst, host_key, host_key_len);
+    kpp_request_set_output(req, &dst, 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);
+

no need for this newline

Quoted 78 lines

+    kpp_request_free(req);
+    return ret;

drivers/nvme/host/auth.c new file +76 -0
+}
+EXPORT_SYMBOL_GPL(nvme_auth_gen_pubkey);
+
+int nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm,
+        u8 *ctrl_key, size_t ctrl_key_len,
+        u8 *sess_key, size_t sess_key_len)
+{
+    struct kpp_request *req;
+    struct crypto_wait wait;
+    struct scatterlist src, dst;
+    int ret;
+
+    req = kpp_request_alloc(dh_tfm, GFP_KERNEL);
+    if (!req)
+        return -ENOMEM;
+
+    crypto_init_wait(&wait);
+    sg_init_one(&src, ctrl_key, ctrl_key_len);
+    kpp_request_set_input(req, &src, ctrl_key_len);
+    sg_init_one(&dst, sess_key, sess_key_len);
+    kpp_request_set_output(req, &dst, 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);
+
+    kpp_request_free(req);
+    return ret;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_gen_shared_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 > 0)
+        dev_dbg(ctrl->device,
+            "%s: qid %d nvme status %d\n", __func__, qid, ret);
+    else if (ret < 0)
+        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)
+{
+    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_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;
+
Quoted 9 lines
Quoted 6 lines

+    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;

Why EIO?

See next comment.

Quoted 10 lines
Quoted 6 lines

+    }
+    if (ret < 0) {
+        dev_dbg(ctrl->device, "%s: qid %d error %d\n", +            __func__, qid, ret);
+        return ret;
+    }

Why did you choose to do these error conditionals differently for the send and receive functions?

Because we have _three_ kinds of errors here: error codes, NVMe status, and authentication status.
And of course the authentication status is _not_ an NVMe status, so we can't easily overload both into a single value.
As the authentication status will be set from the received data I chose to fold all NVMe status onto -EIO, leaving the positive value free for authentication status.

Quoted 33 lines
Quoted 33 lines
drivers/nvme/host/auth.c new file +33 -0
+
+    return 0;
+}
+
+static int nvme_auth_receive_validate(struct nvme_ctrl *ctrl, int qid,
+        struct nvmf_auth_dhchap_failure_data *data,
+        u16 transaction, u8 expected_msg)
+{
+    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->rescode_exp;
+    }
+    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_INCORRECT_MESSAGE;
+    }
+    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_INCORRECT_MESSAGE;
+    }
+    return 0;
+}
+
+static int nvme_auth_set_dhchap_negotiate_data(struct nvme_ctrl *ctrl,
+        struct nvme_dhchap_queue_context *chap)
Quoted 9 lines

drivers/nvme/host/auth.c line 583

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+{
+    struct nvmf_auth_dhchap_negotiate_data *data = chap->buf;
+    size_t size = sizeof(*data) + sizeof(union nvmf_auth_protocol);
+
+    if (chap->buf_size < size) {
+        chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;

Is this an internal error? not sure I understand setting of this status

As mentioned above, we now have three possible status codes to content with. So yes, this is an internal error, expressed as authentication error code.

The spec insists on using an authentication error here; it would be possible to use a normal NVMe status, but that's not what the spec wants ...

Quoted 92 lines
Quoted 48 lines

+        return -EINVAL;
+    }
+    memset((u8 *)chap->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 = 6;
+    data->auth_protocol[0].dhchap.idlist[0] = NVME_AUTH_DHCHAP_SHA256; +    data->auth_protocol[0].dhchap.idlist[1] = NVME_AUTH_DHCHAP_SHA384; +    data->auth_protocol[0].dhchap.idlist[2] = NVME_AUTH_DHCHAP_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;

drivers/nvme/host/auth.c new file +22 -0
+
+    return size;
+}
+
+static int nvme_auth_process_dhchap_challenge(struct nvme_ctrl *ctrl,
+        struct nvme_dhchap_queue_context *chap)
+{
+    struct nvmf_auth_dhchap_challenge_data *data = chap->buf;
+    size_t size = sizeof(*data) + data->hl + data->dhvlen;
+    const char *hmac_name;
+
+    if (chap->buf_size < size) {
+        chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+        return NVME_SC_INVALID_FIELD;
+    }
+
+    hmac_name = nvme_auth_hmac_name(data->hashid);
+    if (!hmac_name) {
+        dev_warn(ctrl->device,
+             "qid %d: invalid HASH ID %d\n",
+             chap->qid, data->hashid);
+        chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;

drivers/nvme/host/auth.c line 635

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+        return -EPROTO;
+    }
+    if (chap->hash_id == data->hashid && chap->shash_tfm &&
+        !strcmp(crypto_shash_alg_name(chap->shash_tfm), hmac_name) &&
+        crypto_shash_digestsize(chap->shash_tfm) == data->hl) {
+        dev_dbg(ctrl->device,
+            "qid %d: reuse existing hash %s\n",
+            chap->qid, hmac_name);
+        goto select_kpp;
+    }

newline

Quoted 5 lines

+    if (chap->shash_tfm) {
+        crypto_free_shash(chap->shash_tfm);
+        chap->hash_id = 0;
+        chap->hash_len = 0;
+    }

newline

Quoted 10 lines

+    chap->shash_tfm = crypto_alloc_shash(hmac_name, 0, +                         CRYPTO_ALG_ALLOCATES_MEMORY); +    if (IS_ERR(chap->shash_tfm)) {
+        dev_warn(ctrl->device,
+             "qid %d: failed to allocate hash %s, error %ld\n", +             chap->qid, hmac_name, PTR_ERR(chap->shash_tfm)); +        chap->shash_tfm = NULL;
+        chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED; +        return NVME_SC_AUTH_REQUIRED;
+    }

newline

Quoted 9 lines

+    if (crypto_shash_digestsize(chap->shash_tfm) != data->hl) { +        dev_warn(ctrl->device,
+             "qid %d: invalid hash length %d\n",
+             chap->qid, data->hl);
+        crypto_free_shash(chap->shash_tfm);
+        chap->shash_tfm = NULL;
+        chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; +        return NVME_SC_AUTH_REQUIRED;
+    }

Quoted 7 lines

newline

Quoted 2 lines

+    if (chap->hash_id != data->hashid) {
+        kfree(chap->host_response);

kfree_sensitive? also why is is freed here? where was it allocated?

This is generated when calculating the host response in nvme_auth_dhchap_host_response().

Quoted 15 lines
Quoted 6 lines

+        chap->host_response = NULL;
+    }
+    chap->hash_id = data->hashid;
+    chap->hash_len = data->hl;
+    dev_dbg(ctrl->device, "qid %d: selected hash %s\n", +        chap->qid, hmac_name);

Quoted 8 lines

+
+    gid_name = nvme_auth_dhgroup_kpp(data->dhgid);
+    if (!gid_name) {
+        dev_warn(ctrl->device,
+             "qid %d: invalid DH group id %d\n",
+             chap->qid, data->dhgid);
+        chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; +        return -EPROTO;

Quoted 4 lines

No need for all the previous frees?
Maybe we can rework these such that we first do all the checks and then go and allocate stuff?

Hmm. Will have a look if that is feasible.

Quoted 43 lines
Quoted 43 lines

+    }

drivers/nvme/host/auth.c new file +42 -0
+
+    if (data->dhgid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
+        if (data->dhvlen == 0) {
+            dev_warn(ctrl->device,
+                 "qid %d: 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)) {
+            int ret = PTR_ERR(chap->dh_tfm);
+
+            dev_warn(ctrl->device,
+                 "qid %d: failed to initialize %s\n",
+                 chap->qid, gid_name);
+            chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
+            chap->dh_tfm = NULL;
+            return ret;
+        }
+        chap->dhgroup_id = data->dhgid;
+    } else if (data->dhvlen != 0) {
+        dev_warn(ctrl->device,
+             "qid %d: invalid DH value for NULL DH\n",
+            chap->qid);
+        chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
+        return -EPROTO;
+    }
+    dev_dbg(ctrl->device, "qid %d: selected DH group %s\n",
+        chap->qid, gid_name);
+
+select_kpp:
+    chap->s1 = le32_to_cpu(data->seqnum);
+    memcpy(chap->c1, data->cval, chap->hash_len);
+
+    return 0;
+}
+
+static int nvme_auth_set_dhchap_reply_data(struct nvme_ctrl *ctrl,
+        struct nvme_dhchap_queue_context *chap)
+{
+    struct nvmf_auth_dhchap_reply_data *data = chap->buf;
Quoted 9 lines
Quoted 6 lines

+    size_t size = sizeof(*data);
+
+    size += 2 * chap->hash_len;
+    if (ctrl->opts->dhchap_bidi) {
+        get_random_bytes(chap->c2, chap->hash_len); +        chap->s2 = nvme_dhchap_seqnum++;

Any serialization needed on nvme_dhchap_seqnum?

Maybe; will be switching to atomic here.
Have been lazy ...

Quoted 12 lines
Quoted 12 lines

+    } else
+        memset(chap->c2, 0, chap->hash_len);

drivers/nvme/host/auth.c new file +10 -0
+
+
+    if (chap->buf_size < size) {
+        chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+        return -EINVAL;
+    }
+    memset(chap->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);
Quoted 9 lines
Quoted 5 lines

+    data->hl = chap->hash_len;
+    data->dhvlen = 0;
+    data->seqnum = cpu_to_le32(chap->s2);
+    memcpy(data->rval, chap->response, chap->hash_len); +    if (ctrl->opts->dhchap_bidi) {

Can we unite the "if (ctrl->opts->dhchap_bidi)"
conditionals?

Sure.

[ .. ]

Quoted 36 lines
Quoted 36 lines
drivers/nvme/host/auth.c new file +36 -0
+int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
+{
+    struct nvme_dhchap_queue_context *chap;
+
+    if (!ctrl->dhchap_key || !ctrl->dhchap_key_len) {
+        dev_warn(ctrl->device, "qid %d: no key\n", qid);
+        return -ENOKEY;
+    }
+
+    mutex_lock(&ctrl->dhchap_auth_mutex);
+    /* Check if the context is already queued */
+    list_for_each_entry(chap, &ctrl->dhchap_auth_list, entry) {
+        if (chap->qid == qid) {
+            mutex_unlock(&ctrl->dhchap_auth_mutex);
+            queue_work(nvme_wq, &chap->auth_work);
+            return 0;
+        }
+    }
+    chap = kzalloc(sizeof(*chap), GFP_KERNEL);
+    if (!chap) {
+        mutex_unlock(&ctrl->dhchap_auth_mutex);
+        return -ENOMEM;
+    }
+    chap->qid = qid;
+    chap->ctrl = ctrl;
+
+    /*
+     * Allocate a large enough buffer for the entire negotiation:
+     * 4k should be enough to ffdhe8192.
+     */
+    chap->buf_size = 4096;
+    chap->buf = kzalloc(chap->buf_size, GFP_KERNEL);
+    if (!chap->buf) {
+        mutex_unlock(&ctrl->dhchap_auth_mutex);
+        kfree(chap);
+        return -ENOMEM;
Quoted 9 lines
Quoted 6 lines

+    }
+
+    INIT_WORK(&chap->auth_work, __nvme_auth_work); +    list_add(&chap->entry, &ctrl->dhchap_auth_list); +    mutex_unlock(&ctrl->dhchap_auth_mutex); +    queue_work(nvme_wq, &chap->auth_work);

Why is the auth in a work? e.g. it won't fail the connect?

For re-authentication.
Re-authentication should _not_ fail the connection if it stops in some intermediate step, only once the the protocol ran to completion the status is updated.
Meaning that we will have additional I/O ongoing while re-authentication is in progress, so we can't stop all I/O here but rather need to shift it onto a workqueue.

Quoted 179 lines
Quoted 179 lines

+    return 0;

drivers/nvme/host/auth.c new file +91 -0
+}
+EXPORT_SYMBOL_GPL(nvme_auth_negotiate);
+
+int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid)
+{
+    struct nvme_dhchap_queue_context *chap;
+    int ret;
+
+    mutex_lock(&ctrl->dhchap_auth_mutex);
+    list_for_each_entry(chap, &ctrl->dhchap_auth_list, entry) {
+        if (chap->qid != qid)
+            continue;
+        mutex_unlock(&ctrl->dhchap_auth_mutex);
+        flush_work(&chap->auth_work);
+        ret = chap->error;
+        nvme_auth_reset(chap);
+        return ret;
+    }
+    mutex_unlock(&ctrl->dhchap_auth_mutex);
+    return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_wait);
+
+/* Assumes that the controller is in state RESETTING */
+static void nvme_dhchap_auth_work(struct work_struct *work)
+{
+    struct nvme_ctrl *ctrl =
+        container_of(work, struct nvme_ctrl, dhchap_auth_work);
+    int ret, q;
+
+    nvme_stop_queues(ctrl);
+    /* Authenticate admin queue first */
+    ret = nvme_auth_negotiate(ctrl, NVME_QID_ANY);
+    if (ret) {
+        dev_warn(ctrl->device,
+             "qid 0: error %d setting up authentication\n", ret);
+        goto out;
+    }
+    ret = nvme_auth_wait(ctrl, NVME_QID_ANY);
+    if (ret) {
+        dev_warn(ctrl->device,
+             "qid 0: authentication failed\n");
+        goto out;
+    }
+    dev_info(ctrl->device, "qid 0: authenticated\n");
+
+    for (q = 1; q < ctrl->queue_count; q++) {
+        ret = nvme_auth_negotiate(ctrl, q);
+        if (ret) {
+            dev_warn(ctrl->device,
+                 "qid %d: error %d setting up authentication\n",
+                 q, ret);
+            goto out;
+        }
+    }
+out:
+    /*
+     * Failure is a soft-state; credentials remain valid until
+     * the controller terminates the connection.
+     */
+    if (nvme_change_ctrl_state(ctrl, NVME_CTRL_LIVE))
+        nvme_start_queues(ctrl);
+}
+
+void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl)
+{
+    INIT_LIST_HEAD(&ctrl->dhchap_auth_list);
+    INIT_WORK(&ctrl->dhchap_auth_work, nvme_dhchap_auth_work);
+    mutex_init(&ctrl->dhchap_auth_mutex);
+    nvme_auth_generate_key(ctrl);
+}
+EXPORT_SYMBOL_GPL(nvme_auth_init_ctrl);
+
+void nvme_auth_stop(struct nvme_ctrl *ctrl)
+{
+    struct nvme_dhchap_queue_context *chap = NULL, *tmp;
+
+    cancel_work_sync(&ctrl->dhchap_auth_work);
+    mutex_lock(&ctrl->dhchap_auth_mutex);
+    list_for_each_entry_safe(chap, tmp, &ctrl->dhchap_auth_list, entry)
+        cancel_work_sync(&chap->auth_work);
+    mutex_unlock(&ctrl->dhchap_auth_mutex);
+}
+EXPORT_SYMBOL_GPL(nvme_auth_stop);
+
+void nvme_auth_free(struct nvme_ctrl *ctrl)
+{
+    struct nvme_dhchap_queue_context *chap = NULL, *tmp;
+
+    mutex_lock(&ctrl->dhchap_auth_mutex);
+    list_for_each_entry_safe(chap, tmp, &ctrl->dhchap_auth_list,

entry) {
+        list_del_init(&chap->entry);
+        flush_work(&chap->auth_work);
+        __nvme_auth_free(chap);
+    }
+    mutex_unlock(&ctrl->dhchap_auth_mutex);
+    kfree(ctrl->dhchap_key);
+    ctrl->dhchap_key = NULL;
+    ctrl->dhchap_key_len = 0;

drivers/nvme/host/auth.c new file +2 -0
+}
+EXPORT_SYMBOL_GPL(nvme_auth_free);
drivers/nvme/host/auth.h new file +23 -0
new file mode 100644
index 000000000000..cf1255f9db6d
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions
+ */
+
+#ifndef _NVME_AUTH_H
+#define _NVME_AUTH_H
+
+#include <crypto/kpp.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);
+
+unsigned char *nvme_auth_extract_secret(unsigned char *dhchap_secret,
+                    size_t *dhchap_key_len);
+u8 *nvme_auth_transform_key(u8 *key, size_t key_len, u8 key_hash,

char *nqn);

drivers/nvme/host/auth.h new file +2 -0
+
+#endif /* _NVME_AUTH_H */
drivers/nvme/host/core.c +2 -0
index 7efb31b87f37..f669b054790b 100644
@@ -24,6 +24,7 @@
     #include "nvme.h"
   #include "fabrics.h"
+#include "auth.h"
     #define CREATE_TRACE_POINTS
   #include "trace.h"
@@ -322,6 +323,7 @@ enum nvme_disposition {
       COMPLETE,
       RETRY,
       FAILOVER,
+    AUTHENTICATE,
   };

    static inline enum nvme_disposition nvme_decide_disposition(struct request *req)

drivers/nvme/host/core.c +2 -0
@@ -329,6 +331,9 @@ static inline enum nvme_disposition
 nvme_decide_disposition(struct request *req)
       if (likely(nvme_req(req)->status == 0))
           return COMPLETE;
   +    if ((nvme_req(req)->status & 0x7ff) == NVME_SC_AUTH_REQUIRED)
+        return AUTHENTICATE;
+

      if (blk_noretry_request(req) || (nvme_req(req)->status & NVME_SC_DNR) || nvme_req(req)->retries >= nvme_max_retries)

drivers/nvme/host/core.c +4 -1
@@ -361,11 +366,13 @@ static inline void nvme_end_req(struct request
 *req)
     void nvme_complete_rq(struct request *req)
   {
+    struct nvme_ctrl *ctrl = nvme_req(req)->ctrl;
+
       trace_nvme_complete_rq(req);
       nvme_cleanup_cmd(req);
   -    if (nvme_req(req)->ctrl->kas)
-        nvme_req(req)->ctrl->comp_seen = true;
+    if (ctrl->kas)
+        ctrl->comp_seen = true;
         switch (nvme_decide_disposition(req)) {

      case COMPLETE:

drivers/nvme/host/core.c +0 -0
@@ -377,6 +384,15 @@ void nvme_complete_rq(struct request *req)

      case FAILOVER:

Quoted 9 lines
Quoted 6 lines

          nvme_failover_req(req);
          return;
+    case AUTHENTICATE:
+#ifdef CONFIG_NVME_AUTH
+        if (nvme_change_ctrl_state(ctrl, NVME_CTRL_RESETTING)) +            queue_work(nvme_wq, &ctrl->dhchap_auth_work);

Why is the state change here and not in nvme_dhchap_auth_work?

Because switching to 'resetting' is an easy way to synchronize with the admin queue.

Quoted 16 lines
Quoted 16 lines

+        nvme_retry_req(req);

drivers/nvme/host/core.c +4 -0
+#else
+        nvme_end_req(req);
+#endif
+        return;

      }
  }
  EXPORT_SYMBOL_GPL(nvme_complete_rq);

drivers/nvme/host/core.c +0 -0
@@ -707,7 +723,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 ==

Quoted 9 lines
Quoted 5 lines

nvme_fabrics_type_connect ||
+                 req->cmd->fabrics.fctype ==
nvme_fabrics_type_auth_send ||
+                 req->cmd->fabrics.fctype ==
nvme_fabrics_type_auth_receive))

What happens if the auth command comes before the connect (say in case of ctrl reset when auth was already queued but not yet executed?

See below.

Quoted 12 lines
Quoted 12 lines

                  return true;
              break;
          default:

drivers/nvme/host/core.c +0 -0
@@ -3458,6 +3476,51 @@ 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

drivers/nvme/host/core.c +4 -0
+static ssize_t nvme_ctrl_dhchap_secret_show(struct device *dev,
+        struct device_attribute *attr, char *buf)
+{
+    struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
Quoted 9 lines
Quoted 5 lines

+    struct nvmf_ctrl_options *opts = ctrl->opts; +
+    if (!opts->dhchap_secret)
+        return sysfs_emit(buf, "none\n");
+    return sysfs_emit(buf, "%s\n", opts->dhchap_secret);

Should we actually show this? don't know enough how much the secret should be kept a secret...

I found it logical, as we need the 'store' functionality to trigger re-authentication.
But sure, we can make this a write-only attribute.

Quoted 21 lines
Quoted 21 lines
drivers/nvme/host/core.c +21 -0
+}
+
+static ssize_t nvme_ctrl_dhchap_secret_store(struct device *dev,
+        struct device_attribute *attr, const char *buf, size_t count)
+{
+    struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+    struct nvmf_ctrl_options *opts = ctrl->opts;
+    char *dhchap_secret;
+
+    if (!ctrl->opts->dhchap_secret)
+        return -EINVAL;
+    if (count < 7)
+        return -EINVAL;
+    if (memcmp(buf, "DHHC-1:", 7))
+        return -EINVAL;
+
+    dhchap_secret = kzalloc(count + 1, GFP_KERNEL);
+    if (!dhchap_secret)
+        return -ENOMEM;
+    memcpy(dhchap_secret, buf, count);
+    if (strcmp(dhchap_secret, opts->dhchap_secret)) {
Quoted 9 lines
Quoted 6 lines

+        kfree(opts->dhchap_secret);
+        opts->dhchap_secret = dhchap_secret;
+        /* Key has changed; reset authentication data */ +        nvme_auth_free(ctrl);
+        nvme_auth_generate_key(ctrl);
+    }

Nice, worth a comment "/* Re-authentication with new secret */"

Right, will do.

Thanks for the review!

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
maintainer
#19 lore raw

Re: [PATCH 07/12] nvme: Implement In-Band authentication

Quoted 25 lines
Quoted 16 lines
Quoted 16 lines
drivers/nvme/host/core.c +4 -1
@@ -361,11 +366,13 @@ static inline void nvme_end_req(struct request
 *req)
     void nvme_complete_rq(struct request *req)
   {
+    struct nvme_ctrl *ctrl = nvme_req(req)->ctrl;
+
       trace_nvme_complete_rq(req);
       nvme_cleanup_cmd(req);
   -    if (nvme_req(req)->ctrl->kas)
-        nvme_req(req)->ctrl->comp_seen = true;
+    if (ctrl->kas)
+        ctrl->comp_seen = true;
         switch (nvme_decide_disposition(req)) {
       case COMPLETE:
@@ -377,6 +384,15 @@ void nvme_complete_rq(struct request *req)
       case FAILOVER:
Quoted 9 lines

drivers/nvme/host/core.c void nvme_complete_rq(struct request *req) · line 390

Quoted 6 lines
drivers/nvme/host/core.c +4 -0
           nvme_failover_req(req);
           return;
+    case AUTHENTICATE:
+#ifdef CONFIG_NVME_AUTH
+        if (nvme_change_ctrl_state(ctrl, NVME_CTRL_RESETTING))
+            queue_work(nvme_wq, &ctrl->dhchap_auth_work);

Why is the state change here and not in nvme_dhchap_auth_work?

Quoted 2 lines

Because switching to 'resetting' is an easy way to synchronize with the admin queue.

Maybe fold this into nvme_authenticate_ctrl? in case someone adds/moves this in the future and forgets the ctrl state serialization?

author maintainer
#20 lore raw

Re: [PATCH 07/12] nvme: Implement In-Band authentication

Quoted 25 lines · Sagi Grimberg
Quoted 25 lines
Quoted 25 lines
Quoted 15 lines
drivers/nvme/host/core.c +4 -1
@@ -361,11 +366,13 @@ static inline void nvme_end_req(struct request
 *req)
      void nvme_complete_rq(struct request *req)
    {
+    struct nvme_ctrl *ctrl = nvme_req(req)->ctrl;
+
        trace_nvme_complete_rq(req);
        nvme_cleanup_cmd(req);
    -    if (nvme_req(req)->ctrl->kas)
-        nvme_req(req)->ctrl->comp_seen = true;
+    if (ctrl->kas)
+        ctrl->comp_seen = true;
          switch (nvme_decide_disposition(req)) {

       case COMPLETE:

(quoted diff) +0 -0
@@ -377,6 +384,15 @@ void nvme_complete_rq(struct request *req)
Quoted 7 lines

       case FAILOVER:
           nvme_failover_req(req);
           return;
+    case AUTHENTICATE:
+#ifdef CONFIG_NVME_AUTH
+        if (nvme_change_ctrl_state(ctrl, NVME_CTRL_RESETTING)) +            queue_work(nvme_wq, &ctrl->dhchap_auth_work);

Why is the state change here and not in nvme_dhchap_auth_work?

Quoted 5 lines
Quoted 2 lines

Because switching to 'resetting' is an easy way to synchronize with the admin queue.

Maybe fold this into nvme_authenticate_ctrl? in case someone adds/moves this in the future and forgets the ctrl state serialization?

Yeah; not a bad idea. Will be looking into it.

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
#21 lore raw

Re: [PATCH 07/12] nvme: Implement In-Band authentication

In reply to Hannes Reinecke
Quoted 236 lines · Hannes Reinecke

Implement NVMe-oF In-Band authentication according to NVMe TPAR 8006. This patch adds two new fabric options 'dhchap_secret' to specify the pre-shared key (in ASCII respresentation according to NVMe 2.0 section 8.13.5.8 'Secret representation') and 'dhchap_bidi' to request bi-directional authentication of both the host and the controller. Re-authentication can be triggered by writing the PSK into the new controller sysfs attribute 'dhchap_secret'.

---
  drivers/nvme/host/Kconfig   |   12 +
  drivers/nvme/host/Makefile  |    1 +
  drivers/nvme/host/auth.c    | 1285 +++++++++++++++++++++++++++++++++++
  drivers/nvme/host/auth.h    |   25 +
  drivers/nvme/host/core.c    |   79 ++-
  drivers/nvme/host/fabrics.c |   73 +-
  drivers/nvme/host/fabrics.h |    6 +
  drivers/nvme/host/nvme.h    |   30 +
  drivers/nvme/host/trace.c   |   32 +
  9 files changed, 1537 insertions(+), 6 deletions(-)

create mode 100644 drivers/nvme/host/auth.c
create mode 100644 drivers/nvme/host/auth.h

drivers/nvme/host/Kconfig +12 -0
index dc0450ca23a3..97e8412dc42d 100644
@@ -83,3 +83,15 @@ 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_CORE
+	select CRYPTO_HMAC
+	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
index dfaacd472e5d..4bae2a4a8d8c 100644
@@ -15,6 +15,7 @@ nvme-core-$(CONFIG_NVME_MULTIPATH)	+= multipath.o
  nvme-core-$(CONFIG_BLK_DEV_ZONED)	+= zns.o
  nvme-core-$(CONFIG_FAULT_INJECTION_DEBUG_FS)	+= fault_inject.o
  nvme-core-$(CONFIG_NVME_HWMON)		+= hwmon.o
+nvme-core-$(CONFIG_NVME_AUTH)		+= auth.o
 
  nvme-y					+= pci.o
 
drivers/nvme/host/auth.c new file +175 -0
new file mode 100644
index 000000000000..5393ac16a002
@@ -0,0 +1,1285 @@
+// 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/dh.h>
+#include <crypto/ffdhe.h>
+#include "nvme.h"
+#include "fabrics.h"
+#include "auth.h"
+
+static u32 nvme_dhchap_seqnum;
+
+struct nvme_dhchap_queue_context {
+	struct list_head entry;
+	struct work_struct auth_work;
+	struct nvme_ctrl *ctrl;
+	struct crypto_shash *shash_tfm;
+	struct crypto_kpp *dh_tfm;
+	void *buf;
+	size_t buf_size;
+	int qid;
+	int error;
+	u32 s1;
+	u32 s2;
+	u16 transaction;
+	u8 status;
+	u8 hash_id;
+	u8 hash_len;
+	u8 dhgroup_id;
+	u8 c1[64];
+	u8 c2[64];
+	u8 response[64];
+	u8 *host_response;
+};
+
+static 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);
+
+static struct nvme_dhchap_hash_map {
+	int id;
+	const char hmac[15];
+	const char digest[15];
+} hash_map[] = {
+	{.id = NVME_AUTH_DHCHAP_SHA256,
+	 .hmac = "hmac(sha256)", .digest = "sha256" },
+	{.id = NVME_AUTH_DHCHAP_SHA384,
+	 .hmac = "hmac(sha384)", .digest = "sha384" },
+	{.id = NVME_AUTH_DHCHAP_SHA512,
+	 .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_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;

drivers/nvme/host/auth.c line 185

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+}
+EXPORT_SYMBOL_GPL(nvme_auth_hmac_id);
+
+unsigned char *nvme_auth_extract_secret(unsigned char *secret, size_t *out_len)
+{
+	unsigned char *key;
+	u32 crc;
+	int key_len;
+	size_t allocated_len;
+

nit reverse tree declaration style like you have used in the next function.

Quoted 88 lines
drivers/nvme/host/auth.c new file +88 -0
+	allocated_len = strlen(secret);
+	key = kzalloc(allocated_len, GFP_KERNEL);
+	if (!key)
+		return ERR_PTR(-ENOMEM);
+
+	key_len = base64_decode(secret, allocated_len, key);
+	if (key_len != 36 && key_len != 52 &&
+	    key_len != 68) {
+		pr_debug("Invalid DH-HMAC-CHAP key len %d\n",
+			 key_len);
+		kfree_sensitive(key);
+		return ERR_PTR(-EINVAL);
+	}
+
+	/* 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, key, key_len);
+
+	if (get_unaligned_le32(key + key_len) != crc) {
+		pr_debug("DH-HMAC-CHAP key crc mismatch (key %08x, crc %08x)\n",
+		       get_unaligned_le32(key + key_len), crc);
+		kfree_sensitive(key);
+		return ERR_PTR(-EKEYREJECTED);
+	}
+	*out_len = key_len;
+	return key;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_extract_secret);
+
+u8 *nvme_auth_transform_key(u8 *key, size_t key_len, u8 key_hash, char *nqn)
+{
+	const char *hmac_name = nvme_auth_hmac_name(key_hash);
+	struct crypto_shash *key_tfm;
+	struct shash_desc *shash;
+	u8 *transformed_key;
+	int ret;
+
+	/* No key transformation required */
+	if (key_hash == 0)
+		return 0;
+
+	hmac_name = nvme_auth_hmac_name(key_hash);
+	if (!hmac_name) {
+		pr_warn("Invalid key hash id %d\n", key_hash);
+		return ERR_PTR(-EKEYREJECTED);
+	}
+	key_tfm = crypto_alloc_shash(hmac_name, 0, 0);
+	if (IS_ERR(key_tfm))
+		return (u8 *)key_tfm;
+
+	shash = kmalloc(sizeof(struct shash_desc) +
+			crypto_shash_descsize(key_tfm),
+			GFP_KERNEL);
+	if (!shash) {
+		crypto_free_shash(key_tfm);
+		return ERR_PTR(-ENOMEM);
+	}
+	transformed_key = kzalloc(crypto_shash_digestsize(key_tfm), GFP_KERNEL);
+	if (!transformed_key) {
+		ret = -ENOMEM;
+		goto out_free_shash;
+	}
+
+	shash->tfm = key_tfm;
+	ret = crypto_shash_setkey(key_tfm, key, key_len);
+	if (ret < 0)
+		goto out_free_shash;
+	ret = crypto_shash_init(shash);
+	if (ret < 0)
+		goto out_free_shash;
+	ret = crypto_shash_update(shash, nqn, strlen(nqn));
+	if (ret < 0)
+		goto out_free_shash;
+	ret = crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
+	if (ret < 0)
+		goto out_free_shash;
+	ret = crypto_shash_final(shash, transformed_key);
+out_free_shash:
+	kfree(shash);
+	crypto_free_shash(key_tfm);
+	if (ret < 0) {
+		kfree_sensitive(transformed_key);
+		return ERR_PTR(ret);
+	}

drivers/nvme/host/auth.c line 283

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	return transformed_key;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_transform_key);
+
+static int nvme_auth_hash_skey(int hmac_id, u8 *skey, size_t skey_len, u8 *hkey)
+{
+	const char *digest_name;
+	struct crypto_shash *tfm;
+	int ret;
+

same here.

Quoted 18 lines
drivers/nvme/host/auth.c new file +18 -0
+	digest_name = nvme_auth_digest_name(hmac_id);
+	if (!digest_name) {
+		pr_debug("%s: failed to get digest for %d\n", __func__,
+			 hmac_id);
+		return -EINVAL;
+	}
+	tfm = crypto_alloc_shash(digest_name, 0, 0);
+	if (IS_ERR(tfm))
+		return -ENOMEM;
+
+	ret = crypto_shash_tfm_digest(tfm, skey, skey_len, hkey);
+	if (ret < 0)
+		pr_debug("%s: Failed to hash digest len %zu\n", __func__,
+			 skey_len);
+
+	crypto_free_shash(tfm);
+	return ret;
+}

drivers/nvme/host/auth.c line 311

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+
+int nvme_auth_augmented_challenge(u8 hmac_id, u8 *skey, size_t skey_len,
+		u8 *challenge, u8 *aug, size_t hlen)
+{
+	struct crypto_shash *tfm;
+	struct shash_desc *desc;
+	u8 *hashed_key;
+	const char *hmac_name;
+	int ret;
+

nit: reverse tree.

Quoted 53 lines
drivers/nvme/host/auth.c new file +53 -0
+	hashed_key = kmalloc(hlen, GFP_KERNEL);
+	if (!hashed_key)
+		return -ENOMEM;
+
+	ret = nvme_auth_hash_skey(hmac_id, skey,
+				  skey_len, hashed_key);
+	if (ret < 0)
+		goto out_free_key;
+
+	hmac_name = nvme_auth_hmac_name(hmac_id);
+	if (!hmac_name) {
+		pr_warn("%s: invalid hash algoritm %d\n",
+			__func__, hmac_id);
+		ret = -EINVAL;
+		goto out_free_key;
+	}
+	tfm = crypto_alloc_shash(hmac_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, hlen);
+	if (ret)
+		goto out_free_desc;
+
+	ret = crypto_shash_init(desc);
+	if (ret)
+		goto out_free_desc;
+
+	ret = crypto_shash_update(desc, challenge, hlen);
+	if (ret)
+		goto out_free_desc;
+
+	ret = crypto_shash_final(desc, aug);
+out_free_desc:
+	kfree_sensitive(desc);
+out_free_hash:
+	crypto_free_shash(tfm);
+out_free_key:
+	kfree_sensitive(hashed_key);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_augmented_challenge);
+
+int nvme_auth_gen_privkey(struct crypto_kpp *dh_tfm, int dh_gid)

drivers/nvme/host/auth.c line 374

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+{
+	char *pkey;
+	int ret, pkey_len;
+
+	if (dh_gid == NVME_AUTH_DHCHAP_DHGROUP_2048 ||
+	    dh_gid == NVME_AUTH_DHCHAP_DHGROUP_3072 ||
+	    dh_gid == NVME_AUTH_DHCHAP_DHGROUP_4096 ||
+	    dh_gid == NVME_AUTH_DHCHAP_DHGROUP_6144 ||
+	    dh_gid == NVME_AUTH_DHCHAP_DHGROUP_8192) {
+		struct dh p = {0};

spaces after 0 above ?

Quoted 40 lines
drivers/nvme/host/auth.c new file +40 -0
+		int bits = nvme_auth_dhgroup_pubkey_size(dh_gid) << 3;
+		int dh_secret_len = 64;
+		u8 *dh_secret = kzalloc(dh_secret_len, GFP_KERNEL);
+
+		if (!dh_secret)
+			return -ENOMEM;
+
+		/*
+		 * NVMe base spec v2.0: The DH value shall be set to the value
+		 * of g^x mod p, where 'x' is a random number selected by the
+		 * host that shall be at least 256 bits long.
+		 *
+		 * We will be using a 512 bit random number as private key.
+		 * This is large enough to provide adequate security, but
+		 * small enough such that we can trivially conform to
+		 * NIST SB800-56A section 5.6.1.1.4 if
+		 * we guarantee that the random number is not either
+		 * all 0xff or all 0x00. But that should be guaranteed
+		 * by the in-kernel RNG anyway.
+		 */
+		get_random_bytes(dh_secret, dh_secret_len);
+
+		ret = crypto_ffdhe_params(&p, bits);
+		if (ret) {
+			kfree_sensitive(dh_secret);
+			return ret;
+		}
+
+		p.key = dh_secret;
+		p.key_size = dh_secret_len;
+
+		pkey_len = crypto_dh_key_len(&p);
+		pkey = kmalloc(pkey_len, GFP_KERNEL);
+		if (!pkey) {
+			kfree_sensitive(dh_secret);
+			return -ENOMEM;
+		}
+
+		get_random_bytes(pkey, pkey_len);
+		ret = crypto_dh_encode_key(pkey, pkey_len, &p);

drivers/nvme/host/auth.c line 424

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+		if (ret) {
+			pr_debug("failed to encode private key, error %d\n",
+				 ret);
+			kfree_sensitive(dh_secret);
+			goto out;
+		}
+	} else {
+		pr_warn("invalid dh group %d\n", dh_gid);
+		return -EINVAL;
+	}

I'd get rid of the if..else part, and return error early in the function if following condition is false :-

	if (!(dh_gid == NVME_AUTH_DHCHAP_DHGROUP_2048 ||
	    dh_gid == NVME_AUTH_DHCHAP_DHGROUP_3072 ||
	    dh_gid == NVME_AUTH_DHCHAP_DHGROUP_4096 ||
	    dh_gid == NVME_AUTH_DHCHAP_DHGROUP_6144 ||
	    dh_gid == NVME_AUTH_DHCHAP_DHGROUP_8192)) {
		pr_warn("invalid dh group %d\n", dh_gid);
		return -EINVAL;
	}

so the main code doesn't have to be in the if( ) condition..

Quoted 57 lines
drivers/nvme/host/auth.c new file +57 -0
+	ret = crypto_kpp_set_secret(dh_tfm, pkey, pkey_len);
+	if (ret)
+		pr_debug("failed to set private key, error %d\n", ret);
+out:
+	kfree_sensitive(pkey);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_gen_privkey);
+
+int nvme_auth_gen_pubkey(struct crypto_kpp *dh_tfm,
+		u8 *host_key, size_t host_key_len)
+{
+	struct kpp_request *req;
+	struct crypto_wait wait;
+	struct scatterlist dst;
+	int ret;
+
+	req = kpp_request_alloc(dh_tfm, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	crypto_init_wait(&wait);
+	kpp_request_set_input(req, NULL, 0);
+	sg_init_one(&dst, host_key, host_key_len);
+	kpp_request_set_output(req, &dst, 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);
+
+	kpp_request_free(req);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_gen_pubkey);
+
+int nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm,
+		u8 *ctrl_key, size_t ctrl_key_len,
+		u8 *sess_key, size_t sess_key_len)
+{
+	struct kpp_request *req;
+	struct crypto_wait wait;
+	struct scatterlist src, dst;
+	int ret;
+
+	req = kpp_request_alloc(dh_tfm, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	crypto_init_wait(&wait);
+	sg_init_one(&src, ctrl_key, ctrl_key_len);
+	kpp_request_set_input(req, &src, ctrl_key_len);
+	sg_init_one(&dst, sess_key, sess_key_len);
+	kpp_request_set_output(req, &dst, 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);

drivers/nvme/host/auth.c line 491

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+
+	kpp_request_free(req);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_gen_shared_secret);
+
+static int nvme_auth_send(struct nvme_ctrl *ctrl, int qid,
+		void *data, size_t tl)
+{
+	struct nvme_command cmd = {};

I think we are using one space like { } in the host/core, please check.

drivers/nvme/host/auth.c line 495

Quoted 4 lines
drivers/nvme/host/auth.c new file +4 -0
+	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;

it will be great if we avoid above initilization and make it on a separate line.

Quoted 20 lines
drivers/nvme/host/auth.c new file +20 -0
+	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 > 0)
+		dev_dbg(ctrl->device,
+			"%s: qid %d nvme status %d\n", __func__, qid, ret);
+	else if (ret < 0)
+		dev_dbg(ctrl->device,
+			"%s: qid %d error %d\n", __func__, qid, ret);
+	return ret;
+}
+

drivers/nvme/host/auth.c line 525

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+static int nvme_auth_receive(struct nvme_ctrl *ctrl, int qid,
+		void *buf, size_t al)
+{
+	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;
+

same above ?

Quoted 186 lines
drivers/nvme/host/auth.c new file +186 -0
+	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;
+	}
+
+	return 0;
+}
+
+static int nvme_auth_receive_validate(struct nvme_ctrl *ctrl, int qid,
+		struct nvmf_auth_dhchap_failure_data *data,
+		u16 transaction, u8 expected_msg)
+{
+	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->rescode_exp;
+	}
+	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_INCORRECT_MESSAGE;
+	}
+	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_INCORRECT_MESSAGE;
+	}
+	return 0;
+}
+
+static int nvme_auth_set_dhchap_negotiate_data(struct nvme_ctrl *ctrl,
+		struct nvme_dhchap_queue_context *chap)
+{
+	struct nvmf_auth_dhchap_negotiate_data *data = chap->buf;
+	size_t size = sizeof(*data) + sizeof(union nvmf_auth_protocol);
+
+	if (chap->buf_size < size) {
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+		return -EINVAL;
+	}
+	memset((u8 *)chap->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 = 6;
+	data->auth_protocol[0].dhchap.idlist[0] = NVME_AUTH_DHCHAP_SHA256;
+	data->auth_protocol[0].dhchap.idlist[1] = NVME_AUTH_DHCHAP_SHA384;
+	data->auth_protocol[0].dhchap.idlist[2] = NVME_AUTH_DHCHAP_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;
+}
+
+static int nvme_auth_process_dhchap_challenge(struct nvme_ctrl *ctrl,
+		struct nvme_dhchap_queue_context *chap)
+{
+	struct nvmf_auth_dhchap_challenge_data *data = chap->buf;
+	size_t size = sizeof(*data) + data->hl + data->dhvlen;
+	const char *hmac_name;
+
+	if (chap->buf_size < size) {
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+		return NVME_SC_INVALID_FIELD;
+	}
+
+	hmac_name = nvme_auth_hmac_name(data->hashid);
+	if (!hmac_name) {
+		dev_warn(ctrl->device,
+			 "qid %d: invalid HASH ID %d\n",
+			 chap->qid, data->hashid);
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
+		return -EPROTO;
+	}
+	if (chap->hash_id == data->hashid && chap->shash_tfm &&
+	    !strcmp(crypto_shash_alg_name(chap->shash_tfm), hmac_name) &&
+	    crypto_shash_digestsize(chap->shash_tfm) == data->hl) {
+		dev_dbg(ctrl->device,
+			"qid %d: reuse existing hash %s\n",
+			chap->qid, hmac_name);
+		goto select_kpp;
+	}
+	if (chap->shash_tfm) {
+		crypto_free_shash(chap->shash_tfm);
+		chap->hash_id = 0;
+		chap->hash_len = 0;
+	}
+	chap->shash_tfm = crypto_alloc_shash(hmac_name, 0,
+					     CRYPTO_ALG_ALLOCATES_MEMORY);
+	if (IS_ERR(chap->shash_tfm)) {
+		dev_warn(ctrl->device,
+			 "qid %d: failed to allocate hash %s, error %ld\n",
+			 chap->qid, hmac_name, PTR_ERR(chap->shash_tfm));
+		chap->shash_tfm = NULL;
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
+		return NVME_SC_AUTH_REQUIRED;
+	}
+	if (crypto_shash_digestsize(chap->shash_tfm) != data->hl) {
+		dev_warn(ctrl->device,
+			 "qid %d: invalid hash length %d\n",
+			 chap->qid, data->hl);
+		crypto_free_shash(chap->shash_tfm);
+		chap->shash_tfm = NULL;
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
+		return NVME_SC_AUTH_REQUIRED;
+	}
+	if (chap->hash_id != data->hashid) {
+		kfree(chap->host_response);
+		chap->host_response = NULL;
+	}
+	chap->hash_id = data->hashid;
+	chap->hash_len = data->hl;
+	dev_dbg(ctrl->device, "qid %d: selected hash %s\n",
+		chap->qid, hmac_name);
+
+	gid_name = nvme_auth_dhgroup_kpp(data->dhgid);
+	if (!gid_name) {
+		dev_warn(ctrl->device,
+			 "qid %d: 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) {
+		if (data->dhvlen == 0) {
+			dev_warn(ctrl->device,
+				 "qid %d: 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)) {
+			int ret = PTR_ERR(chap->dh_tfm);
+
+			dev_warn(ctrl->device,
+				 "qid %d: failed to initialize %s\n",
+				 chap->qid, gid_name);
+			chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
+			chap->dh_tfm = NULL;
+			return ret;
+		}
+		chap->dhgroup_id = data->dhgid;
+	} else if (data->dhvlen != 0) {
+		dev_warn(ctrl->device,
+			 "qid %d: invalid DH value for NULL DH\n",
+			chap->qid);
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
+		return -EPROTO;
+	}
+	dev_dbg(ctrl->device, "qid %d: selected DH group %s\n",
+		chap->qid, gid_name);
+
+select_kpp:
+	chap->s1 = le32_to_cpu(data->seqnum);
+	memcpy(chap->c1, data->cval, chap->hash_len);
+

drivers/nvme/host/auth.c line 721

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	return 0;
+}
+
+static int nvme_auth_set_dhchap_reply_data(struct nvme_ctrl *ctrl,
+		struct nvme_dhchap_queue_context *chap)
+{
+	struct nvmf_auth_dhchap_reply_data *data = chap->buf;
+	size_t size = sizeof(*data);
+
+	size += 2 * chap->hash_len;

a comment would be nice for such calculations as above..

Quoted 658 lines
drivers/nvme/host/auth.c new file +564 -0
+	if (ctrl->opts->dhchap_bidi) {
+		get_random_bytes(chap->c2, chap->hash_len);
+		chap->s2 = nvme_dhchap_seqnum++;
+	} else
+		memset(chap->c2, 0, chap->hash_len);
+
+
+	if (chap->buf_size < size) {
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+		return -EINVAL;
+	}
+	memset(chap->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 = 0;
+	data->seqnum = cpu_to_le32(chap->s2);
+	memcpy(data->rval, chap->response, chap->hash_len);
+	if (ctrl->opts->dhchap_bidi) {
+		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);
+	}
+	return size;
+}
+
+static int nvme_auth_process_dhchap_success1(struct nvme_ctrl *ctrl,
+		struct nvme_dhchap_queue_context *chap)
+{
+	struct nvmf_auth_dhchap_success1_data *data = chap->buf;
+	size_t size = sizeof(*data);
+
+	if (ctrl->opts->dhchap_bidi)
+		size += chap->hash_len;
+
+
+	if (chap->buf_size < size) {
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+		return NVME_SC_INVALID_FIELD;
+	}
+
+	if (data->hl != chap->hash_len) {
+		dev_warn(ctrl->device,
+			 "qid %d: invalid hash length %d\n",
+			 chap->qid, data->hl);
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
+		return NVME_SC_INVALID_FIELD;
+	}
+
+	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: controller authentication failed\n",
+			 chap->qid);
+		chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
+		return NVME_SC_AUTH_REQUIRED;
+	}
+	dev_info(ctrl->device,
+		 "qid %d: controller authenticated\n",
+		chap->qid);
+	return 0;
+}
+
+static int nvme_auth_set_dhchap_success2_data(struct nvme_ctrl *ctrl,
+		struct nvme_dhchap_queue_context *chap)
+{
+	struct nvmf_auth_dhchap_success2_data *data = chap->buf;
+	size_t size = sizeof(*data);
+
+	memset(chap->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_set_dhchap_failure2_data(struct nvme_ctrl *ctrl,
+		struct nvme_dhchap_queue_context *chap)
+{
+	struct nvmf_auth_dhchap_failure_data *data = chap->buf;
+	size_t size = sizeof(*data);
+
+	memset(chap->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->rescode = NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED;
+	data->rescode_exp = chap->status;
+
+	return size;
+}
+
+static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
+		struct nvme_dhchap_queue_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);
+	if (chap->dh_tfm) {
+		challenge = kmalloc(chap->hash_len, GFP_KERNEL);
+		if (!challenge) {
+			ret = -ENOMEM;
+			goto out;
+		}
+		ret = nvme_auth_augmented_challenge(chap->hash_id,
+						    chap->sess_key,
+						    chap->sess_key_len,
+						    chap->c1, challenge,
+						    chap->hash_len);
+		if (ret)
+			goto out;
+	}
+	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:
+	if (challenge != chap->c1)
+		kfree(challenge);
+	return ret;
+}
+
+static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl,
+		struct nvme_dhchap_queue_context *chap)
+{
+	SHASH_DESC_ON_STACK(shash, chap->shash_tfm);
+	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->hash_id,
+						    chap->sess_key,
+						    chap->sess_key_len,
+						    chap->c2, challenge,
+						    chap->hash_len);
+		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",
+		__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:
+	if (challenge != chap->c2)
+		kfree(challenge);
+	return ret;
+}
+
+int nvme_auth_generate_key(struct nvme_ctrl *ctrl)
+{
+	int ret;
+	u8 key_hash;
+
+	if (!ctrl->opts->dhchap_secret)
+		return 0;
+
+	if (ctrl->dhchap_key && ctrl->dhchap_key_len)
+		/* Key already set */
+		return 0;
+
+	if (sscanf(ctrl->opts->dhchap_secret, "DHHC-1:%hhd:%*s:",
+		   &key_hash) != 1)
+		return -EINVAL;
+
+	/* Pass in the secret without the 'DHHC-1:XX:' prefix */
+	ctrl->dhchap_key = nvme_auth_extract_secret(ctrl->opts->dhchap_secret + 10,
+					     &ctrl->dhchap_key_len);
+	if (IS_ERR(ctrl->dhchap_key)) {
+		ret = PTR_ERR(ctrl->dhchap_key);
+		ctrl->dhchap_key = NULL;
+		return ret;
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_generate_key);
+
+static void nvme_auth_reset(struct nvme_dhchap_queue_context *chap)
+{
+	chap->status = 0;
+	chap->error = 0;
+	chap->s1 = 0;
+	chap->s2 = 0;
+	chap->transaction = 0;
+	memset(chap->c1, 0, sizeof(chap->c1));
+	memset(chap->c2, 0, sizeof(chap->c2));
+}
+
+static void __nvme_auth_free(struct nvme_dhchap_queue_context *chap)
+{
+	if (chap->shash_tfm)
+		crypto_free_shash(chap->shash_tfm);
+	kfree_sensitive(chap->host_response);
+	kfree(chap->buf);
+	kfree(chap);
+}
+
+static void __nvme_auth_work(struct work_struct *work)
+{
+	struct nvme_dhchap_queue_context *chap =
+		container_of(work, struct nvme_dhchap_queue_context, auth_work);
+	struct nvme_ctrl *ctrl = chap->ctrl;
+	size_t tl;
+	int ret = 0;
+
+	chap->transaction = ctrl->transaction++;
+
+	/* DH-HMAC-CHAP Step 1: send negotiate */
+	dev_dbg(ctrl->device, "%s: qid %d send negotiate\n",
+		__func__, chap->qid);
+	ret = nvme_auth_set_dhchap_negotiate_data(ctrl, chap);
+	if (ret < 0) {
+		chap->error = ret;
+		return;
+	}
+	tl = ret;
+	ret = nvme_auth_send(ctrl, chap->qid, chap->buf, tl);
+	if (ret) {
+		chap->error = ret;
+		return;
+	}
+
+	/* DH-HMAC-CHAP Step 2: receive challenge */
+	dev_dbg(ctrl->device, "%s: qid %d receive challenge\n",
+		__func__, chap->qid);
+
+	memset(chap->buf, 0, chap->buf_size);
+	ret = nvme_auth_receive(ctrl, chap->qid, chap->buf, chap->buf_size);
+	if (ret) {
+		dev_warn(ctrl->device,
+			 "qid %d failed to receive challenge, %s %d\n",
+			 chap->qid, ret < 0 ? "error" : "nvme status", ret);
+		chap->error = ret;
+		return;
+	}
+	ret = nvme_auth_receive_validate(ctrl, chap->qid, chap->buf, chap->transaction,
+					 NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE);
+	if (ret) {
+		chap->status = ret;
+		chap->error = NVME_SC_AUTH_REQUIRED;
+		return;
+	}
+
+	ret = nvme_auth_process_dhchap_challenge(ctrl, chap);
+	if (ret) {
+		/* Invalid challenge parameters */
+		goto fail2;
+	}
+
+	if (chap->ctrl_key_len) {
+		dev_dbg(ctrl->device,
+			"%s: qid %d DH exponential\n",
+			__func__, chap->qid);
+		ret = nvme_auth_dhchap_exponential(ctrl, chap);
+		if (ret)
+			goto fail2;
+	}
+
+	dev_dbg(ctrl->device, "%s: qid %d host response\n",
+		__func__, chap->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 send reply\n",
+		__func__, chap->qid);
+	ret = nvme_auth_set_dhchap_reply_data(ctrl, chap);
+	if (ret < 0)
+		goto fail2;
+
+	tl = ret;
+	ret = nvme_auth_send(ctrl, chap->qid, chap->buf, tl);
+	if (ret)
+		goto fail2;
+
+	/* DH-HMAC-CHAP Step 4: receive success1 */
+	dev_dbg(ctrl->device, "%s: qid %d receive success1\n",
+		__func__, chap->qid);
+
+	memset(chap->buf, 0, chap->buf_size);
+	ret = nvme_auth_receive(ctrl, chap->qid, chap->buf, chap->buf_size);
+	if (ret) {
+		dev_warn(ctrl->device,
+			 "qid %d failed to receive success1, %s %d\n",
+			 chap->qid, ret < 0 ? "error" : "nvme status", ret);
+		chap->error = ret;
+		return;
+	}
+	ret = nvme_auth_receive_validate(ctrl, chap->qid,
+					 chap->buf, chap->transaction,
+					 NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1);
+	if (ret) {
+		chap->status = ret;
+		chap->error = NVME_SC_AUTH_REQUIRED;
+		return;
+	}
+
+	if (ctrl->opts->dhchap_bidi) {
+		dev_dbg(ctrl->device,
+			"%s: qid %d controller response\n",
+			__func__, chap->qid);
+		ret = nvme_auth_dhchap_ctrl_response(ctrl, chap);
+		if (ret)
+			goto fail2;
+	}
+
+	ret = nvme_auth_process_dhchap_success1(ctrl, chap);
+	if (ret < 0) {
+		/* Controller authentication failed */
+		goto fail2;
+	}
+
+	/* DH-HMAC-CHAP Step 5: send success2 */
+	dev_dbg(ctrl->device, "%s: qid %d send success2\n",
+		__func__, chap->qid);
+	tl = nvme_auth_set_dhchap_success2_data(ctrl, chap);
+	ret = nvme_auth_send(ctrl, chap->qid, chap->buf, tl);
+	if (!ret) {
+		chap->error = 0;
+		return;
+	}
+
+fail2:
+	dev_dbg(ctrl->device, "%s: qid %d send failure2, status %x\n",
+		__func__, chap->qid, chap->status);
+	tl = nvme_auth_set_dhchap_failure2_data(ctrl, chap);
+	ret = nvme_auth_send(ctrl, chap->qid, chap->buf, tl);
+	if (!ret)
+		ret = -EPROTO;
+	chap->error = ret;
+}
+
+int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
+{
+	struct nvme_dhchap_queue_context *chap;
+
+	if (!ctrl->dhchap_key || !ctrl->dhchap_key_len) {
+		dev_warn(ctrl->device, "qid %d: no key\n", qid);
+		return -ENOKEY;
+	}
+
+	mutex_lock(&ctrl->dhchap_auth_mutex);
+	/* Check if the context is already queued */
+	list_for_each_entry(chap, &ctrl->dhchap_auth_list, entry) {
+		if (chap->qid == qid) {
+			mutex_unlock(&ctrl->dhchap_auth_mutex);
+			queue_work(nvme_wq, &chap->auth_work);
+			return 0;
+		}
+	}
+	chap = kzalloc(sizeof(*chap), GFP_KERNEL);
+	if (!chap) {
+		mutex_unlock(&ctrl->dhchap_auth_mutex);
+		return -ENOMEM;
+	}
+	chap->qid = qid;
+	chap->ctrl = ctrl;
+
+	/*
+	 * Allocate a large enough buffer for the entire negotiation:
+	 * 4k should be enough to ffdhe8192.
+	 */
+	chap->buf_size = 4096;
+	chap->buf = kzalloc(chap->buf_size, GFP_KERNEL);
+	if (!chap->buf) {
+		mutex_unlock(&ctrl->dhchap_auth_mutex);
+		kfree(chap);
+		return -ENOMEM;
+	}
+
+	INIT_WORK(&chap->auth_work, __nvme_auth_work);
+	list_add(&chap->entry, &ctrl->dhchap_auth_list);
+	mutex_unlock(&ctrl->dhchap_auth_mutex);
+	queue_work(nvme_wq, &chap->auth_work);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_negotiate);
+
+int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid)
+{
+	struct nvme_dhchap_queue_context *chap;
+	int ret;
+
+	mutex_lock(&ctrl->dhchap_auth_mutex);
+	list_for_each_entry(chap, &ctrl->dhchap_auth_list, entry) {
+		if (chap->qid != qid)
+			continue;
+		mutex_unlock(&ctrl->dhchap_auth_mutex);
+		flush_work(&chap->auth_work);
+		ret = chap->error;
+		nvme_auth_reset(chap);
+		return ret;
+	}
+	mutex_unlock(&ctrl->dhchap_auth_mutex);
+	return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_wait);
+
+/* Assumes that the controller is in state RESETTING */
+static void nvme_dhchap_auth_work(struct work_struct *work)
+{
+	struct nvme_ctrl *ctrl =
+		container_of(work, struct nvme_ctrl, dhchap_auth_work);
+	int ret, q;
+
+	nvme_stop_queues(ctrl);
+	/* Authenticate admin queue first */
+	ret = nvme_auth_negotiate(ctrl, NVME_QID_ANY);
+	if (ret) {
+		dev_warn(ctrl->device,
+			 "qid 0: error %d setting up authentication\n", ret);
+		goto out;
+	}
+	ret = nvme_auth_wait(ctrl, NVME_QID_ANY);
+	if (ret) {
+		dev_warn(ctrl->device,
+			 "qid 0: authentication failed\n");
+		goto out;
+	}
+	dev_info(ctrl->device, "qid 0: authenticated\n");
+
+	for (q = 1; q < ctrl->queue_count; q++) {
+		ret = nvme_auth_negotiate(ctrl, q);
+		if (ret) {
+			dev_warn(ctrl->device,
+				 "qid %d: error %d setting up authentication\n",
+				 q, ret);
+			goto out;
+		}
+	}
+out:
+	/*
+	 * Failure is a soft-state; credentials remain valid until
+	 * the controller terminates the connection.
+	 */
+	if (nvme_change_ctrl_state(ctrl, NVME_CTRL_LIVE))
+		nvme_start_queues(ctrl);
+}
+
+void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl)
+{
+	INIT_LIST_HEAD(&ctrl->dhchap_auth_list);
+	INIT_WORK(&ctrl->dhchap_auth_work, nvme_dhchap_auth_work);
+	mutex_init(&ctrl->dhchap_auth_mutex);
+	nvme_auth_generate_key(ctrl);
+}
+EXPORT_SYMBOL_GPL(nvme_auth_init_ctrl);
+
+void nvme_auth_stop(struct nvme_ctrl *ctrl)
+{
+	struct nvme_dhchap_queue_context *chap = NULL, *tmp;
+
+	cancel_work_sync(&ctrl->dhchap_auth_work);
+	mutex_lock(&ctrl->dhchap_auth_mutex);
+	list_for_each_entry_safe(chap, tmp, &ctrl->dhchap_auth_list, entry)
+		cancel_work_sync(&chap->auth_work);
+	mutex_unlock(&ctrl->dhchap_auth_mutex);
+}
+EXPORT_SYMBOL_GPL(nvme_auth_stop);
+
+void nvme_auth_free(struct nvme_ctrl *ctrl)
+{
+	struct nvme_dhchap_queue_context *chap = NULL, *tmp;
+
+	mutex_lock(&ctrl->dhchap_auth_mutex);
+	list_for_each_entry_safe(chap, tmp, &ctrl->dhchap_auth_list, entry) {
+		list_del_init(&chap->entry);
+		flush_work(&chap->auth_work);
+		__nvme_auth_free(chap);
+	}
+	mutex_unlock(&ctrl->dhchap_auth_mutex);
+	kfree(ctrl->dhchap_key);
+	ctrl->dhchap_key = NULL;
+	ctrl->dhchap_key_len = 0;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_free);
drivers/nvme/host/auth.h new file +25 -0
new file mode 100644
index 000000000000..cf1255f9db6d
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions
+ */
+
+#ifndef _NVME_AUTH_H
+#define _NVME_AUTH_H
+
+#include <crypto/kpp.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);
+
+unsigned char *nvme_auth_extract_secret(unsigned char *dhchap_secret,
+					size_t *dhchap_key_len);
+u8 *nvme_auth_transform_key(u8 *key, size_t key_len, u8 key_hash, char *nqn);
+
+#endif /* _NVME_AUTH_H */
drivers/nvme/host/core.c +18 -2
index 7efb31b87f37..f669b054790b 100644
@@ -24,6 +24,7 @@
 
  #include "nvme.h"
  #include "fabrics.h"
+#include "auth.h"
 
  #define CREATE_TRACE_POINTS
  #include "trace.h"
@@ -322,6 +323,7 @@ enum nvme_disposition {
  	COMPLETE,
  	RETRY,
  	FAILOVER,
+	AUTHENTICATE,
  };
 
  static inline enum nvme_disposition nvme_decide_disposition(struct request *req)
@@ -329,6 +331,9 @@ static inline enum nvme_disposition nvme_decide_disposition(struct request *req)
  	if (likely(nvme_req(req)->status == 0))
  		return COMPLETE;
 
+	if ((nvme_req(req)->status & 0x7ff) == NVME_SC_AUTH_REQUIRED)
+		return AUTHENTICATE;
+
  	if (blk_noretry_request(req) ||
  	    (nvme_req(req)->status & NVME_SC_DNR) ||
  	    nvme_req(req)->retries >= nvme_max_retries)
@@ -361,11 +366,13 @@ static inline void nvme_end_req(struct request *req)
 
  void nvme_complete_rq(struct request *req)
  {
+	struct nvme_ctrl *ctrl = nvme_req(req)->ctrl;
+
  	trace_nvme_complete_rq(req);
  	nvme_cleanup_cmd(req);
 
-	if (nvme_req(req)->ctrl->kas)
-		nvme_req(req)->ctrl->comp_seen = true;
+	if (ctrl->kas)
+		ctrl->comp_seen = true;
 
  	switch (nvme_decide_disposition(req)) {
  	case COMPLETE:
@@ -377,6 +384,15 @@ void nvme_complete_rq(struct request *req)
  	case FAILOVER:
  		nvme_failover_req(req);
  		return;
+	case AUTHENTICATE:
+#ifdef CONFIG_NVME_AUTH
+		if (nvme_change_ctrl_state(ctrl, NVME_CTRL_RESETTING))
+			queue_work(nvme_wq, &ctrl->dhchap_auth_work);
+		nvme_retry_req(req);
+#else
+		nvme_end_req(req);
+#endif
+		return;
  	}
  }
  EXPORT_SYMBOL_GPL(nvme_complete_rq);

drivers/nvme/host/core.c bool __nvme_check_ready(struct nvme_ctrl *ctrl, struct request *rq, · line 728

Quoted 7 lines
drivers/nvme/host/core.c +3 -1
@@ -707,7 +723,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))

why not create a inline helper for above check ?

Quoted 255 lines
drivers/nvme/host/core.c +55 -0
  				return true;
  			break;
  		default:
@@ -3458,6 +3476,51 @@ 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
+static ssize_t nvme_ctrl_dhchap_secret_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+	struct nvmf_ctrl_options *opts = ctrl->opts;
+
+	if (!opts->dhchap_secret)
+		return sysfs_emit(buf, "none\n");
+	return sysfs_emit(buf, "%s\n", opts->dhchap_secret);
+}
+
+static ssize_t nvme_ctrl_dhchap_secret_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+	struct nvmf_ctrl_options *opts = ctrl->opts;
+	char *dhchap_secret;
+
+	if (!ctrl->opts->dhchap_secret)
+		return -EINVAL;
+	if (count < 7)
+		return -EINVAL;
+	if (memcmp(buf, "DHHC-1:", 7))
+		return -EINVAL;
+
+	dhchap_secret = kzalloc(count + 1, GFP_KERNEL);
+	if (!dhchap_secret)
+		return -ENOMEM;
+	memcpy(dhchap_secret, buf, count);
+	if (strcmp(dhchap_secret, opts->dhchap_secret)) {
+		kfree(opts->dhchap_secret);
+		opts->dhchap_secret = dhchap_secret;
+		/* Key has changed; reset authentication data */
+		nvme_auth_free(ctrl);
+		nvme_auth_generate_key(ctrl);
+	}
+	if (nvme_change_ctrl_state(ctrl, NVME_CTRL_RESETTING))
+		queue_work(nvme_wq, &ctrl->dhchap_auth_work);
+	return count;
+}
+DEVICE_ATTR(dhchap_secret, S_IRUGO | S_IWUSR,
+	nvme_ctrl_dhchap_secret_show, nvme_ctrl_dhchap_secret_store);
+#endif
+
  static struct attribute *nvme_dev_attrs[] = {
  	&dev_attr_reset_controller.attr,
  	&dev_attr_rescan_controller.attr,
@@ -3479,6 +3542,9 @@ 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_secret.attr,
+#endif
  	NULL
  };
 
@@ -3502,6 +3568,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_secret.attr && !ctrl->opts)
+		return 0;
+#endif
 
  	return a->mode;
  }
@@ -4312,6 +4382,7 @@ EXPORT_SYMBOL_GPL(nvme_complete_async_event);
  void nvme_stop_ctrl(struct nvme_ctrl *ctrl)
  {
  	nvme_mpath_stop(ctrl);
+	nvme_auth_stop(ctrl);
  	nvme_stop_keep_alive(ctrl);
  	nvme_stop_failfast_work(ctrl);
  	flush_work(&ctrl->async_event_work);
@@ -4366,6 +4437,7 @@ static void nvme_free_ctrl(struct device *dev)
 
  	nvme_free_cels(ctrl);
  	nvme_mpath_uninit(ctrl);
+	nvme_auth_free(ctrl);
  	__free_page(ctrl->discard_page);
 
  	if (subsys) {
@@ -4456,6 +4528,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
 
  	nvme_fault_inject_init(&ctrl->fault_inject, dev_name(ctrl->device));
  	nvme_mpath_init_ctrl(ctrl);
+	nvme_auth_init_ctrl(ctrl);
 
  	return 0;
  out_free_name:
drivers/nvme/host/fabrics.c +70 -3
index 9a8eade7cd23..ee6058c24743 100644
@@ -370,6 +370,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;
@@ -402,8 +403,25 @@ 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: failed to setup authentication\n");
+			ret = NVME_SC_AUTH_REQUIRED;
+			goto out_free_data;
+		}
+		ret = nvme_auth_wait(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;
@@ -436,6 +454,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;
@@ -461,6 +480,24 @@ 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 %d: failed to setup authentication\n", qid);
+			ret = NVME_SC_AUTH_REQUIRED;
+		} else {
+			ret = nvme_auth_wait(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;
  }
@@ -552,6 +589,8 @@ 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_BIDI,		"dhchap_bidi"		},
  	{ NVMF_OPT_ERR,			NULL			}
  };
 
@@ -827,6 +866,23 @@ 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 (strlen(p) < 11 || strncmp(p, "DHHC-1:", 7)) {
+				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_BIDI:
+			opts->dhchap_bidi = true;
+			break;
  		default:
  			pr_warn("unknown parameter or missing value '%s' in ctrl creation request\n",
  				p);
@@ -945,6 +1001,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);
@@ -954,7 +1011,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_BIDI)
 
  static struct nvme_ctrl *
  nvmf_create_ctrl(struct device *dev, const char *buf)
@@ -1171,7 +1231,14 @@ static void __exit nvmf_exit(void)
  	BUILD_BUG_ON(sizeof(struct nvmf_connect_command) != 64);
  	BUILD_BUG_ON(sizeof(struct nvmf_property_get_command) != 64);
  	BUILD_BUG_ON(sizeof(struct nvmf_property_set_command) != 64);
+	BUILD_BUG_ON(sizeof(struct nvmf_auth_send_command) != 64);
+	BUILD_BUG_ON(sizeof(struct nvmf_auth_receive_command) != 64);
  	BUILD_BUG_ON(sizeof(struct nvmf_connect_data) != 1024);
+	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);
  }
 
  MODULE_LICENSE("GPL v2");
drivers/nvme/host/fabrics.h +2 -0
index a146cb903869..27df1aac5736 100644
@@ -67,6 +67,8 @@ 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_BIDI	= 1 << 23,
  };
 
  /**

drivers/nvme/host/fabrics.h enum { · line 102

Quoted 5 lines
drivers/nvme/host/fabrics.h +2 -0
@@ -96,6 +98,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_bidi: enable DH-HMAC-CHAP bi-directional authentication

Is it my editor or we need to align above comment ?

Quoted 101 lines
drivers/nvme/host/fabrics.h +2 -0
   * @disable_sqflow: disable controller sq flow control
   * @hdr_digest: generate/verify header digest (TCP)
   * @data_digest: generate/verify data digest (TCP)
@@ -120,6 +124,8 @@ struct nvmf_ctrl_options {
  	unsigned int		kato;
  	struct nvmf_host	*host;
  	int			max_reconnects;
+	char			*dhchap_secret;
+	bool			dhchap_bidi;
  	bool			disable_sqflow;
  	bool			hdr_digest;
  	bool			data_digest;
drivers/nvme/host/nvme.h +30 -0
index 9871c0c9374c..b0dcb7d79b9e 100644
@@ -318,6 +318,15 @@ struct nvme_ctrl {
  	struct work_struct ana_work;
  #endif
 
+#ifdef CONFIG_NVME_AUTH
+	struct work_struct dhchap_auth_work;
+	struct list_head dhchap_auth_list;
+	struct mutex dhchap_auth_mutex;
+	unsigned char *dhchap_key;
+	size_t dhchap_key_len;
+	u16 transaction;
+#endif
+
  	/* Power saving configuration */
  	u64 ps_max_latency_us;
  	bool apst_enabled;
@@ -885,6 +894,27 @@ static inline bool nvme_ctrl_sgl_supported(struct nvme_ctrl *ctrl)
  	return ctrl->sgls & ((1 << 0) | (1 << 1));
  }
 
+#ifdef CONFIG_NVME_AUTH
+void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl);
+void nvme_auth_stop(struct nvme_ctrl *ctrl);
+int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid);
+int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid);
+void nvme_auth_free(struct nvme_ctrl *ctrl);
+int nvme_auth_generate_key(struct nvme_ctrl *ctrl);
+#else
+static inline void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl) {};
+static inline void nvme_auth_stop(struct nvme_ctrl *ctrl) {};
+static inline int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
+{
+	return -EPROTONOSUPPORT;
+}
+static inline int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid)
+{
+	return NVME_SC_AUTH_REQUIRED;
+}
+static inline void nvme_auth_free(struct nvme_ctrl *ctrl) {};
+#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 +28 -0
index 2a89c5aa0790..1c36fcedea20 100644
@@ -287,6 +287,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 trace_seq *p, · line 343

Quoted 10 lines
drivers/nvme/host/trace.c +4 -0
@@ -306,6 +334,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);
  	}

_______________________________________________
Linux-nvme mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-nvme

maintainer
#22 lore raw

Re: [PATCH 07/12] nvme: Implement In-Band authentication

In reply to Hannes Reinecke

drivers/nvme/host/auth.c line 1215

Quoted 9 lines
drivers/nvme/host/auth.c new file +8 -0
+/* Assumes that the controller is in state RESETTING */
+static void nvme_dhchap_auth_work(struct work_struct *work)
+{
+	struct nvme_ctrl *ctrl =
+		container_of(work, struct nvme_ctrl, dhchap_auth_work);
+	int ret, q;
+
+	nvme_stop_queues(ctrl);
	blk_mq_quiesce_queue(ctrl->admin_q);
Quoted 21 lines
drivers/nvme/host/auth.c new file +21 -0
+	/* Authenticate admin queue first */
+	ret = nvme_auth_negotiate(ctrl, NVME_QID_ANY);
+	if (ret) {
+		dev_warn(ctrl->device,
+			 "qid 0: error %d setting up authentication\n", ret);
+		goto out;
+	}
+	ret = nvme_auth_wait(ctrl, NVME_QID_ANY);
+	if (ret) {
+		dev_warn(ctrl->device,
+			 "qid 0: authentication failed\n");
+		goto out;
+	}
+	dev_info(ctrl->device, "qid 0: authenticated\n");
+
+	for (q = 1; q < ctrl->queue_count; q++) {
+		ret = nvme_auth_negotiate(ctrl, q);
+		if (ret) {
+			dev_warn(ctrl->device,
+				 "qid %d: error %d setting up authentication\n",
+				 q, ret);

drivers/nvme/host/auth.c line 1246

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+			goto out;
+		}
+	}
+out:
+	/*
+	 * Failure is a soft-state; credentials remain valid until
+	 * the controller terminates the connection.
+	 */
+	if (nvme_change_ctrl_state(ctrl, NVME_CTRL_LIVE))
+		nvme_start_queues(ctrl);
		blk_mq_unquiesce_queue(ctrl->admin_q);
Quoted 1 line

+}

author maintainer
#23 lore raw

Re: [PATCH 07/12] nvme: Implement In-Band authentication

Quoted 36 lines · Sagi Grimberg

drivers/nvme/host/auth.c line 1215

Quoted 9 lines
drivers/nvme/host/auth.c new file +8 -0
+/* Assumes that the controller is in state RESETTING */
+static void nvme_dhchap_auth_work(struct work_struct *work)
+{
+    struct nvme_ctrl *ctrl =
+        container_of(work, struct nvme_ctrl, dhchap_auth_work);
+    int ret, q;
+
+    nvme_stop_queues(ctrl);

    blk_mq_quiesce_queue(ctrl->admin_q);

Quoted 24 lines

+    /* Authenticate admin queue first */
+    ret = nvme_auth_negotiate(ctrl, NVME_QID_ANY);
+    if (ret) {
+        dev_warn(ctrl->device,
+             "qid 0: error %d setting up authentication\n", ret); +        goto out;
+    }
+    ret = nvme_auth_wait(ctrl, NVME_QID_ANY);
+    if (ret) {
+        dev_warn(ctrl->device,
+             "qid 0: authentication failed\n");
+        goto out;
+    }
+    dev_info(ctrl->device, "qid 0: authenticated\n"); +
+    for (q = 1; q < ctrl->queue_count; q++) {
+        ret = nvme_auth_negotiate(ctrl, q);
+        if (ret) {
+            dev_warn(ctrl->device,
+                 "qid %d: error %d setting up authentication\n", +                 q, ret);
+            goto out;
+        }
+    }

Quoted 10 lines
Quoted 7 lines

+out:
+    /*
+     * Failure is a soft-state; credentials remain valid until +     * the controller terminates the connection.
+     */
+    if (nvme_change_ctrl_state(ctrl, NVME_CTRL_LIVE)) +        nvme_start_queues(ctrl);

        blk_mq_unquiesce_queue(ctrl->admin_q);

Quoted 1 line

+}

Actually, after recent discussions on the fmds group there shouldn't be a requirement to stop the queues, so I'll be dropping the stop/start queue things. (And the change in controller state, too, as it isn't required, either).

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
maintainer
#24 lore raw

Re: [PATCH 07/12] nvme: Implement In-Band authentication

Quoted 3 lines

Actually, after recent discussions on the fmds group there shouldn't be a requirement to stop the queues, so I'll be dropping the stop/start queue things. (And the change in controller state, too, as it isn't required, either).

Hmm, ok.

maintainer
#25 lore raw

Re: [PATCH 07/12] nvme: Implement In-Band authentication

In reply to Hannes Reinecke

drivers/nvme/host/auth.c line 1214

Quoted 8 lines
drivers/nvme/host/auth.c new file +7 -0
+/* Assumes that the controller is in state RESETTING */
+static void nvme_dhchap_auth_work(struct work_struct *work)
+{
+	struct nvme_ctrl *ctrl =
+		container_of(work, struct nvme_ctrl, dhchap_auth_work);
+	int ret, q;
+

Here I would print a single:

	dev_info(ctrl->device, "re-authenticating controller");

This is instead of all the queue re-authentication prints that should be dev_dbg.

Let's avoid doing the per-queue print...

author maintainer
#26 lore raw

Re: [PATCH 07/12] nvme: Implement In-Band authentication

Quoted 12 lines · Sagi Grimberg

drivers/nvme/host/auth.c line 1214

Quoted 8 lines
drivers/nvme/host/auth.c new file +7 -0
+/* Assumes that the controller is in state RESETTING */
+static void nvme_dhchap_auth_work(struct work_struct *work)
+{
+    struct nvme_ctrl *ctrl =
+        container_of(work, struct nvme_ctrl, dhchap_auth_work);
+    int ret, q;
+

Here I would print a single:
    dev_info(ctrl->device, "re-authenticating controller");

Quoted 4 lines

This is instead of all the queue re-authentication prints that should be dev_dbg.

Let's avoid doing the per-queue print...

Hmm. Actually the spec allows to use different keys per queue, even though our implementation doesn't. And fmds has struggled to come up with a sane usecase for that. But yes, okay, will be updating 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
maintainer
#27 lore raw

Re: [PATCH 07/12] nvme: Implement In-Band authentication

Quoted 13 lines · Hannes Reinecke
Quoted 12 lines · Sagi Grimberg

drivers/nvme/host/auth.c line 1214

Quoted 8 lines
drivers/nvme/host/auth.c new file +7 -0
+/* Assumes that the controller is in state RESETTING */
+static void nvme_dhchap_auth_work(struct work_struct *work)
+{
+    struct nvme_ctrl *ctrl =
+        container_of(work, struct nvme_ctrl, dhchap_auth_work);
+    int ret, q;
+

Here I would print a single:
     dev_info(ctrl->device, "re-authenticating controller");

Quoted 6 lines
Quoted 4 lines

This is instead of all the queue re-authentication prints that should be dev_dbg.

Let's avoid doing the per-queue print...

Hmm. Actually the spec allows to use different keys per queue, even though our implementation doesn't. And fmds has struggled to come up with a sane usecase for that.

We don't need to support it, but regardless we should not info print per-queue.

Quoted 1 line

But yes, okay, will be updating it.

Great...

author maintainer
#28 lore raw

PATCH v3 8/12 nvme-auth: Diffie-Hellman key exchange support

In reply to Hannes Reinecke

Implement Diffie-Hellman key exchange using FFDHE groups for NVMe In-Band Authentication.

---
 drivers/nvme/host/Kconfig |   1 +
 drivers/nvme/host/auth.c  | 190 ++++++++++++++++++++++++++++++++++----
 drivers/nvme/host/auth.h  |   8 ++
 3 files changed, 182 insertions(+), 17 deletions(-)
drivers/nvme/host/Kconfig +1 -0
index 97e8412dc42d..3ba46877d447 100644
@@ -90,6 +90,7 @@ config NVME_AUTH
 	select CRYPTO_HMAC
 	select CRYPTO_SHA256
 	select CRYPTO_SHA512
+	select CRYPTO_FFDHE
 	help
 	  This provides support for NVMe over Fabrics In-Band Authentication
 	  for the NVMe over TCP transport.
drivers/nvme/host/auth.c +173 -17

279 lines not on this page · raw message

drivers/nvme/host/auth.h +8 -0
index cf1255f9db6d..aec954e9de1e 100644
@@ -21,5 +21,13 @@ int nvme_auth_hmac_id(const char *hmac_name);
 unsigned char *nvme_auth_extract_secret(unsigned char *dhchap_secret,
 					size_t *dhchap_key_len);
 u8 *nvme_auth_transform_key(u8 *key, size_t key_len, u8 key_hash, char *nqn);
+int nvme_auth_augmented_challenge(u8 hmac_id, u8 *skey, size_t skey_len,
+				  u8 *challenge, u8 *aug, size_t hlen);
+int nvme_auth_gen_privkey(struct crypto_kpp *dh_tfm, int dh_gid);
+int nvme_auth_gen_pubkey(struct crypto_kpp *dh_tfm,
+			 u8 *host_key, size_t host_key_len);
+int nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm,
+				u8 *ctrl_key, size_t ctrl_key_len,
+				u8 *sess_key, size_t sess_key_len);
 
 #endif /* _NVME_AUTH_H */
Signature · 1 line
2.29.2
#29 lore raw

Re: [PATCH 08/12] nvme-auth: Diffie-Hellman key exchange support

I love your patch! Perhaps something to improve:

Hi Hannes,

I love your patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v5.14 next-20210910]
[cannot apply to cryptodev/master crypto/master linux-nvme/for-next] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Hannes-Reinecke/nvme-In-band-authentication-support/20210910-144627
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git bf9f243f23e6623f310ba03fbb14e10ec3a61290
config: powerpc-randconfig-s032-20210911 (attached as .config)
compiler: powerpc64-linux-gcc (GCC) 11.2.0

reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# apt-get install sparse
# sparse version: v0.6.4-dirty
# https://github.com/0day-ci/linux/commit/8b89de04a6eef68be62dcebf344e946c3e6843de
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Hannes-Reinecke/nvme-In-band-authentication-support/20210910-144627
git checkout 8b89de04a6eef68be62dcebf344e946c3e6843de
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=powerpc SHELL=/bin/bash drivers/nvme/host/ drivers/nvme/target/

If you fix the issue, kindly add following tag as appropriate

sparse warnings: (new ones prefixed by >>)

>> drivers/nvme/host/auth.c:235:24: sparse: sparse: Using plain integer as NULL pointer
>> drivers/nvme/host/auth.c:509:26: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __le32 [assigned] [usertype] tl @@     got unsigned long [usertype] tl @@
   drivers/nvme/host/auth.c:509:26: sparse:     expected restricted __le32 [assigned] [usertype] tl
   drivers/nvme/host/auth.c:509:26: sparse:     got unsigned long [usertype] tl
>> drivers/nvme/host/auth.c:537:29: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __le32 [assigned] [usertype] al @@     got unsigned long [usertype] al @@
   drivers/nvme/host/auth.c:537:29: sparse:     expected restricted __le32 [assigned] [usertype] al
   drivers/nvme/host/auth.c:537:29: sparse:     got unsigned long [usertype] al
>> drivers/nvme/host/auth.c:618:54: sparse: sparse: restricted __le16 degrades to integer
>> drivers/nvme/host/auth.c:735:46: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected unsigned long [usertype] size @@     got restricted __le16 [usertype] dhvlen @@
   drivers/nvme/host/auth.c:735:46: sparse:     expected unsigned long [usertype] size
   drivers/nvme/host/auth.c:735:46: sparse:     got restricted __le16 [usertype] dhvlen
>> drivers/nvme/host/auth.c:738:36: sparse: sparse: incorrect type in assignment (different base types) @@     expected int ctrl_key_len @@     got restricted __le16 [usertype] dhvlen @@
   drivers/nvme/host/auth.c:738:36: sparse:     expected int ctrl_key_len
   drivers/nvme/host/auth.c:738:36: sparse:     got restricted __le16 [usertype] dhvlen
   drivers/nvme/host/auth.c:740:28: sparse: sparse: incorrect type in argument 3 (different base types) @@     expected unsigned long [usertype] size @@     got restricted __le16 [usertype] dhvlen @@
   drivers/nvme/host/auth.c:740:28: sparse:     expected unsigned long [usertype] size
   drivers/nvme/host/auth.c:740:28: sparse:     got restricted __le16 [usertype] dhvlen
>> drivers/nvme/host/auth.c:773:22: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __le16 [usertype] dhvlen @@     got int host_key_len @@

drivers/nvme/host/auth.c:773:22: sparse: expected restricted __le16 [usertype] dhvlen
drivers/nvme/host/auth.c:773:22: sparse: got int host_key_len

vim +235 drivers/nvme/host/auth.c

0f7847d23405a2 Hannes Reinecke 2021-09-10  224  
0f7847d23405a2 Hannes Reinecke 2021-09-10  225  u8 *nvme_auth_transform_key(u8 *key, size_t key_len, u8 key_hash, char *nqn)
0f7847d23405a2 Hannes Reinecke 2021-09-10  226  {
0f7847d23405a2 Hannes Reinecke 2021-09-10  227  	const char *hmac_name = nvme_auth_hmac_name(key_hash);
0f7847d23405a2 Hannes Reinecke 2021-09-10  228  	struct crypto_shash *key_tfm;
0f7847d23405a2 Hannes Reinecke 2021-09-10  229  	struct shash_desc *shash;
0f7847d23405a2 Hannes Reinecke 2021-09-10  230  	u8 *transformed_key;
0f7847d23405a2 Hannes Reinecke 2021-09-10  231  	int ret;
0f7847d23405a2 Hannes Reinecke 2021-09-10  232  
0f7847d23405a2 Hannes Reinecke 2021-09-10  233  	/* No key transformation required */
0f7847d23405a2 Hannes Reinecke 2021-09-10  234  	if (key_hash == 0)
0f7847d23405a2 Hannes Reinecke 2021-09-10 @235  		return 0;
0f7847d23405a2 Hannes Reinecke 2021-09-10  236  
0f7847d23405a2 Hannes Reinecke 2021-09-10  237  	hmac_name = nvme_auth_hmac_name(key_hash);
0f7847d23405a2 Hannes Reinecke 2021-09-10  238  	if (!hmac_name) {
0f7847d23405a2 Hannes Reinecke 2021-09-10  239  		pr_warn("Invalid key hash id %d\n", key_hash);
0f7847d23405a2 Hannes Reinecke 2021-09-10  240  		return ERR_PTR(-EKEYREJECTED);
0f7847d23405a2 Hannes Reinecke 2021-09-10  241  	}
0f7847d23405a2 Hannes Reinecke 2021-09-10  242  	key_tfm = crypto_alloc_shash(hmac_name, 0, 0);
0f7847d23405a2 Hannes Reinecke 2021-09-10  243  	if (IS_ERR(key_tfm))
0f7847d23405a2 Hannes Reinecke 2021-09-10  244  		return (u8 *)key_tfm;
0f7847d23405a2 Hannes Reinecke 2021-09-10  245  
0f7847d23405a2 Hannes Reinecke 2021-09-10  246  	shash = kmalloc(sizeof(struct shash_desc) +
0f7847d23405a2 Hannes Reinecke 2021-09-10  247  			crypto_shash_descsize(key_tfm),
0f7847d23405a2 Hannes Reinecke 2021-09-10  248  			GFP_KERNEL);
0f7847d23405a2 Hannes Reinecke 2021-09-10  249  	if (!shash) {
0f7847d23405a2 Hannes Reinecke 2021-09-10  250  		crypto_free_shash(key_tfm);
0f7847d23405a2 Hannes Reinecke 2021-09-10  251  		return ERR_PTR(-ENOMEM);
0f7847d23405a2 Hannes Reinecke 2021-09-10  252  	}
0f7847d23405a2 Hannes Reinecke 2021-09-10  253  	transformed_key = kzalloc(crypto_shash_digestsize(key_tfm), GFP_KERNEL);
0f7847d23405a2 Hannes Reinecke 2021-09-10  254  	if (!transformed_key) {
0f7847d23405a2 Hannes Reinecke 2021-09-10  255  		ret = -ENOMEM;
0f7847d23405a2 Hannes Reinecke 2021-09-10  256  		goto out_free_shash;
0f7847d23405a2 Hannes Reinecke 2021-09-10  257  	}
0f7847d23405a2 Hannes Reinecke 2021-09-10  258  
0f7847d23405a2 Hannes Reinecke 2021-09-10  259  	shash->tfm = key_tfm;
0f7847d23405a2 Hannes Reinecke 2021-09-10  260  	ret = crypto_shash_setkey(key_tfm, key, key_len);
0f7847d23405a2 Hannes Reinecke 2021-09-10  261  	if (ret < 0)
0f7847d23405a2 Hannes Reinecke 2021-09-10  262  		goto out_free_shash;
0f7847d23405a2 Hannes Reinecke 2021-09-10  263  	ret = crypto_shash_init(shash);
0f7847d23405a2 Hannes Reinecke 2021-09-10  264  	if (ret < 0)
0f7847d23405a2 Hannes Reinecke 2021-09-10  265  		goto out_free_shash;
0f7847d23405a2 Hannes Reinecke 2021-09-10  266  	ret = crypto_shash_update(shash, nqn, strlen(nqn));
0f7847d23405a2 Hannes Reinecke 2021-09-10  267  	if (ret < 0)
0f7847d23405a2 Hannes Reinecke 2021-09-10  268  		goto out_free_shash;
0f7847d23405a2 Hannes Reinecke 2021-09-10  269  	ret = crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
0f7847d23405a2 Hannes Reinecke 2021-09-10  270  	if (ret < 0)
0f7847d23405a2 Hannes Reinecke 2021-09-10  271  		goto out_free_shash;
0f7847d23405a2 Hannes Reinecke 2021-09-10  272  	ret = crypto_shash_final(shash, transformed_key);
0f7847d23405a2 Hannes Reinecke 2021-09-10  273  out_free_shash:
0f7847d23405a2 Hannes Reinecke 2021-09-10  274  	kfree(shash);
0f7847d23405a2 Hannes Reinecke 2021-09-10  275  	crypto_free_shash(key_tfm);
0f7847d23405a2 Hannes Reinecke 2021-09-10  276  	if (ret < 0) {
0f7847d23405a2 Hannes Reinecke 2021-09-10  277  		kfree_sensitive(transformed_key);
0f7847d23405a2 Hannes Reinecke 2021-09-10  278  		return ERR_PTR(ret);
0f7847d23405a2 Hannes Reinecke 2021-09-10  279  	}
0f7847d23405a2 Hannes Reinecke 2021-09-10  280  	return transformed_key;
0f7847d23405a2 Hannes Reinecke 2021-09-10  281  }
0f7847d23405a2 Hannes Reinecke 2021-09-10  282  EXPORT_SYMBOL_GPL(nvme_auth_transform_key);
0f7847d23405a2 Hannes Reinecke 2021-09-10  283  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]

author maintainer
#30 lore raw

PATCH v3 9/12 nvmet: Parse fabrics commands on all queues

In reply to Hannes Reinecke

Fabrics 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
index b8425fa34300..6e253c3c5e0f 100644
@@ -943,6 +943,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
author maintainer
#32 lore raw

PATCH v3 10/12 nvmet: Implement basic In-Band Authentication

In reply to Hannes Reinecke

Implement NVMe-oF In-Band authentication according to NVMe TPAR 8006. This patch adds two additional configfs entries 'dhchap_key' and 'dhchap_hash' to the 'host' configfs directory. The 'dhchap_key' needs to be
in the ASCII format as specified in NVMe 2.0 section 8.13.5.8 'Secret representation'.
'dhchap_hash' is taken from the hash specified in the ASCII representation of the key, or defaults to 'hmac(sha256)' if no key transformation has been specified.

---
 drivers/nvme/target/Kconfig            |  11 +
 drivers/nvme/target/Makefile           |   1 +
 drivers/nvme/target/admin-cmd.c        |   4 +
 drivers/nvme/target/auth.c             | 301 ++++++++++++++++
 drivers/nvme/target/configfs.c         |  71 +++-
 drivers/nvme/target/core.c             |   8 +
 drivers/nvme/target/fabrics-cmd-auth.c | 464 +++++++++++++++++++++++++
 drivers/nvme/target/fabrics-cmd.c      |  30 +-
 drivers/nvme/target/nvmet.h            |  63 ++++
 9 files changed, 950 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 +11 -0
index 973561c93888..70f3c385fc9f 100644
@@ -83,3 +83,14 @@ 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_HMAC
+	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
index 9837e580fa7e..c66820102493 100644
@@ -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
index aa6d84d8848e..868d65c869cd 100644
@@ -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 +301 -0

301 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
index 6e253c3c5e0f..afe7ca1f9175 100644
@@ -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) {
 		/*
@@ -1268,6 +1269,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;
 }
 
@@ -1459,6 +1465,8 @@ static void nvmet_ctrl_free(struct kref *ref)
 	flush_work(&ctrl->async_event_work);
 	cancel_work_sync(&ctrl->fatal_err_work);
 
+	nvmet_destroy_auth(ctrl);
+
 	ida_simple_remove(&cntlid_ida, ctrl->cntlid);
 
 	nvmet_async_events_free(ctrl);
drivers/nvme/target/fabrics-cmd-auth.c new file +464 -0

464 lines not on this page · raw message

drivers/nvme/target/fabrics-cmd.c +28 -2
index 7d0454cee920..d5a4a9a68ee1 100644
@@ -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);
@@ -173,6 +181,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;
@@ -215,17 +224,31 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req)
 
 	uuid_copy(&ctrl->hostid, &d->hostid);
 
+	ret = nvmet_setup_auth(ctrl);
+	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:
@@ -285,6 +308,9 @@ static void nvmet_execute_io_connect(struct nvmet_req *req)
 	req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);
 
 	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 +63 -0

85 lines not on this page · raw message

Signature · 1 line
2.29.2
#33 lore raw

Re: [PATCH 10/12] nvmet: Implement basic In-Band Authentication

I love your patch! Perhaps something to improve:

Hi Hannes,

I love your patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v5.14 next-20210910]
[cannot apply to cryptodev/master crypto/master linux-nvme/for-next] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Hannes-Reinecke/nvme-In-band-authentication-support/20210910-144627
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git bf9f243f23e6623f310ba03fbb14e10ec3a61290
config: powerpc-randconfig-s032-20210911 (attached as .config)
compiler: powerpc64-linux-gcc (GCC) 11.2.0

reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# apt-get install sparse
# sparse version: v0.6.4-dirty
# https://github.com/0day-ci/linux/commit/c117a1b0b131e4dfcd82ae5934b2659248acb372
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Hannes-Reinecke/nvme-In-band-authentication-support/20210910-144627
git checkout c117a1b0b131e4dfcd82ae5934b2659248acb372
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=powerpc SHELL=/bin/bash drivers/nvme/target/

If you fix the issue, kindly add following tag as appropriate

sparse warnings: (new ones prefixed by >>)

>> drivers/nvme/target/fabrics-cmd-auth.c:20:30: sparse: sparse: invalid assignment: |=
>> drivers/nvme/target/fabrics-cmd-auth.c:20:30: sparse:    left side has type restricted __le32
>> drivers/nvme/target/fabrics-cmd-auth.c:20:30: sparse:    right side has type int
>> drivers/nvme/target/fabrics-cmd-auth.c:359:20: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __le16 [usertype] t_id @@     got restricted __le32 [usertype] @@

drivers/nvme/target/fabrics-cmd-auth.c:359:20: sparse: expected restricted __le16 [usertype] t_id
drivers/nvme/target/fabrics-cmd-auth.c:359:20: sparse: got restricted __le32 [usertype]

vim +20 drivers/nvme/target/fabrics-cmd-auth.c

    14	
    15	void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
    16	{
    17		/* Initialize in-band authentication */
    18		req->sq->authenticated = false;
    19		req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
  > 20		req->cqe->result.u32 |= 0x2 << 16;
    21	}
    22	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]

maintainer
#34 lore raw

Re: [PATCH 10/12] nvmet: Implement basic In-Band Authentication

In reply to Hannes Reinecke

drivers/nvme/target/auth.c line 65

Quoted 11 lines
drivers/nvme/target/auth.c new file +10 -0
+int nvmet_setup_auth(struct nvmet_ctrl *ctrl)
+{
+	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;

+ if (ctrl->subsys->allow_any_host)
+ goto out_unlock;

maintainer
#35 lore raw

Re: [PATCH 10/12] nvmet: Implement basic In-Band Authentication

In reply to Hannes Reinecke
Quoted 53 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +52 -0
+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 line 201

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)
+		goto done_failure1;
+	if (data->auth_type == NVME_AUTH_COMMON_MESSAGES) {
+		if (data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE) {
+			/* Restart negotiation */
+			pr_debug("%s: ctrl %d qid %d reset negotiation\n", __func__,
+				 ctrl->cntlid, req->sq->qid);

This is the point where you need to reset also auth config as this may have changed and the host will not create a new controller but rather re-authenticate on the existing controller.

i.e.

+ if (!req->sq->qid) {
+ nvmet_destroy_auth(ctrl);
+ if (nvmet_setup_auth(ctrl) < 0) {
+ pr_err("Failed to setup re-authentication\n");
+ goto done_failure1;
+ }
+ }

Quoted 5 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +5 -0
+			req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
+		} else if (data->auth_id != req->sq->dhchap_step)
+			goto done_failure1;
+		/* Validate negotiation parameters */
+		status = nvmet_auth_negotiate(req, d);/
author maintainer
#36 lore raw

Re: [PATCH 10/12] nvmet: Implement basic In-Band Authentication

Quoted 71 lines · Sagi Grimberg
Quoted 54 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +9 -0
+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;
+    }

drivers/nvme/target/fabrics-cmd-auth.c new file +15 -0
+
+    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 line 201

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)
+        goto done_failure1;
+    if (data->auth_type == NVME_AUTH_COMMON_MESSAGES) {
+        if (data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE) {
+            /* Restart negotiation */
+            pr_debug("%s: ctrl %d qid %d reset negotiation\n", __func__,
+                 ctrl->cntlid, req->sq->qid);

This is the point where you need to reset also auth config as this may have changed and the host will not create a new controller but rather re-authenticate on the existing controller.

i.e.

Quoted 11 lines

+                       if (!req->sq->qid) {
+                               nvmet_destroy_auth(ctrl); +                               if (nvmet_setup_auth(ctrl) < 0) { +                                       pr_err("Failed to setup re-authentication\n");
+                                       goto done_failure1; +                               }
+                       }

Not sure. We have two paths how re-authentication can be triggered.
The one is from the host, which sends a 'negotiate' command to the controller (ie this path). Then nothing on the controller has changed, and we just need to ensure that we restart negotiation.
IE we should _not_ reset the authentication (as that would also remove the controller keys, which haven't changed). We should just ensure that all ephemeral data is regenerated. But that should be handled in-line, and I _think_ I have covered all of that. The other path to trigger re-authentication is when changing values on the controller via configfs. Then sure we need to reset the controller data, and trigger reauthentication.
And there I do agree, that path isn't fully implemented / tested.
But should be started whenever the configfs values change.

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
author maintainer
#37 lore raw

Re: [PATCH 10/12] nvmet: Implement basic In-Band Authentication

Quoted 85 lines · Hannes Reinecke
Quoted 72 lines · Sagi Grimberg
Quoted 63 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +9 -0
+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;
+    }

drivers/nvme/target/fabrics-cmd-auth.c new file +24 -0
+
+    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__,
+         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)
+        goto done_failure1;
+    if (data->auth_type == NVME_AUTH_COMMON_MESSAGES) {
+        if (data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE) {
+            /* Restart negotiation */
+            pr_debug("%s: ctrl %d qid %d reset negotiation\n", 
Quoted 2 lines

__func__,
+                 ctrl->cntlid, req->sq->qid);

This is the point where you need to reset also auth config as this may have changed and the host will not create a new controller but rather re-authenticate on the existing controller.

i.e.

Quoted 11 lines

+                       if (!req->sq->qid) {
+                               nvmet_destroy_auth(ctrl); +                               if (nvmet_setup_auth(ctrl) < 0) { +                                       pr_err("Failed to setup re-authentication\n");
+                                       goto done_failure1; +                               }
+                       }

Quoted 7 lines

Not sure. We have two paths how re-authentication can be triggered.
The one is from the host, which sends a 'negotiate' command to the controller (ie this path).  Then nothing on the controller has changed, and we just need to ensure that we restart negotiation.
IE we should _not_ reset the authentication (as that would also remove the controller keys, which haven't changed). We should just ensure that all ephemeral data is regenerated. But that should be handled in-line, and I _think_ I have covered all of that. The other path to trigger re-authentication is when changing values on the controller via configfs. Then sure we need to reset the controller data, and trigger reauthentication.
And there I do agree, that path isn't fully implemented / tested.
But should be started whenever the configfs values change.

Actually, having re-read the spec I'm not sure if the second path is correct.
As per spec only the _host_ can trigger re-authentication. There is no provision for the controller to trigger re-authentication, and given that re-auth is a soft-state anyway (ie the current authentication stays valid until re-auth enters a final state) I _think_ we should be good with the current implementation, where we can change the controller keys via configfs, but they will only become active once the host triggers
re-authentication.

And indeed, that's the only way how it could work, otherwise it'll be tricky to change keys in a running connection.
If we were to force renegotiation when changing controller keys we would immediately fail the connection, as we cannot guarantee that controller _and_ host keys are changed at the same time.

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
maintainer
#38 lore raw

Re: [PATCH 10/12] nvmet: Implement basic In-Band Authentication

Quoted 93 lines · Hannes Reinecke
Quoted 85 lines · Hannes Reinecke
Quoted 72 lines · Sagi Grimberg
Quoted 63 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +9 -0
+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;
+    }

drivers/nvme/target/fabrics-cmd-auth.c new file +24 -0
+
+    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__,
+         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)
+        goto done_failure1;
+    if (data->auth_type == NVME_AUTH_COMMON_MESSAGES) {
+        if (data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE) {
+            /* Restart negotiation */
+            pr_debug("%s: ctrl %d qid %d reset negotiation\n", 
Quoted 2 lines

__func__,
+                 ctrl->cntlid, req->sq->qid);

This is the point where you need to reset also auth config as this may have changed and the host will not create a new controller but rather re-authenticate on the existing controller.

i.e.

Quoted 11 lines

+                       if (!req->sq->qid) {
+                               nvmet_destroy_auth(ctrl); +                               if (nvmet_setup_auth(ctrl) < 0) { +                                       pr_err("Failed to setup re-authentication\n");
+                                       goto done_failure1; +                               }
+                       }

Quoted 7 lines

Not sure. We have two paths how re-authentication can be triggered.
The one is from the host, which sends a 'negotiate' command to the controller (ie this path).  Then nothing on the controller has changed, and we just need to ensure that we restart negotiation.
IE we should _not_ reset the authentication (as that would also remove the controller keys, which haven't changed). We should just ensure that all ephemeral data is regenerated. But that should be handled in-line, and I _think_ I have covered all of that. The other path to trigger re-authentication is when changing values on the controller via configfs. Then sure we need to reset the controller data, and trigger reauthentication.
And there I do agree, that path isn't fully implemented / tested.
But should be started whenever the configfs values change.

Quoted 4 lines

Actually, having re-read the spec I'm not sure if the second path is correct.
As per spec only the _host_ can trigger re-authentication. There is no provision for the controller to trigger re-authentication, and given that re-auth is a soft-state anyway (ie the current authentication stays valid until re-auth enters a final state) I _think_ we should be good with the current implementation, where we can change the controller keys via configfs, but they will only become active once the host triggers
re-authentication.

Agree, so the proposed addition is good with you?

Quoted 2 lines

And indeed, that's the only way how it could work, otherwise it'll be tricky to change keys in a running connection.
If we were to force renegotiation when changing controller keys we would immediately fail the connection, as we cannot guarantee that controller _and_ host keys are changed at the same time.

Exactly, changing the hostkey in the controller must not trigger re-auth, the host will remain connected and operational as it authenticated before. As the host re-authenticates or reconnect it needs to authenticate against the new key.

author maintainer
#39 lore raw

Re: [PATCH 10/12] nvmet: Implement basic In-Band Authentication

Quoted 115 lines · Sagi Grimberg
Quoted 101 lines · Hannes Reinecke
Quoted 86 lines · Hannes Reinecke
Quoted 73 lines · Sagi Grimberg
Quoted 64 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +9 -0
+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;
+    }

drivers/nvme/target/fabrics-cmd-auth.c new file +24 -0
+
+    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__,
+         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)
+        goto done_failure1;
+    if (data->auth_type == NVME_AUTH_COMMON_MESSAGES) {
+        if (data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE) {
+            /* Restart negotiation */
+            pr_debug("%s: ctrl %d qid %d reset negotiation\n",
Quoted 2 lines

__func__,
+                 ctrl->cntlid, req->sq->qid);

This is the point where you need to reset also auth config as this may have changed and the host will not create a new controller but rather re-authenticate on the existing controller.

i.e.

Quoted 11 lines

+                       if (!req->sq->qid) {
+                               nvmet_destroy_auth(ctrl); +                               if (nvmet_setup_auth(ctrl) < 0) { +                                       pr_err("Failed to setup re-authentication\n");
+                                       goto done_failure1; +                               }
+                       }

Quoted 14 lines

Not sure. We have two paths how re-authentication can be triggered. The one is from the host, which sends a 'negotiate' command to the controller (ie this path).  Then nothing on the controller has changed, and we just need to ensure that we restart negotiation. IE we should _not_ reset the authentication (as that would also remove the controller keys, which haven't changed). We should just ensure that all ephemeral data is regenerated. But that should be handled in-line, and I _think_ I have covered all of that. The other path to trigger re-authentication is when changing values on the controller via configfs. Then sure we need to reset the controller data, and trigger reauthentication.
And there I do agree, that path isn't fully implemented / tested. But should be started whenever the configfs values change.

Quoted 10 lines

Actually, having re-read the spec I'm not sure if the second path is correct.
As per spec only the _host_ can trigger re-authentication. There is no provision for the controller to trigger re-authentication, and given that re-auth is a soft-state anyway (ie the current authentication stays valid until re-auth enters a final state) I _think_ we should be good with the current implementation, where we can change the controller keys
via configfs, but they will only become active once the host triggers re-authentication.

Quoted 2 lines

Agree, so the proposed addition is good with you?

Why would we need it?
I do agree there's a bit missing for removing the old shash_tfm if there is a hash-id mismatch, but why would we need to reset the entire authentication?
The important (ie cryptographically relevant) bits are cleared in nvmet_auth_sq_free(), and they are cleared after authentication is completed.
So why would we need to reset keys and TFMs?

Quoted 10 lines
Quoted 5 lines

And indeed, that's the only way how it could work, otherwise it'll be tricky to change keys in a running connection.
If we were to force renegotiation when changing controller keys we would immediately fail the connection, as we cannot guarantee that controller _and_ host keys are changed at the same time.

Exactly, changing the hostkey in the controller must not trigger re-auth, the host will remain connected and operational as it authenticated before. As the host re-authenticates or reconnect it needs to authenticate against the new key.

Right. I'll be adding a comment to the configfs functions to the effect.

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
maintainer
#40 lore raw

Re: [PATCH 10/12] nvmet: Implement basic In-Band Authentication

Quoted 12 lines
Quoted 12 lines
Quoted 11 lines

Actually, having re-read the spec I'm not sure if the second path is correct.
As per spec only the _host_ can trigger re-authentication. There is no provision for the controller to trigger re-authentication, and given that re-auth is a soft-state anyway (ie the current authentication stays valid until re-auth enters a final state) I _think_ we should be good with the current implementation, where we can change the controller keys
via configfs, but they will only become active once the host triggers re-authentication.

Quoted 6 lines
Quoted 2 lines

Agree, so the proposed addition is good with you?

Why would we need it?
I do agree there's a bit missing for removing the old shash_tfm if there is a hash-id mismatch, but why would we need to reset the entire authentication?

Just need to update the new host dhchap_key from the host at this point as the host is doing a re-authentication. I agree we don't need a big hammer but we do need the re-authentication to not access old host dhchap_key.

author maintainer
#41 lore raw

Re: [PATCH 10/12] nvmet: Implement basic In-Band Authentication

Quoted 19 lines · Sagi Grimberg
Quoted 12 lines
Quoted 12 lines
Quoted 11 lines

Actually, having re-read the spec I'm not sure if the second path is correct.
As per spec only the _host_ can trigger re-authentication. There is no provision for the controller to trigger re-authentication, and given that re-auth is a soft-state anyway (ie the current authentication stays valid until re-auth enters a final state) I _think_ we should be good with the current implementation, where we can change the controller keys
via configfs, but they will only become active once the host triggers re-authentication.

Quoted 6 lines
Quoted 2 lines

Agree, so the proposed addition is good with you?

Why would we need it?
I do agree there's a bit missing for removing the old shash_tfm if there is a hash-id mismatch, but why would we need to reset the entire authentication?

Quoted 4 lines

Just need to update the new host dhchap_key from the host at this point as the host is doing a re-authentication. I agree we don't need a big hammer but we do need the re-authentication to not access old host dhchap_key.

Sure. And, upon reviewing, I guess you are right; will be including your snippet. For the next round :-)

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
author maintainer
#42 lore raw

PATCH v3 11/12 nvmet-auth: Diffie-Hellman key exchange support

In reply to Hannes Reinecke

Implement Diffie-Hellman key exchange using FFDHE groups for NVMe In-Band Authentication.
This patch adds a new host configfs attribute 'dhchap_dhgroup' to select the FFDHE group to use.

---
 drivers/nvme/target/Kconfig            |   1 +
 drivers/nvme/target/auth.c             | 148 ++++++++++++++++++++++++-
 drivers/nvme/target/configfs.c         |  31 ++++++
 drivers/nvme/target/fabrics-cmd-auth.c |  30 ++++-
 drivers/nvme/target/nvmet.h            |   6 +
 5 files changed, 209 insertions(+), 7 deletions(-)
drivers/nvme/target/Kconfig +1 -0
index 70f3c385fc9f..2e41d70fd881 100644
@@ -90,6 +90,7 @@ config NVME_TARGET_AUTH
 	select CRYPTO_HMAC
 	select CRYPTO_SHA256
 	select CRYPTO_SHA512
+	select CRYPTO_FFDHE
 	help
 	  This enables support for NVMe over Fabrics In-band Authentication
 
drivers/nvme/target/auth.c +144 -4

175 lines not on this page · raw message

drivers/nvme/target/configfs.c +31 -0
index 7c13810a637f..4aa554982995 100644
@@ -1713,9 +1713,40 @@ static ssize_t nvmet_host_dhchap_hash_store(struct config_item *item,
 
 CONFIGFS_ATTR(nvmet_host_, dhchap_hash);
 
+static ssize_t nvmet_host_dhchap_dhgroup_show(struct config_item *item,
+		char *page)
+{
+	struct nvmet_host *host = to_host(item);
+	const char *dhgroup = nvme_auth_dhgroup_name(host->dhchap_dhgroup_id);
+
+	return sprintf(page, "%s\n", dhgroup ? dhgroup : "none");
+}
+
+static ssize_t nvmet_host_dhchap_dhgroup_store(struct config_item *item,
+		const char *page, size_t count)
+{
+	struct nvmet_host *host = to_host(item);
+	int dhgroup_id;
+
+	dhgroup_id = nvme_auth_dhgroup_id(page);
+	if (dhgroup_id < 0)
+		return -EINVAL;
+	if (dhgroup_id != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
+		const char *kpp = nvme_auth_dhgroup_kpp(dhgroup_id);
+
+		if (!crypto_has_kpp(kpp, 0, 0))
+			return -EINVAL;
+	}
+	host->dhchap_dhgroup_id = dhgroup_id;
+	return count;
+}
+
+CONFIGFS_ATTR(nvmet_host_, dhchap_dhgroup);
+
 static struct configfs_attribute *nvmet_host_attrs[] = {
 	&nvmet_host_attr_dhchap_key,
 	&nvmet_host_attr_dhchap_hash,
+	&nvmet_host_attr_dhchap_dhgroup,
 	NULL,
 };
 #endif /* CONFIG_NVME_TARGET_AUTH */
drivers/nvme/target/fabrics-cmd-auth.c +27 -3
index ab9dfc06bac0..2f1b95098917 100644
@@ -64,13 +64,24 @@ static u16 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
 			null_dh = dhgid;
 			continue;
 		}
+		if (ctrl->dh_tfm && ctrl->dh_gid == dhgid) {
+			pr_debug("%s: ctrl %d qid %d: reusing existing DH group %d\n",
+				 __func__, ctrl->cntlid, req->sq->qid, dhgid);
+			break;
+		}
+		if (nvmet_setup_dhgroup(ctrl, dhgid) < 0)
+			continue;
+		if (nvme_auth_gen_privkey(ctrl->dh_tfm, dhgid) == 0)
+			break;
+		crypto_free_kpp(ctrl->dh_tfm);
+		ctrl->dh_tfm = NULL;
+		ctrl->dh_gid = 0;
 	}
-	if (null_dh < 0) {
+	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;
 	}
-	ctrl->dh_gid = null_dh;
 	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);
@@ -91,7 +102,11 @@ static u16 nvmet_auth_reply(struct nvmet_req *req, void *d)
 		return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
 
 	if (data->dhvlen) {
-		return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+		if (!ctrl->dh_tfm)
+			return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+		if (nvmet_auth_ctrl_sesskey(req, data->rval + 2 * data->hl,
+					    data->dhvlen) < 0)
+			return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
 	}
 
 	response = kmalloc(data->hl, GFP_KERNEL);
@@ -232,6 +247,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
 			NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
 		goto done_kfree;
 	}
+
 	switch (data->auth_id) {
 	case NVME_AUTH_DHCHAP_MESSAGE_REPLY:
 		status = nvmet_auth_reply(req, d);
@@ -303,6 +319,8 @@ static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al)
 	int hash_len = crypto_shash_digestsize(ctrl->shash_tfm);
 	int data_size = sizeof(*d) + hash_len;
 
+	if (ctrl->dh_tfm)
+		data_size += ctrl->dh_keysize;
 	if (al < data_size) {
 		pr_debug("%s: buffer too small (al %d need %d)\n", __func__,
 			 al, data_size);
@@ -321,6 +339,12 @@ static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al)
 		return -ENOMEM;
 	get_random_bytes(req->sq->dhchap_c1, data->hl);
 	memcpy(data->cval, req->sq->dhchap_c1, data->hl);
+	if (ctrl->dh_tfm) {
+		data->dhgid = ctrl->dh_gid;
+		data->dhvlen = ctrl->dh_keysize;
+		ret = nvmet_auth_ctrl_exponential(req, data->cval + data->hl,
+						  data->dhvlen);
+	}
 	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);
drivers/nvme/target/nvmet.h +6 -0
index ab25f9e18027..d0849404f398 100644
@@ -227,6 +227,7 @@ struct nvmet_ctrl {
 	size_t			dhchap_key_len;
 	struct crypto_shash	*shash_tfm;
 	u8			shash_id;
+	struct crypto_kpp	*dh_tfm;
 	u32			dh_gid;
 	u32			dh_keysize;
 #endif
@@ -694,6 +695,7 @@ int nvmet_setup_auth(struct nvmet_ctrl *ctrl);
 void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req);
 void nvmet_destroy_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);
@@ -703,6 +705,10 @@ 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)
 {
Signature · 1 line
2.29.2
#43 lore raw

Re: [PATCH 11/12] nvmet-auth: Diffie-Hellman key exchange support

I love your patch! Perhaps something to improve:

Hi Hannes,

I love your patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v5.14 next-20210910]
[cannot apply to cryptodev/master crypto/master linux-nvme/for-next] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Hannes-Reinecke/nvme-In-band-authentication-support/20210910-144627
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git bf9f243f23e6623f310ba03fbb14e10ec3a61290
config: powerpc-randconfig-s032-20210911 (attached as .config)
compiler: powerpc64-linux-gcc (GCC) 11.2.0

reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# apt-get install sparse
# sparse version: v0.6.4-dirty
# https://github.com/0day-ci/linux/commit/3439accdd50a505d2d15d42f298b5e2d727a5607
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Hannes-Reinecke/nvme-In-band-authentication-support/20210910-144627 git checkout 3439accdd50a505d2d15d42f298b5e2d727a5607
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' ARCH=powerpc

If you fix the issue, kindly add following tag as appropriate

sparse warnings: (new ones prefixed by >>)
drivers/nvme/target/fabrics-cmd-auth.c:20:30: sparse: sparse: invalid assignment: |=
drivers/nvme/target/fabrics-cmd-auth.c:20:30: sparse: left side has type restricted __le32
drivers/nvme/target/fabrics-cmd-auth.c:20:30: sparse: right side has type int

>> drivers/nvme/target/fabrics-cmd-auth.c:108:49: sparse: sparse: incorrect type in argument 3 (different base types) @@     expected int buf_size @@     got restricted __le16 [usertype] dhvlen @@
   drivers/nvme/target/fabrics-cmd-auth.c:108:49: sparse:     expected int buf_size
   drivers/nvme/target/fabrics-cmd-auth.c:108:49: sparse:     got restricted __le16 [usertype] dhvlen
>> drivers/nvme/target/fabrics-cmd-auth.c:344:30: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __le16 [usertype] dhvlen @@     got unsigned int [usertype] dh_keysize @@

drivers/nvme/target/fabrics-cmd-auth.c:344:30: sparse: expected restricted __le16 [usertype] dhvlen
drivers/nvme/target/fabrics-cmd-auth.c:344:30: sparse: got unsigned int [usertype] dh_keysize
drivers/nvme/target/fabrics-cmd-auth.c:346:55: sparse: sparse: incorrect type in argument 3 (different base types) @@ expected int buf_size @@ got restricted __le16 [usertype] dhvlen @@
drivers/nvme/target/fabrics-cmd-auth.c:346:55: sparse: expected int buf_size
drivers/nvme/target/fabrics-cmd-auth.c:346:55: sparse: got restricted __le16 [usertype] dhvlen
drivers/nvme/target/fabrics-cmd-auth.c:383:20: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __le16 [usertype] t_id @@ got restricted __le32 [usertype] @@
drivers/nvme/target/fabrics-cmd-auth.c:383:20: sparse: expected restricted __le16 [usertype] t_id
drivers/nvme/target/fabrics-cmd-auth.c:383:20: sparse: got restricted __le32 [usertype]

vim +108 drivers/nvme/target/fabrics-cmd-auth.c

    14	
    15	void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
    16	{
    17		/* Initialize in-band authentication */
    18		req->sq->authenticated = false;
    19		req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
  > 20		req->cqe->result.u32 |= 0x2 << 16;
    21	}
    22	
    23	static u16 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
    24	{
    25		struct nvmet_ctrl *ctrl = req->sq->ctrl;
    26		struct nvmf_auth_dhchap_negotiate_data *data = d;
    27		int i, hash_id, null_dh = -1;
    28	
    29		pr_debug("%s: ctrl %d qid %d: data sc_d %d napd %d authid %d halen %d dhlen %d\n",
    30			 __func__, ctrl->cntlid, req->sq->qid,
    31			 data->sc_c, data->napd, data->auth_protocol[0].dhchap.authid,
    32			 data->auth_protocol[0].dhchap.halen,
    33			 data->auth_protocol[0].dhchap.dhlen);
    34		req->sq->dhchap_tid = le16_to_cpu(data->t_id);
    35		if (data->sc_c)
    36			return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH;
    37	
    38		if (data->napd != 1)
    39			return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
    40	
    41		if (data->auth_protocol[0].dhchap.authid !=
    42		    NVME_AUTH_DHCHAP_AUTH_ID)
    43			return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
    44	
    45		hash_id = 0;
    46		for (i = 0; i < data->auth_protocol[0].dhchap.halen; i++) {
    47			if (ctrl->shash_id != data->auth_protocol[0].dhchap.idlist[i])
    48				continue;
    49			hash_id = ctrl->shash_id;
    50			break;
    51		}
    52		if (hash_id == 0) {
    53			pr_debug("%s: ctrl %d qid %d: no usable hash found\n",
    54				 __func__, ctrl->cntlid, req->sq->qid);
    55			return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
    56		}
    57	
    58		for (i = data->auth_protocol[0].dhchap.halen;
    59		     i < data->auth_protocol[0].dhchap.halen +
    60			     data->auth_protocol[0].dhchap.dhlen; i++) {
    61			int dhgid = data->auth_protocol[0].dhchap.idlist[i];
    62	
    63			if (dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL) {
    64				null_dh = dhgid;
    65				continue;
    66			}
    67			if (ctrl->dh_tfm && ctrl->dh_gid == dhgid) {
    68				pr_debug("%s: ctrl %d qid %d: reusing existing DH group %d\n",
    69					 __func__, ctrl->cntlid, req->sq->qid, dhgid);
    70				break;
    71			}
    72			if (nvmet_setup_dhgroup(ctrl, dhgid) < 0)
    73				continue;
    74			if (nvme_auth_gen_privkey(ctrl->dh_tfm, dhgid) == 0)
    75				break;
    76			crypto_free_kpp(ctrl->dh_tfm);
    77			ctrl->dh_tfm = NULL;
    78			ctrl->dh_gid = 0;
    79		}
    80		if (!ctrl->dh_tfm && null_dh < 0) {
    81			pr_debug("%s: ctrl %d qid %d: no DH group selected\n",
    82				 __func__, ctrl->cntlid, req->sq->qid);
    83			return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
    84		}
    85		pr_debug("%s: ctrl %d qid %d: DH group %s (%d)\n",
    86			 __func__, ctrl->cntlid, req->sq->qid,
    87			 nvme_auth_dhgroup_name(ctrl->dh_gid), ctrl->dh_gid);
    88		return 0;
    89	}
    90	
    91	static u16 nvmet_auth_reply(struct nvmet_req *req, void *d)
    92	{
    93		struct nvmet_ctrl *ctrl = req->sq->ctrl;
    94		struct nvmf_auth_dhchap_reply_data *data = d;
    95		int hash_len = crypto_shash_digestsize(ctrl->shash_tfm);
    96		u8 *response;
    97	
    98		pr_debug("%s: ctrl %d qid %d: data hl %d cvalid %d dhvlen %d\n",
    99			 __func__, ctrl->cntlid, req->sq->qid,
   100			 data->hl, data->cvalid, data->dhvlen);
   101		if (data->hl != hash_len)
   102			return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
   103	
   104		if (data->dhvlen) {
   105			if (!ctrl->dh_tfm)
   106				return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
   107			if (nvmet_auth_ctrl_sesskey(req, data->rval + 2 * data->hl,
 > 108						    data->dhvlen) < 0)

109 return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
110 }
111
112 response = kmalloc(data->hl, GFP_KERNEL);
113 if (!response)
114 return NVME_AUTH_DHCHAP_FAILURE_FAILED;
115
116 if (nvmet_auth_host_hash(req, response, data->hl) < 0) {
117 pr_debug("ctrl %d qid %d DH-HMAC-CHAP hash failed\n",
118 ctrl->cntlid, req->sq->qid);
119 kfree(response);
120 return NVME_AUTH_DHCHAP_FAILURE_FAILED;
121 }
122
123 if (memcmp(data->rval, response, data->hl)) {
124 pr_info("ctrl %d qid %d DH-HMAC-CHAP response mismatch\n",
125 ctrl->cntlid, req->sq->qid);
126 kfree(response);
127 return NVME_AUTH_DHCHAP_FAILURE_FAILED;
128 }
129 kfree(response);
130 pr_info("ctrl %d qid %d DH-HMAC-CHAP host authenticated\n",
131 ctrl->cntlid, req->sq->qid);
132 if (data->cvalid) {
133 req->sq->dhchap_c2 = kmalloc(data->hl, GFP_KERNEL);
134 if (!req->sq->dhchap_c2)
135 return NVME_AUTH_DHCHAP_FAILURE_FAILED;
136 memcpy(req->sq->dhchap_c2, data->rval + data->hl, data->hl);
137
138 pr_debug("ctrl %d qid %d challenge %*ph\n",
139 ctrl->cntlid, req->sq->qid, data->hl,
140 req->sq->dhchap_c2);
141 req->sq->dhchap_s2 = le32_to_cpu(data->seqnum);
142 } else
143 req->sq->dhchap_c2 = NULL;
144
145 return 0;
146 }
147

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

author maintainer
#44 lore raw

PATCH v3 12/12 nvmet-auth: expire authentication sessions

In reply to Hannes Reinecke

Each authentication step is required to be completed within the KATO interval (or two minutes if not set). So add a workqueue function to reset the transaction ID and the expected next protocol step; this will automatically the next authentication command referring to the terminated authentication.

---
 drivers/nvme/target/auth.c             |  1 +
 drivers/nvme/target/fabrics-cmd-auth.c | 20 +++++++++++++++++++-
 drivers/nvme/target/nvmet.h            |  1 +
 3 files changed, 21 insertions(+), 1 deletion(-)
drivers/nvme/target/auth.c +1 -0
index fe44593a37f8..c7c62ba089da 100644
@@ -197,6 +197,7 @@ int nvmet_setup_auth(struct nvmet_ctrl *ctrl)
 
 void nvmet_auth_sq_free(struct nvmet_sq *sq)
 {
+	cancel_delayed_work(&sq->auth_expired_work);
 	kfree(sq->dhchap_c1);
 	sq->dhchap_c1 = NULL;
 	kfree(sq->dhchap_c2);
drivers/nvme/target/fabrics-cmd-auth.c +19 -1
index 2f1b95098917..7e7322846b82 100644
@@ -12,9 +12,22 @@
 #include "nvmet.h"
 #include "../host/auth.h"
 
+static void nvmet_auth_expired_work(struct work_struct *work)
+{
+	struct nvmet_sq *sq = container_of(to_delayed_work(work),
+			struct nvmet_sq, auth_expired_work);
+
+	pr_debug("%s: ctrl %d qid %d transaction %u expired, resetting\n",
+		 __func__, sq->ctrl->cntlid, sq->qid, sq->dhchap_tid);
+	sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
+	sq->dhchap_tid = -1;
+}
+
 void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
 {
 	/* Initialize in-band authentication */
+	INIT_DELAYED_WORK(&req->sq->auth_expired_work,
+			  nvmet_auth_expired_work);
 	req->sq->authenticated = false;
 	req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
 	req->cqe->result.u32 |= 0x2 << 16;
@@ -303,8 +316,13 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
 	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)
+	    req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_FAILURE2) {
+		unsigned long auth_expire_secs = ctrl->kato ? ctrl->kato : 120;
+
+		mod_delayed_work(system_wq, &req->sq->auth_expired_work,
+				 auth_expire_secs * HZ);
 		return;
+	}
 	/* Final states, clear up variables */
 	nvmet_auth_sq_free(req->sq);
 	if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE2)
drivers/nvme/target/nvmet.h +1 -0
index d0849404f398..84bf7043674e 100644
@@ -109,6 +109,7 @@ struct nvmet_sq {
 	u32			sqhd;
 	bool			sqhd_disabled;
 #ifdef CONFIG_NVME_TARGET_AUTH
+	struct delayed_work	auth_expired_work;
 	bool			authenticated;
 	u16			dhchap_tid;
 	u16			dhchap_status;
Signature · 1 line
2.29.2
maintainer
#45 lore raw
In reply to Hannes Reinecke
Quoted 13 lines

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.

Quoted 6 lines

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.

Still no nvme-cli nor nvmetcli :(

author maintainer
#46 lore raw
Quoted 13 lines · Sagi Grimberg
Quoted 13 lines

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.

Quoted 8 lines
Quoted 6 lines

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.

Still no nvme-cli nor nvmetcli :(

Just send it (for libnvme and nvme-cli). Patch for nvmetcli to follow.

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
maintainer
#47 lore raw
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines

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.

Quoted 5 lines
Quoted 3 lines
Quoted 1 line

As usual, comments and reviews are welcome.

Still no nvme-cli nor nvmetcli :(

Just send it (for libnvme and nvme-cli). Patch for nvmetcli to follow.

Hey Hannes,

I think that this series is getting into close-to-inclustion shape. Please in your next respin:
1. Make sure to send nvme-cli and nvmetcli with the series
2. Collect Review tags

Thanks!