nvme: In-band authentication support
Subsystems (5)
Hannes Reinecke
maintainerv9 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.
Thanks to Nicolai Stange the crypto DH framework has been upgraded to provide us with a FFDHE implementation; I've updated the patchset to use the ephemeral key generation provided there.
Note that this is just for in-band authentication. Secure concatenation (ie starting TLS with the negotiated parameters) requires a TLS handshake, which the in-kernel TLS implementation does not provide. This is being worked on with a different patchset, and subject to discussion at LSF..
The nvme-cli support has already been merged; please use the latest nvme-cli git repository to build the most recent version.
A copy of this patchset can be found atgit://git.kernel.org/pub/scm/linux/kernel/git/hare/scsi-devel
branch auth.v9
As usual, comments and reviews are welcome.
Changes to v8:
- Rebased to Nicolais crypto DH rework
- Fixed oops on non-fabrics devices
Changes to v7:
- Space out hash list and dhgroup list in nvme negotiate data to be conformant with the spec
- Update sequence number handling to start with a random value and ignore '0' as mandated by the spec
- Update nvme_auth_generate_key to return the key as suggested by Sagi
- Add nvmet_parse_fabrics_io_cmd() as suggested by hch
Changes to v6:
- Use 'u8' for DH group id and hash id
- Use 'struct nvme_dhchap_key'
- Rename variables to drop 'DHCHAP'
- Include reviews from Chaitanya
Changes to v5:
- Unify nvme_auth_generate_key()
- Unify nvme_auth_extract_key()
- Fixed bug where re-authentication with wrong controller key would not fail
- Include reviews from Sagi
Changes to v4:
- Validate against blktest suite
- Fixup base64 decoding
- Transform secret with correct hmac algorithm
Changes to v3:
- Renamed parameter to 'dhchap_ctrl_key'
- Fixed bi-directional authentication
- Included reviews from Sagi
- Fixed base64 algorithm for transport encoding
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 (11):
crypto: add crypto_has_shash()
crypto: add crypto_has_kpp()
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 io queues
nvmet: Implement basic In-Band Authentication
nvmet-auth: Diffie-Hellman key exchange support
nvmet-auth: expire authentication sessions
crypto/kpp.c | 6 + crypto/shash.c | 6 + drivers/nvme/host/Kconfig | 12 + drivers/nvme/host/Makefile | 1 + drivers/nvme/host/auth.c | 1480 ++++++++++++++++++++++++ drivers/nvme/host/auth.h | 40 + drivers/nvme/host/core.c | 141 ++- drivers/nvme/host/fabrics.c | 83 +- drivers/nvme/host/fabrics.h | 7 + drivers/nvme/host/nvme.h | 31 + drivers/nvme/host/rdma.c | 1 + drivers/nvme/host/tcp.c | 1 + drivers/nvme/host/trace.c | 32 + drivers/nvme/target/Kconfig | 13 + drivers/nvme/target/Makefile | 1 + drivers/nvme/target/admin-cmd.c | 4 +- drivers/nvme/target/auth.c | 525 +++++++++ drivers/nvme/target/configfs.c | 138 ++- drivers/nvme/target/core.c | 15 + drivers/nvme/target/fabrics-cmd-auth.c | 534 +++++++++ drivers/nvme/target/fabrics-cmd.c | 55 +- drivers/nvme/target/nvmet.h | 75 +- include/crypto/hash.h | 2 + include/crypto/kpp.h | 2 + include/linux/base64.h | 16 + include/linux/nvme.h | 188 ++- lib/Makefile | 2 +- lib/base64.c | 103 ++ 28 files changed, 3499 insertions(+), 15 deletions(-) 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/linux/base64.h create mode 100644 lib/base64.c
Signature · 1 line
2.29.2
Hannes Reinecke
maintainerPATCH v9 1/11 crypto: add crypto_has_shash()
Add helper function to determine if a given synchronous hash is supported.
--- crypto/shash.c | 6 ++++++ include/crypto/hash.h | 2 ++ 2 files changed, 8 insertions(+)
crypto/shash.c +6 -0
@@ -521,6 +521,12 @@ struct crypto_shash *crypto_alloc_shash(const char *alg_name, u32 type, }EXPORT_SYMBOL_GPL (crypto_alloc_shash); +int crypto_has_shash (const char *alg_name,u32 type,u32 mask) +{ +return crypto_type_has_alg (alg_name, &crypto_shash_type, type, mask); +} +EXPORT_SYMBOL_GPL (crypto_has_shash); +static int shash_prepare_alg (struct shash_alg *alg) {struct crypto_alg *base = &alg->base;
include/crypto/hash.h +2 -0
@@ -718,6 +718,8 @@ static inline void ahash_request_set_crypt(struct ahash_request *req,struct crypto_shash *crypto_alloc_shash (const char *alg_name,u32 type,u32 mask); +int crypto_has_shash (const char *alg_name,u32 type,u32 mask); +static inline struct crypto_tfm *crypto_shash_tfm (struct crypto_shash *tfm) {return &tfm->base;
Signature · 1 line
2.29.2
Hannes Reinecke
maintainerPATCH v9 2/11 crypto: add crypto_has_kpp()
In reply to Hannes ReineckeAdd helper function to determine if a given key-agreement protocol primitive is supported.
--- crypto/kpp.c | 6 ++++++ include/crypto/kpp.h | 2 ++ 2 files changed, 8 insertions(+)
crypto/kpp.c +6 -0
@@ -104,6 +104,12 @@ int crypto_grab_kpp(struct crypto_kpp_spawn *spawn, }EXPORT_SYMBOL_GPL (crypto_grab_kpp); +int crypto_has_kpp (const char *alg_name,u32 type,u32 mask) +{ +return crypto_type_has_alg (alg_name, &crypto_kpp_type, type, mask); +} +EXPORT_SYMBOL_GPL (crypto_has_kpp); +static void kpp_prepare_alg (struct kpp_alg *alg) {struct crypto_alg *base = &alg->base;
include/crypto/kpp.h +2 -0
@@ -104,6 +104,8 @@ struct kpp_alg {*/ struct crypto_kpp *crypto_alloc_kpp (const char *alg_name,u32 type,u32 mask); +int crypto_has_kpp (const char *alg_name,u32 type,u32 mask); +static inline struct crypto_tfm *crypto_kpp_tfm (struct crypto_kpp *tfm) {return &tfm->base;
Signature · 1 line
2.29.2
Hannes Reinecke
maintainerPATCH v9 3/11 lib/base64: RFC4648-compliant base64 encoding
In reply to Hannes ReineckeAdd 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 | 103 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 include/linux/base64.h create mode 100644 lib/base64.c
include/linux/base64.h new file +16 -0
@@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* base64 encoding, lifted from fs/crypto/fname.c. +*/ + +#ifndef _LINUX_BASE64_H +#define _LINUX_BASE64_H + +#include <linux/types.h> + +#define BASE64_CHARS (nbytes) DIV_ROUND_UP((nbytes) * 4, 3) + +int base64_encode (const u8 *src,int len,char *dst); +int base64_decode (const char *src,int len,u8 *dst); + +#endif /* _LINUX_BASE64_H */
lib/Makefile +1 -1
@@ -46,7 +46,7 @@ obj-y += bcd.o sort.o parser.o debug_locks.o random32.o \ bust_spinlocks.o kasprintf.o bitmap.o scatterlist.o \ list_sort.o uuid.o iov_iter.o clz_ctz.o \ bsearch.o find_bit.o llist.o memweight.o kfifo.o \ - percpu-refcount.o rhashtable.o \ + percpu-refcount.o rhashtable.o base64.o \ once.o refcount.o usercopy.o errseq.o bucket_locks.o \ generic-radix-tree.o obj-$(CONFIG_STRING_SELFTEST) += test_string.o
lib/base64.c new file +103 -0
103 lines not on this page · raw message
Signature · 1 line
2.29.2
Hannes Reinecke
maintainerPATCH v9 4/11 nvme: add definitions for NVMe In-Band authentication
In reply to Hannes ReineckeAdd new definitions for NVMe In-band authentication as defined in the NVMe Base Specification v2.0.
--- include/linux/nvme.h | 188 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 187 insertions(+), 1 deletion(-)
include/linux/nvme.h +187 -1
@@ -19,6 +19,7 @@#define NVMF_TRSVCID_SIZE 32#define NVMF_TRADDR_SIZE 256#define NVMF_TSAS_SIZE 256 +#define NVMF_AUTH_HASH_LEN 64#define NVME_DISC_SUBSYS_NAME "nqn.2014-08.org.nvmexpress.discovery"
@@ -1286,6 +1287,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 }
@@ -1293,7 +1296,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.
@@ -1423,6 +1428,185 @@ 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_HASH_SHA256 =0x01 , +NVME_AUTH_HASH_SHA384 =0x02 , +NVME_AUTH_HASH_SHA512 =0x03 , +NVME_AUTH_HASH_INVALID =0xff , +}; + +/* Defined Diffie-Hellman group identifiers for DH-HMAC-CHAP authentication */ +enum { +NVME_AUTH_DHGROUP_NULL =0x00 , +NVME_AUTH_DHGROUP_2048 =0x01 , +NVME_AUTH_DHGROUP_3072 =0x02 , +NVME_AUTH_DHGROUP_4096 =0x03 , +NVME_AUTH_DHGROUP_6144 =0x04 , +NVME_AUTH_DHGROUP_8192 =0x05 , +NVME_AUTH_DHGROUP_INVALID =0xff , +}; + +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;
@@ -1466,6 +1650,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
Hannes Reinecke
maintainerPATCH v9 5/11 nvme-fabrics: decode 'authentication required' connect error
In reply to Hannes ReineckeThe '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
@@ -331,6 +331,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
Hannes Reinecke
maintainerPATCH v9 6/11 nvme: Implement In-Band authentication
In reply to Hannes ReineckeImplement 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_ctrl_secret' to specify the pre-shared controller key for 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' or 'dhchap_ctrl_secret'.
--- drivers/nvme/host/Kconfig | 11 + drivers/nvme/host/Makefile | 1 + drivers/nvme/host/auth.c | 1140 +++++++++++++++++++++++++++++++++++ drivers/nvme/host/auth.h | 32 + drivers/nvme/host/core.c | 141 ++++- drivers/nvme/host/fabrics.c | 79 ++- drivers/nvme/host/fabrics.h | 7 + drivers/nvme/host/nvme.h | 31 + drivers/nvme/host/rdma.c | 1 + drivers/nvme/host/tcp.c | 1 + drivers/nvme/host/trace.c | 32 + 11 files changed, 1469 insertions(+), 7 deletions(-) create mode 100644 drivers/nvme/host/auth.c create mode 100644 drivers/nvme/host/auth.h
drivers/nvme/host/Kconfig +11 -0
@@ -91,3 +91,14 @@ config NVME_TCP from https://github.com/linux-nvme/nvme-cli. If unsure, say N. + +config NVME_AUTH + bool "NVM Express over Fabrics In-Band Authentication" + depends on NVME_CORE + select CRYPTO_HMAC + select CRYPTO_SHA256 + select CRYPTO_SHA512 + help + This provides support for NVMe over Fabrics In-Band Authentication. + + If unsure, say N.
drivers/nvme/host/Makefile +1 -0
@@ -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 +1140 -0
1,140 lines not on this page · raw message
drivers/nvme/host/auth.h new file +32 -0
@@ -0,0 +1,32 @@ +/* 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> + +struct nvme_dhchap_key { +u8 *key; +size_t len; +u8 hash; +}; + +u32 nvme_auth_get_seqnum (void ); +const char *nvme_auth_dhgroup_name (u8 dhgroup_id); +const char *nvme_auth_dhgroup_kpp (u8 dhgroup_id); +u8 nvme_auth_dhgroup_id (const char *dhgroup_name); + +const char *nvme_auth_hmac_name (u8 hmac_id); +const char *nvme_auth_digest_name (u8 hmac_id); +size_t nvme_auth_hmac_hash_len (u8 hmac_id); +u8 nvme_auth_hmac_id (const char *hmac_name); + +struct nvme_dhchap_key *nvme_auth_extract_key (unsigned char *secret, +u8 key_hash); +void nvme_auth_free_key (struct nvme_dhchap_key *key); +u8 *nvme_auth_transform_key (struct nvme_dhchap_key *key,char *nqn); + +#endif /* _NVME_AUTH_H */
drivers/nvme/host/core.c +137 -4
223 lines not on this page · raw message
drivers/nvme/host/fabrics.c +76 -3
134 lines not on this page · raw message
drivers/nvme/host/fabrics.h +7 -0
@@ -68,6 +68,8 @@ enum {NVMF_OPT_FAIL_FAST_TMO =1 <<20 ,NVMF_OPT_HOST_IFACE =1 <<21 ,NVMF_OPT_DISCOVERY =1 <<22 , +NVMF_OPT_DHCHAP_SECRET =1 <<23 , + NVMF_OPT_DHCHAP_CTRL_SECRET = 1 << 24, };/**
@@ -97,6 +99,9 @@ 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_ctrl_secret: DH-HMAC-CHAP controller secret for bi-directional +* authentication * @disable_sqflow: disable controller sq flow control * @hdr_digest: generate/verify header digest (TCP) * @data_digest: generate/verify data digest (TCP)
@@ -121,6 +126,8 @@ struct nvmf_ctrl_options {unsigned int kato;struct nvmf_host *host;int max_reconnects; +char *dhchap_secret; +char *dhchap_ctrl_secret;bool disable_sqflow;bool hdr_digest;bool data_digest;
drivers/nvme/host/nvme.h +31 -0
@@ -323,6 +323,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; +struct nvme_dhchap_key *host_key; +struct nvme_dhchap_key *ctrl_key; +u16 transaction; +#endif +/* Power saving configuration */ u64 ps_max_latency_us;bool apst_enabled;
@@ -928,6 +937,28 @@ 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_reset (struct nvme_ctrl *ctrl); +void nvme_auth_free (struct nvme_ctrl *ctrl); +int nvme_auth_generate_key (u8 *secret,struct nvme_dhchap_key **ret_key); +#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/rdma.c +1 -0
@@ -1197,6 +1197,7 @@ static void nvme_rdma_error_recovery_work(struct work_struct *work)struct nvme_rdma_ctrl *ctrl =container_of (work, struct nvme_rdma_ctrl, err_work); +nvme_auth_stop (&ctrl->ctrl);nvme_stop_keep_alive (&ctrl->ctrl);flush_work (&ctrl->ctrl.async_event_work);nvme_rdma_teardown_io_queues (ctrl, false);
drivers/nvme/host/tcp.c +1 -0
@@ -2174,6 +2174,7 @@ static void nvme_tcp_error_recovery_work(struct work_struct *work) struct nvme_tcp_ctrl, err_work);struct nvme_ctrl *ctrl = &tcp_ctrl->ctrl; +nvme_auth_stop (ctrl);nvme_stop_keep_alive (ctrl);flush_work (&ctrl->async_event_work);nvme_tcp_teardown_io_queues (ctrl, false);
drivers/nvme/host/trace.c +32 -0
@@ -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
Re: [PATCH 06/11] nvme: Implement In-Band authentication
Quoted 229 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_ctrl_secret' to specify the pre-shared controller key for 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' or 'dhchap_ctrl_secret'.Signed-off-by Hannes Reinecke <[email protected]>--- drivers/nvme/host/Kconfig | 11 + drivers/nvme/host/Makefile | 1 + drivers/nvme/host/auth.c | 1140 +++++++++++++++++++++++++++++++++++ drivers/nvme/host/auth.h | 32 + drivers/nvme/host/core.c | 141 ++++- drivers/nvme/host/fabrics.c | 79 ++- drivers/nvme/host/fabrics.h | 7 + drivers/nvme/host/nvme.h | 31 + drivers/nvme/host/rdma.c | 1 + drivers/nvme/host/tcp.c | 1 + drivers/nvme/host/trace.c | 32 + 11 files changed, 1469 insertions(+), 7 deletions(-)create mode 100644
drivers/nvme/host/auth.c
create mode 100644drivers/nvme/host/auth.hdrivers/nvme/host/Kconfig +11
-0@@ -91,3 +91,14 @@ config NVME_TCP from https://github.com/linux-nvme/nvme-cli. If unsure, say N. + +config NVME_AUTH + bool "NVM Express over Fabrics In-Band Authentication" + depends on NVME_CORE + select CRYPTO_HMAC + select CRYPTO_SHA256 + select CRYPTO_SHA512 + help + This provides support for NVMe over Fabrics In-Band Authentication. + + If unsure, say N.drivers/nvme/host/Makefile +1
-0@@ -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.odrivers/nvme/host/auth.c new file +166
-0@@ -0,0 +1,1140 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2020 Hannes Reinecke, SUSE Linux +*/ + +#include <linux/crc32.h> +#include <linux/base64.h> +#include <linux/prandom.h> +#include <asm/unaligned.h> +#include <crypto/hash.h> +#include <crypto/dh.h> +#include "nvme.h" +#include "fabrics.h" +#include "auth.h" + +static u32 nvme_dhchap_seqnum; +static DEFINE_MUTEX (nvme_dhchap_mutex); + +struct nvme_dhchap_queue_context { +struct list_head entry; +struct work_struct auth_work; +struct nvme_ctrl *ctrl; +struct crypto_shash *shash_tfm; +void *buf; +size_t buf_size; +int qid; +int error; +u32 s1; +u32 s2; +u16 transaction; +u8 status; +u8 hash_id; +size_t hash_len; +u8 dhgroup_id; +u8 c1[64 ]; +u8 c2[64 ]; +u8 response[64 ]; +u8 *host_response; +}; + +u32 nvme_auth_get_seqnum (void ) +{ +u32 seqnum; + +mutex_lock (&nvme_dhchap_mutex); +if (!nvme_dhchap_seqnum) + nvme_dhchap_seqnum =prandom_u32 (); +else { + nvme_dhchap_seqnum++; +if (!nvme_dhchap_seqnum) + nvme_dhchap_seqnum++; + } + seqnum = nvme_dhchap_seqnum; +mutex_unlock (&nvme_dhchap_mutex); +return seqnum; +} +EXPORT_SYMBOL_GPL (nvme_auth_get_seqnum); + +static struct nvme_auth_dhgroup_map { +u8 id; +const char name[16 ]; +const char kpp[16 ]; +} dhgroup_map[] = { + { .id =NVME_AUTH_DHGROUP_NULL , + .name ="null" , .kpp ="null" }, + { .id =NVME_AUTH_DHGROUP_2048 , + .name ="ffdhe2048" , .kpp ="ffdhe2048(dh)" }, + { .id =NVME_AUTH_DHGROUP_3072 , + .name ="ffdhe3072" , .kpp ="ffdhe3072(dh)" }, + { .id =NVME_AUTH_DHGROUP_4096 , + .name ="ffdhe4096" , .kpp ="ffdhe4096(dh)" }, + { .id =NVME_AUTH_DHGROUP_6144 , + .name ="ffdhe6144" , .kpp ="ffdhe6144(dh)" }, + { .id =NVME_AUTH_DHGROUP_8192 , + .name ="ffdhe8192" , .kpp ="ffdhe8192(dh)" }, +}; + +const char *nvme_auth_dhgroup_name (u8 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); + +const char *nvme_auth_dhgroup_kpp (u8 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); + +u8 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 NVME_AUTH_DHGROUP_INVALID ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_id); + +static struct nvme_dhchap_hash_map { +int id; +int len; +const char hmac[15 ]; +const char digest[15 ]; +} hash_map[] = { + {.id =NVME_AUTH_HASH_SHA256 , .len =32 , + .hmac ="hmac(sha256)" , .digest ="sha256" }, + {.id =NVME_AUTH_HASH_SHA384 , .len =48 , + .hmac ="hmac(sha384)" , .digest ="sha384" }, + {.id =NVME_AUTH_HASH_SHA512 , .len =64 , + .hmac ="hmac(sha512)" , .digest ="sha512" }, +}; + +const char *nvme_auth_hmac_name (u8 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 (u8 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); + +u8 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 NVME_AUTH_HASH_INVALID ; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_id); + +size_t nvme_auth_hmac_hash_len (u8 hmac_id)
drivers/nvme/host/auth.c line 176
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+{ +int i; + +for (i =0 ; i <ARRAY_SIZE (hash_map); i++) { +if (hash_map[i].id == hmac_id) +return hash_map[i].len; + } +return 0 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_hash_len);
we can remove loops in the above helpers and the id member in the struct with the help of the sparse array, why not :-
/* Defined hash functions for DH-HMAC-CHAP authentication */ enum {
NVME_AUTH_HASH_SHA256 = 0x01,
NVME_AUTH_HASH_SHA384 = 0x02,
NVME_AUTH_HASH_SHA512 = 0x03,
NVME_AUTH_HASH_INVALID = 0xff,};
static struct nvme_dhchap_hash_map {
int len;
const char *hmac;
const char *digest;
} hash_map[] = {
[NVME_AUTH_HASH_SHA256] = { .len = 32,
.hmac = "hmac(sha256)",
.digest = "sha256"
},
[NVME_AUTH_HASH_SHA384] = { .len = 48,
.hmac = "hmac(sha384)",
.digest = "sha384"
},
[NVME_AUTH_HASH_SHA512] = { .len = 64,
.hmac = "hmac(sha512)",
.digest = "sha512"
},
};const char *nvme_auth_hmac_name(unsigned int hmac_id)
{
if ((hmac_id > ARRAY_SIZE(hash_map)) || !hash_map[hmac_id].hmac)
return NULL;
return hash_map[hmac_id].hmac;
}const char *nvme_auth_digest_name(unsigned int hmac_id)
{
if ((hmac_id > ARRAY_SIZE(hash_map)) || !hash_map[hmac_id].hmac)
return NULL;
return hash_map[hmac_id].digest;
}size_t nvme_auth_hmac_hash_len(unsigned int hmac_id)
{
if ((hmac_id > ARRAY_SIZE(hash_map)) || !hash_map[hmac_id].hmac)
return 0;
return hash_map[hmac_id].len;
}unsigned int nvme_auth_hmac_id(const char *hmac_name)
{
int i;
for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
if (!hash_map[i].hmac)
continue;
if (!strncmp(hash_map[i].hmac, hmac_name,
strlen(hash_map[i].hmac)))
return i;
}
return NVME_AUTH_HASH_INVALID;
}See [1] that has a test program execution, same can be applied to nvme_dhchap_hash_map.
Quoted 199 lines
drivers/nvme/host/auth.c new file +199
-0+ +struct nvme_dhchap_key *nvme_auth_extract_key (unsigned char *secret, +u8 key_hash) +{ +struct nvme_dhchap_key *key; +unsigned char *p; +u32 crc; +int ret, key_len; +size_t allocated_len =strlen (secret); + +/* Secret might be affixed with a ':' */ + p =strrchr (secret,':' ); +if (p) + allocated_len = p - secret; + key =kzalloc (sizeof (*key),GFP_KERNEL ); +if (!key) +return ERR_PTR (-ENOMEM ); + key->key =kzalloc (allocated_len,GFP_KERNEL ); +if (!key->key) { + ret = -ENOMEM ; + goto out_free_key; + } + + key_len =base64_decode (secret, allocated_len, key->key); +if (key_len <0 ) { +pr_debug ("base64 key decoding error %d\n" , + key_len); + ret = key_len; + goto out_free_secret; + } + +if (key_len !=36 && key_len !=52 && + key_len !=68 ) { +pr_err ("Invalid DH-HMAC-CHAP key len %d\n" , + key_len); + ret = -EINVAL ; + goto out_free_secret; + } + +if (key_hash >0 && + (key_len -4 ) !=nvme_auth_hmac_hash_len (key_hash)) { +pr_err ("Invalid DH-HMAC-CHAP key len %d for %s\n" , key_len, +nvme_auth_hmac_name (key_hash)); + ret = -EINVAL ; + goto out_free_secret; + } + +/* The last four bytes is the CRC in little-endian format */ + key_len -=4 ; +/* +* The linux implementation doesn't do pre- and post-increments, +* so we have to do it manually. +*/ + crc = ~crc32 (~0 , key->key, key_len); + +if (get_unaligned_le32 (key->key + key_len) != crc) { +pr_err ("DH-HMAC-CHAP key crc mismatch (key %08x, crc %08x)\n" , +get_unaligned_le32 (key->key + key_len), crc); + ret = -EKEYREJECTED ; + goto out_free_secret; + } + key->len = key_len; + key->hash = key_hash; +return key; +out_free_secret: +kfree_sensitive (key->key); +out_free_key: +kfree (key); +return ERR_PTR (ret); +} +EXPORT_SYMBOL_GPL (nvme_auth_extract_key); + +void nvme_auth_free_key (struct nvme_dhchap_key *key) +{ +if (!key) +return ; +kfree_sensitive (key->key); +kfree (key); +} +EXPORT_SYMBOL_GPL (nvme_auth_free_key); + +u8 *nvme_auth_transform_key (struct nvme_dhchap_key *key,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; + +if (key->hash ==0 ) { + transformed_key =kmemdup (key->key, key->len,GFP_KERNEL ); +return transformed_key ? transformed_key :ERR_PTR (-ENOMEM ); + } + +if (!key || !key->key) { +pr_warn ("No key specified\n" ); +return ERR_PTR (-ENOKEY ); + } +if (!hmac_name) { +pr_warn ("Invalid key hash id %d\n" , key->hash); +return ERR_PTR (-EINVAL ); + } + + 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) { + ret = -ENOMEM ; + goto out_free_key; + } + + 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, 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); +out_free_key: +crypto_free_shash (key_tfm); +if (ret <0 ) { +kfree_sensitive (transformed_key); +return ERR_PTR (ret); + } +return transformed_key; +} +EXPORT_SYMBOL_GPL (nvme_auth_transform_key); + +#define nvme_auth_flags_from_qid (qid) \ + (qid == 0) ? 0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED +#define nvme_auth_queue_from_qid (ctrl, qid) \ + (qid == 0) ? (ctrl)->fabrics_q : (ctrl)->connect_q + +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 =nvme_auth_flags_from_qid (qid); +struct request_queue *q =nvme_auth_queue_from_qid (ctrl, qid); +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 =cpu_to_le32 (tl); + + ret =__nvme_submit_sync_cmd (q, &cmd,NULL , data, tl,0 , + qid ==0 ?NVME_QID_ANY : qid, +0 , flags); +if (ret >0 ) +dev_warn (ctrl->device, +"qid %d auth_send failed with status %d\n" , qid, ret); +else if (ret <0 ) +dev_err (ctrl->device, +"qid %d auth_send failed with error %d\n" , 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 =nvme_auth_flags_from_qid (qid); +struct request_queue *q =nvme_auth_queue_from_qid (ctrl, qid); +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 =cpu_to_le32 (al); + + ret =__nvme_submit_sync_cmd (q, &cmd,NULL , buf, al,0 , + qid ==0 ?NVME_QID_ANY : qid, +0 , flags); +if (ret >0 ) { + dev_warn(ctrl->device,
drivers/nvme/host/auth.c line 385
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ "qid %d auth_recv failed with status %x\n", qid, ret); + ret = -EIO ; + }else if (ret <0 ) { +dev_err (ctrl->device, +"qid %d auth_recv failed with error %d\n" , qid, ret); + } + +return ret; +} +
Why not use something like this ? It reduces the duplicate code and need for macros :-
static int nvme_auth_send_recv_common(bool send, struct nvme_ctrl *ctrl,
int qid, void *buf, size_t buflen)
{
strucy nvme_cmd cmd = { };
blk_mq_req_flags_t flags;
struct request_queue *q;
int ret;
flags = qid == 0 ? 0 : BLK_MQ_REQ_RESERVED | BLK_MQ_REQ_NOWAIT;
q = qid == 0 ? ctrl->fabrics_q : ctrl->connect_q;
/* auth send/recv share common offset for the various fields in
cmd */
cmd.auth_send.opcode = nvme_fabrics_command;
cmd.auth_send.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER;
cmd.auth_send.spsp0 = 0x01;
cmd.auth_send.spsp1 = 0x01;
cmd.auth_send.al = cpu_to_le32(buflen);
cmd.auth_receive.fctype = send ? nvme_fabrics_type_auth_send :
nvme_fabrics_type_auth_receive;
ret = nvme_submit_sync_cmd(q, &cmd, NULL, buf, buflen, 0,
qid == 0 ? NVME_QID_ANY : qid,
0, flags);
if (ret > 0)
dev_warn(ctrl->device,
"qid %d fctype 0x%x failed with status %d\n", qid,
cmd.auth_send.fctype, ret);
else if (ret < 0)
dev_err(ctrl->device,
"qid %d fctype 0x%x failed with error %d\n", qid,
cmd.auth_send.fctype, ret);
return ret;
}static int nvme_auth_send(struct nvme_ctrl *c, int qid, void *data, size_t tl)
{
return nvme_auth_send_recv_common(true, c, qid, data, tl);
}static int nvme_auth_receive(struct nvme_ctrl *ctrl, int qid,
void *buf, size_t al)
{
return nvme_auth_send_recv_common(false, c, qid, data, tl);}
Quoted 17 lines
drivers/nvme/host/auth.c new file +17
-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 ;
drivers/nvme/host/auth.c line 412
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ } +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 ; +} +
[...]
[1] # cat a.c
#include <stdio.h>
#include <sys/types.h> #include <sys/stat.h> #include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/uio.h> #include <stdlib.h>
#define COUNT (10)
#define LEN (1024)
#define ARRAY_SIZE(arr) ((sizeof(arr) / sizeof(arr[0])))
/* Defined hash functions for DH-HMAC-CHAP authentication */ enum {
NVME_AUTH_HASH_SHA256 = 0x01,
NVME_AUTH_HASH_SHA384 = 0x02,
NVME_AUTH_HASH_SHA512 = 0x03,
NVME_AUTH_HASH_INVALID = 0xff,};
static struct nvme_dhchap_hash_map {
int len;
const char *hmac;
const char *digest;
} hash_map[] = {
[NVME_AUTH_HASH_SHA256] = { .len = 32,
.hmac = "hmac(sha256)",
.digest = "sha256"
},
[NVME_AUTH_HASH_SHA384] = { .len = 48,
.hmac = "hmac(sha384)",
.digest = "sha384"
},
[NVME_AUTH_HASH_SHA512] = { .len = 64,
.hmac = "hmac(sha512)",
.digest = "sha512"
},
};const char *nvme_auth_hmac_name(unsigned int hmac_id)
{
if ((hmac_id > ARRAY_SIZE(hash_map)) || !hash_map[hmac_id].hmac)
return NULL;
return hash_map[hmac_id].hmac;
}const char *nvme_auth_digest_name(unsigned int hmac_id)
{
if ((hmac_id > ARRAY_SIZE(hash_map)) || !hash_map[hmac_id].hmac)
return NULL;
return hash_map[hmac_id].digest;
}size_t nvme_auth_hmac_hash_len(unsigned int hmac_id)
{
if ((hmac_id > ARRAY_SIZE(hash_map)) || !hash_map[hmac_id].hmac)
return 0;
return hash_map[hmac_id].len;
}unsigned int nvme_auth_hmac_id(const char *hmac_name)
{
int i;
for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
if (!hash_map[i].hmac)
continue;
if (!strncmp(hash_map[i].hmac, hmac_name,
strlen(hash_map[i].hmac)))
return i;
}
return NVME_AUTH_HASH_INVALID;
}int main(void)
{
printf("-------------------------------------------------------\n");
printf("hmac_id = 0x%s name 0x%x\n", "hmac(sha256)",
nvme_auth_hmac_id("hmac(sha256)"));
printf("hmac_id = 0x%s name 0x%x\n", "hmac(sha384)",
nvme_auth_hmac_id("hmac(sha384)"));
printf("hmac_id = 0x%s name 0x%x\n", "hmac(sha512)",
nvme_auth_hmac_id("hmac(sha512)"));
printf("hmac_id = 0x%s name 0x%x\n", "",
nvme_auth_hmac_id(""));
printf("hmac_id = 0x%s name 0x%x\n", "asdfadsf",
nvme_auth_hmac_id("asdfadsf"));
printf("-------------------------------------------------------\n");
printf("hmac_id = 0x%x name %s\n", NVME_AUTH_HASH_SHA256,
nvme_auth_hmac_name(NVME_AUTH_HASH_SHA256));
printf("hmac_id = 0x%x name %s\n", NVME_AUTH_HASH_SHA384,
nvme_auth_hmac_name(NVME_AUTH_HASH_SHA384));
printf("hmac_id = 0x%x name %s\n", NVME_AUTH_HASH_SHA512,
nvme_auth_hmac_name(NVME_AUTH_HASH_SHA512));
printf("hmac_id = 0x%x name %s\n", 0x05,
nvme_auth_hmac_name(0x05));
printf("hmac_id = 0x%x name %s\n", 0x0ff,
nvme_auth_hmac_name(0xff));
printf("hmac_id = 0x%x name %s\n", 0x0ff1,
nvme_auth_hmac_name(0xff1));
printf("-------------------------------------------------------\n");
printf("hmac_id = 0x%x digest %s\n", NVME_AUTH_HASH_SHA256,
nvme_auth_digest_name(NVME_AUTH_HASH_SHA256));
printf("hmac_id = 0x%x digest %s\n", NVME_AUTH_HASH_SHA384,
nvme_auth_digest_name(NVME_AUTH_HASH_SHA384));
printf("hmac_id = 0x%x digest %s\n", NVME_AUTH_HASH_SHA512,
nvme_auth_digest_name(NVME_AUTH_HASH_SHA512));
printf("hmac_id = 0x%x digest %s\n", 0x05,
nvme_auth_digest_name(0x05));
printf("hmac_id = 0x%x digest %s\n", 0x0ff,
nvme_auth_digest_name(0xff));
printf("hmac_id = 0x%x digest %s\n", 0x0ff1,
nvme_auth_digest_name(0xff1));
printf("-------------------------------------------------------\n");
printf("hmac_id = 0x%x len %d\n", NVME_AUTH_HASH_SHA256,
nvme_auth_hmac_hash_len(NVME_AUTH_HASH_SHA256));
printf("hmac_id = 0x%x len %d\n", NVME_AUTH_HASH_SHA384,
nvme_auth_hmac_hash_len(NVME_AUTH_HASH_SHA384));
printf("hmac_id = 0x%x len %d\n", NVME_AUTH_HASH_SHA512,
nvme_auth_hmac_hash_len(NVME_AUTH_HASH_SHA512));
printf("hmac_id = 0x%x len %d\n", 0x05,
nvme_auth_hmac_hash_len(0x05));
printf("hmac_id = 0x%x len %d\n", 0x0ff,
nvme_auth_hmac_hash_len(0xff));
printf("hmac_id = 0x%x len %d\n", 0x0ff1,
nvme_auth_hmac_hash_len(0xff1));
return 0;
}nvme (nvme-5.18) # gcc a.c nvme (nvme-5.18) # ./a.out
-------------------------------------------------------hmac_id = 0xhmac(sha256) name 0x1 hmac_id = 0xhmac(sha384) name 0x2 hmac_id = 0xhmac(sha512) name 0x3 hmac_id = 0x name 0xff hmac_id = 0xasdfadsf name 0xff
-------------------------------------------------------hmac_id = 0x1 name hmac(sha256) hmac_id = 0x2 name hmac(sha384) hmac_id = 0x3 name hmac(sha512) hmac_id = 0x5 name (null) hmac_id = 0xff name (null) hmac_id = 0xff1 name (null)
-------------------------------------------------------hmac_id = 0x1 digest sha256 hmac_id = 0x2 digest sha384 hmac_id = 0x3 digest sha512 hmac_id = 0x5 digest (null) hmac_id = 0xff digest (null) hmac_id = 0xff1 digest (null)
-------------------------------------------------------hmac_id = 0x1 len 32hmac_id = 0x2 len 48hmac_id = 0x3 len 64hmac_id = 0x5 len 0hmac_id = 0xff len 0hmac_id = 0xff1 len 0
Hannes Reinecke
maintainerRe: [PATCH 06/11] nvme: Implement In-Band authentication
Quoted 235 lines · Chaitanya Kulkarni
Quoted 234 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_ctrl_secret' to specify the pre-shared controller key for 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' or 'dhchap_ctrl_secret'.Signed-off-by Hannes Reinecke <[email protected]>--- drivers/nvme/host/Kconfig | 11 + drivers/nvme/host/Makefile | 1 + drivers/nvme/host/auth.c | 1140 +++++++++++++++++++++++++++++++++++ drivers/nvme/host/auth.h | 32 + drivers/nvme/host/core.c | 141 ++++- drivers/nvme/host/fabrics.c | 79 ++- drivers/nvme/host/fabrics.h | 7 + drivers/nvme/host/nvme.h | 31 + drivers/nvme/host/rdma.c | 1 + drivers/nvme/host/tcp.c | 1 + drivers/nvme/host/trace.c | 32 + 11 files changed, 1469 insertions(+), 7 deletions(-)create mode 100644
drivers/nvme/host/auth.c
create mode 100644drivers/nvme/host/auth.hdrivers/nvme/host/Kconfig +11
-0@@ -91,3 +91,14 @@ config NVME_TCP from https://github.com/linux-nvme/nvme-cli. If unsure, say N. + +config NVME_AUTH + bool "NVM Express over Fabrics In-Band Authentication" + depends on NVME_CORE + select CRYPTO_HMAC + select CRYPTO_SHA256 + select CRYPTO_SHA512 + help + This provides support for NVMe over Fabrics In-Band Authentication. + + If unsure, say N.drivers/nvme/host/Makefile +1
-0@@ -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.odrivers/nvme/host/auth.c new file +171
-0@@ -0,0 +1,1140 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2020 Hannes Reinecke, SUSE Linux +*/ + +#include <linux/crc32.h> +#include <linux/base64.h> +#include <linux/prandom.h> +#include <asm/unaligned.h> +#include <crypto/hash.h> +#include <crypto/dh.h> +#include "nvme.h" +#include "fabrics.h" +#include "auth.h" + +static u32 nvme_dhchap_seqnum; +static DEFINE_MUTEX (nvme_dhchap_mutex); + +struct nvme_dhchap_queue_context { +struct list_head entry; +struct work_struct auth_work; +struct nvme_ctrl *ctrl; +struct crypto_shash *shash_tfm; +void *buf; +size_t buf_size; +int qid; +int error; +u32 s1; +u32 s2; +u16 transaction; +u8 status; +u8 hash_id; +size_t hash_len; +u8 dhgroup_id; +u8 c1[64 ]; +u8 c2[64 ]; +u8 response[64 ]; +u8 *host_response; +}; + +u32 nvme_auth_get_seqnum (void ) +{ +u32 seqnum; + +mutex_lock (&nvme_dhchap_mutex); +if (!nvme_dhchap_seqnum) + nvme_dhchap_seqnum =prandom_u32 (); +else { + nvme_dhchap_seqnum++; +if (!nvme_dhchap_seqnum) + nvme_dhchap_seqnum++; + } + seqnum = nvme_dhchap_seqnum; +mutex_unlock (&nvme_dhchap_mutex); +return seqnum; +} +EXPORT_SYMBOL_GPL (nvme_auth_get_seqnum); + +static struct nvme_auth_dhgroup_map { +u8 id; +const char name[16 ]; +const char kpp[16 ]; +} dhgroup_map[] = { + { .id =NVME_AUTH_DHGROUP_NULL , + .name ="null" , .kpp ="null" }, + { .id =NVME_AUTH_DHGROUP_2048 , + .name ="ffdhe2048" , .kpp ="ffdhe2048(dh)" }, + { .id =NVME_AUTH_DHGROUP_3072 , + .name ="ffdhe3072" , .kpp ="ffdhe3072(dh)" }, + { .id =NVME_AUTH_DHGROUP_4096 , + .name ="ffdhe4096" , .kpp ="ffdhe4096(dh)" }, + { .id =NVME_AUTH_DHGROUP_6144 , + .name ="ffdhe6144" , .kpp ="ffdhe6144(dh)" }, + { .id =NVME_AUTH_DHGROUP_8192 , + .name ="ffdhe8192" , .kpp ="ffdhe8192(dh)" }, +}; + +const char *nvme_auth_dhgroup_name (u8 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); + +const char *nvme_auth_dhgroup_kpp (u8 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); + +u8 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 NVME_AUTH_DHGROUP_INVALID ; +} +EXPORT_SYMBOL_GPL (nvme_auth_dhgroup_id); + +static struct nvme_dhchap_hash_map { +int id; +int len; +const char hmac[15 ]; +const char digest[15 ]; +} hash_map[] = { + {.id =NVME_AUTH_HASH_SHA256 , .len =32 , + .hmac ="hmac(sha256)" , .digest ="sha256" }, + {.id =NVME_AUTH_HASH_SHA384 , .len =48 , + .hmac ="hmac(sha384)" , .digest ="sha384" }, + {.id =NVME_AUTH_HASH_SHA512 , .len =64 , + .hmac ="hmac(sha512)" , .digest ="sha512" }, +}; + +const char *nvme_auth_hmac_name (u8 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 (u8 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); + +u8 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 NVME_AUTH_HASH_INVALID ; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_id); + +size_t nvme_auth_hmac_hash_len (u8 hmac_id) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (hash_map); i++) { +if (hash_map[i].id == hmac_id)
Quoted 9 lines
drivers/nvme/host/auth.c line 176
Quoted 5 lines
drivers/nvme/host/auth.c new file +5
-0+return hash_map[i].len; + } +return 0 ; +} +EXPORT_SYMBOL_GPL (nvme_auth_hmac_hash_len);we can remove loops in the above helpers and the id member in the struct with the help of the sparse array, why not :-
Sure, good idea. Will be doing that.
[ .. ]
Quoted 61 lines
Quoted 20 lines
drivers/nvme/host/auth.c new file +20
-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 =nvme_auth_flags_from_qid (qid); +struct request_queue *q =nvme_auth_queue_from_qid (ctrl, qid); +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 =cpu_to_le32 (al); + + ret =__nvme_submit_sync_cmd (q, &cmd,NULL , buf, al,0 , + qid ==0 ?NVME_QID_ANY : qid, +0 , flags); +if (ret >0 ) { + dev_warn(ctrl->device,drivers/nvme/host/auth.c line 385
Quoted 10 lines
drivers/nvme/host/auth.c new file +10
-0+ "qid %d auth_recv failed with status %x\n", qid, ret); + ret = -EIO ; + }else if (ret <0 ) { +dev_err (ctrl->device, +"qid %d auth_recv failed with error %d\n" , qid, ret); + } + +return ret; +} +Why not use something like this ? It reduces the duplicate code and need for macros :-
static int nvme_auth_send_recv_common(bool send, struct
nvme_ctrl*ctrl,
int qid, void *buf,size_tbuflen){ strucy nvme_cmd cmd = { }; blk_mq_req_flags_t flags; struct request_queue *q; int ret; flags = qid == 0 ? 0 : BLK_MQ_REQ_RESERVED | BLK_MQ_REQ_NOWAIT; q = qid == 0 ? ctrl->fabrics_q : ctrl->connect_q; /* auth send/recv share common offset for the various fields in cmd */ cmd.auth_send.opcode = nvme_fabrics_command; cmd.auth_send.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER; cmd.auth_send.spsp0 = 0x01; cmd.auth_send.spsp1 = 0x01; cmd.auth_send.al = cpu_to_le32(buflen); cmd.auth_receive.fctype = send ? nvme_fabrics_type_auth_send : nvme_fabrics_type_auth_receive; ret = nvme_submit_sync_cmd(q, &cmd, NULL, buf, buflen, 0, qid == 0 ? NVME_QID_ANY : qid, 0, flags); if (ret > 0) dev_warn(ctrl->device,
Quoted 10 lines
"qid %d fctype 0x%x failed with status %d\n", qid,
cmd.auth_send.fctype, ret);
else if (ret < 0)
dev_err(ctrl->device,
"qid %d fctype 0x%x failed with error %d\n", qid,
cmd.auth_send.fctype, ret);
return ret;
}Naa. If we were going down that route we should add a new structure for nvme_auth_common, and use that for the common function. And then the wrapper can just cast nvme_auth_send and nvme_auth_recv to the common structure.
I'll give it a go.
Cheers,
Hannes
Signature · 4 lines
Dr. Hannes Reinecke Kernel Storage Architect [email protected] +49 911 74053 688 SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
Hannes Reinecke
maintainerPATCH v9 7/11 nvme-auth: Diffie-Hellman key exchange support
In reply to Hannes ReineckeImplement Diffie-Hellman key exchange using FFDHE groups for NVMe In-Band Authentication.
--- drivers/nvme/host/Kconfig | 1 + drivers/nvme/host/auth.c | 352 +++++++++++++++++++++++++++++++++++++- drivers/nvme/host/auth.h | 8 + 3 files changed, 355 insertions(+), 6 deletions(-)
drivers/nvme/host/Kconfig +1 -0
@@ -98,6 +98,7 @@ config NVME_AUTH select CRYPTO_HMAC select CRYPTO_SHA256 select CRYPTO_SHA512 + select CRYPTO_DH_RFC7919_GROUPS help This provides support for NVMe over Fabrics In-Band Authentication.
drivers/nvme/host/auth.c +346 -6
444 lines not on this page · raw message
drivers/nvme/host/auth.h +8 -0
@@ -28,5 +28,13 @@ struct nvme_dhchap_key *nvme_auth_extract_key(unsigned char *secret, u8 key_hash);void nvme_auth_free_key (struct nvme_dhchap_key *key);u8 *nvme_auth_transform_key (struct nvme_dhchap_key *key,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,u8 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
Hannes Reinecke
maintainerPATCH v9 8/11 nvmet: parse fabrics commands on io queues
In reply to Hannes ReineckeSome fabrics commands can be sent via io queues, so add a new function nvmet_parse_fabrics_io_cmd() and rename the existing nvmet_parse_fabrics_cmd() to nvmet_parse_fabrics_admin_cmd().
--- drivers/nvme/target/admin-cmd.c | 2 +- drivers/nvme/target/core.c | 4 ++++ drivers/nvme/target/fabrics-cmd.c | 17 ++++++++++++++++- drivers/nvme/target/nvmet.h | 3 ++- 4 files changed, 23 insertions(+), 3 deletions(-)
drivers/nvme/target/admin-cmd.c +1 -1
@@ -1017,7 +1017,7 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)u16 ret;if (nvme_is_fabrics (cmd)) -return nvmet_parse_fabrics_cmd (req); +return nvmet_parse_fabrics_admin_cmd (req);if (nvmet_is_disc_subsys (nvmet_req_subsys (req)))return nvmet_parse_discovery_cmd (req);
drivers/nvme/target/core.c +4 -0
@@ -862,8 +862,12 @@ static inline u16 nvmet_io_cmd_check_access(struct nvmet_req *req)static u16 nvmet_parse_io_cmd (struct nvmet_req *req) { +struct nvme_command *cmd = req->cmd;u16 ret; +if (nvme_is_fabrics (cmd)) +return nvmet_parse_fabrics_io_cmd (req); + ret =nvmet_check_ctrl_status (req);if (unlikely (ret))return ret;
drivers/nvme/target/fabrics-cmd.c +16 -1
@@ -82,7 +82,7 @@ static void nvmet_execute_prop_get(struct nvmet_req *req)nvmet_req_complete (req, status); } -u16 nvmet_parse_fabrics_cmd (struct nvmet_req *req) +u16 nvmet_parse_fabrics_admin_cmd (struct nvmet_req *req) {struct nvme_command *cmd = req->cmd;
@@ -103,6 +103,21 @@ u16 nvmet_parse_fabrics_cmd(struct nvmet_req *req)return 0 ; } +u16 nvmet_parse_fabrics_io_cmd (struct nvmet_req *req) +{ +struct nvme_command *cmd = req->cmd; + +switch (cmd->fabrics.fctype) { +default : +pr_debug ("received unknown capsule type 0x%x\n" , + cmd->fabrics.fctype); + req->error_loc = offsetof(struct nvmf_common_command , fctype); +return NVME_SC_INVALID_OPCODE |NVME_SC_DNR ; + } + +return 0 ; +} +static u16 nvmet_install_queue (struct nvmet_ctrl *ctrl,struct nvmet_req *req) {struct nvmf_connect_command *c = &req->cmd->connect;
drivers/nvme/target/nvmet.h +2 -1
@@ -418,7 +418,8 @@ u16 nvmet_file_parse_io_cmd(struct nvmet_req *req);u16 nvmet_bdev_zns_parse_io_cmd (struct nvmet_req *req);u16 nvmet_parse_admin_cmd (struct nvmet_req *req);u16 nvmet_parse_discovery_cmd (struct nvmet_req *req); -u16 nvmet_parse_fabrics_cmd (struct nvmet_req *req); +u16 nvmet_parse_fabrics_admin_cmd (struct nvmet_req *req); +u16 nvmet_parse_fabrics_io_cmd (struct nvmet_req *req);bool nvmet_req_init (struct nvmet_req *req,struct nvmet_cq *cq,struct nvmet_sq *sq,const struct nvmet_fabrics_ops *ops);
Signature · 1 line
2.29.2
Hannes Reinecke
maintainerPATCH v9 9/11 nvmet: Implement basic In-Band Authentication
In reply to Hannes ReineckeImplement NVMe-oF In-Band authentication according to NVMe TPAR 8006. This patch adds three additional configfs entries 'dhchap_key', 'dhchap_ctrl_key', and 'dhchap_hash' to the 'host' configfs directory. The 'dhchap_key' and 'dhchap_ctrl_key' entries need to be in the ASCII format as specified in NVMe Base Specification v2.0 section 8.13.5.8 'Secret representation'.
'dhchap_hash' defaults to 'hmac(sha256)', and can be written to to switch to a different HMAC algorithm.
--- drivers/nvme/target/Kconfig | 12 + drivers/nvme/target/Makefile | 1 + drivers/nvme/target/admin-cmd.c | 2 + drivers/nvme/target/auth.c | 367 ++++++++++++++++++ drivers/nvme/target/configfs.c | 107 +++++- drivers/nvme/target/core.c | 11 + drivers/nvme/target/fabrics-cmd-auth.c | 491 +++++++++++++++++++++++++ drivers/nvme/target/fabrics-cmd.c | 38 +- drivers/nvme/target/nvmet.h | 62 ++++ 9 files changed, 1088 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 +12 -0
@@ -83,3 +83,15 @@ 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 + depends on NVME_AUTH + 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
@@ -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 +2 -0
@@ -1018,6 +1018,8 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)if (nvme_is_fabrics (cmd))return nvmet_parse_fabrics_admin_cmd (req); +if (unlikely (!nvmet_check_auth_status (req))) +return NVME_SC_AUTH_REQUIRED |NVME_SC_DNR ;if (nvmet_is_disc_subsys (nvmet_req_subsys (req)))return nvmet_parse_discovery_cmd (req);
drivers/nvme/target/auth.c new file +367 -0
367 lines not on this page · raw message
drivers/nvme/target/configfs.c +106 -1
136 lines not on this page · raw message
drivers/nvme/target/core.c +11 -0
@@ -792,6 +792,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) {/*
@@ -868,6 +869,9 @@ static u16 nvmet_parse_io_cmd(struct nvmet_req *req)if (nvme_is_fabrics (cmd))return nvmet_parse_fabrics_io_cmd (req); +if (unlikely (!nvmet_check_auth_status (req))) +return NVME_SC_AUTH_REQUIRED |NVME_SC_DNR ; + ret =nvmet_check_ctrl_status (req);if (unlikely (ret))return ret;
@@ -1272,6 +1276,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 ; }
@@ -1462,6 +1471,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_free (&cntlid_ida, ctrl->cntlid);nvmet_async_events_free (ctrl);
drivers/nvme/target/fabrics-cmd-auth.c new file +491 -0
491 lines not on this page · raw message
drivers/nvme/target/fabrics-cmd.c +36 -2
@@ -93,6 +93,14 @@ u16 nvmet_parse_fabrics_admin_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);
@@ -108,6 +116,14 @@ u16 nvmet_parse_fabrics_io_cmd(struct nvmet_req *req)struct nvme_command *cmd = req->cmd;switch (cmd->fabrics.fctype) { +#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);
@@ -188,6 +204,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 ;
@@ -230,18 +247,32 @@ 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 %s controller %d for subsystem %s for NQN %s%s.\n" , +pr_info ("creating %s controller %d for subsystem %s for NQN %s%s%s.\n" ,nvmet_is_disc_subsys (ctrl->subsys) ?"discovery" :"nvm" , ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn, - ctrl->pi_support ?" T10-PI is enabled" :"" ); + 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:
@@ -301,6 +332,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 +62 -0
84 lines not on this page · raw message
Signature · 1 line
2.29.2
Hannes Reinecke
maintainerPATCH v9 10/11 nvmet-auth: Diffie-Hellman key exchange support
In reply to Hannes ReineckeImplement 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 | 157 +++++++++++++++++++++++++ drivers/nvme/target/configfs.c | 31 +++++ drivers/nvme/target/fabrics-cmd-auth.c | 41 +++++-- drivers/nvme/target/nvmet.h | 9 ++ 5 files changed, 231 insertions(+), 8 deletions(-)
drivers/nvme/target/Kconfig +1 -0
@@ -91,6 +91,7 @@ config NVME_TARGET_AUTH select CRYPTO_HMAC select CRYPTO_SHA256 select CRYPTO_SHA512 + select CRYPTO_DH_GROUPS_RFC7919 help This enables support for NVMe over Fabrics In-band Authentication
drivers/nvme/target/auth.c +157 -0
190 lines not on this page · raw message
drivers/nvme/target/configfs.c +31 -0
@@ -1746,10 +1746,41 @@ 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 ==NVME_AUTH_DHGROUP_INVALID ) +return -EINVAL ; +if (dhgroup_id !=NVME_AUTH_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_ctrl_key, &nvmet_host_attr_dhchap_hash, + &nvmet_host_attr_dhchap_dhgroup,NULL , };#endif /* CONFIG_NVME_TARGET_AUTH */
drivers/nvme/target/fabrics-cmd-auth.c +33 -8
84 lines not on this page · raw message
drivers/nvme/target/nvmet.h +9 -0
@@ -225,6 +225,10 @@ struct nvmet_ctrl {struct nvme_dhchap_key *host_key;struct nvme_dhchap_key *ctrl_key;u8 shash_id; +struct crypto_kpp *dh_tfm; +u8 dh_gid; +u8 *dh_key; +size_t dh_keysize;#endif };
@@ -700,6 +704,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,u8 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);
@@ -709,6 +714,10 @@ static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl) {return ctrl->host_key !=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
Hannes Reinecke
maintainerPATCH v9 11/11 nvmet-auth: expire authentication sessions
In reply to Hannes ReineckeEach 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
@@ -218,6 +218,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
@@ -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 ;
@@ -326,8 +339,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
@@ -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
Sagi Grimberg
maintainerQuoted 16 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.
Thanks to Nicolai Stange the crypto DH framework has been upgraded to provide us with a FFDHE implementation; I've updated the patchset to use the ephemeral key generation provided there.
Note that this is just for in-band authentication. Secure concatenation (ie starting TLS with the negotiated parameters) requires a TLS handshake, which the in-kernel TLS implementation does not provide. This is being worked on with a different patchset, and subject to discussion at LSF..
Quoted 8 lines
The nvme-cli support has already been merged; please use the latest nvme-cli git repository to build the most recent version.
A copy of this patchset can be found at
git://git.kernel.org/pub/scm/linux/kernel/git/hare/scsi-devel
branch auth.v9As usual, comments and reviews are welcome.
Hannes, can you resend blktests patch set as well?
IIRC one test there acted funky...