[PATCH 09/12] nvmet-auth: parse dhchap key from configfs attribute
Hannes Reinecke
hare at kernel.org
Fri Apr 25 02:49:24 PDT 2025
When writing a new dhchap key to the 'dhchap_secret' attribute
we should be parsing it directly and only use the pointer to the
inserted key. This avoids having to pass around a pointer with the
actual data. There is only one snag with that; the dhchap controller
secret is stored under the host configfs directory, so we have no
indication as to which subsystem the dhchap secret matches.
So store it with the host NQN as subsystem NQN to indicate that
it's a controller secret applicable for any subsystem for this
host.
Signed-off-by: Hannes Reinecke <hare at kernel.org>
---
drivers/nvme/target/auth.c | 176 +++++++++++++++++++++------------
drivers/nvme/target/configfs.c | 59 ++++++++---
drivers/nvme/target/nvmet.h | 7 +-
3 files changed, 159 insertions(+), 83 deletions(-)
diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
index 8afe571ea0ce..59394587a875 100644
--- a/drivers/nvme/target/auth.c
+++ b/drivers/nvme/target/auth.c
@@ -20,54 +20,76 @@
#include "nvmet.h"
+void nvmet_auth_revoke_key(struct nvmet_host *host, bool set_ctrl)
+{
+ struct key *key = ERR_PTR(-ENOKEY);
+
+ if (set_ctrl) {
+ if (host->dhchap_ctrl_key) {
+ key = host->dhchap_ctrl_key;
+ host->dhchap_ctrl_key = NULL;
+ }
+ } else {
+ if (host->dhchap_key) {
+ key = host->dhchap_key;
+ host->dhchap_key = NULL;
+ }
+ }
+ if (!IS_ERR(key)) {
+ pr_debug("%s: revoke %s key %08x\n",
+ __func__, set_ctrl ? "ctrl" : "host",
+ key_serial(key));
+ key_revoke(key);
+ key_put(key);
+ }
+}
+
int nvmet_auth_set_key(struct nvmet_host *host, const char *secret,
bool set_ctrl)
{
unsigned char key_hash;
- char *dhchap_secret;
+ struct key *key;
+ size_t len;
if (!strlen(secret)) {
- if (set_ctrl) {
- kfree(host->dhchap_ctrl_secret);
- host->dhchap_ctrl_secret = NULL;
- host->dhchap_ctrl_key_hash = 0;
- } else {
- kfree(host->dhchap_secret);
- host->dhchap_secret = NULL;
- host->dhchap_key_hash = 0;
- }
+ nvmet_auth_revoke_key(host, set_ctrl);
return 0;
}
- if (sscanf(secret, "DHHC-1:%hhd:%*s", &key_hash) != 1)
- return -EINVAL;
- if (key_hash > 3) {
- pr_warn("Invalid DH-HMAC-CHAP hash id %d\n",
- key_hash);
- return -EINVAL;
+
+ len = strcspn(secret, "\n");
+ key = nvme_auth_extract_key(NULL, secret, len);
+ if (IS_ERR(key)) {
+ pr_debug("%s: invalid key specification\n", __func__);
+ return PTR_ERR(key);
+ }
+ down_read(&key->sem);
+ if (key_validate(key)) {
+ pr_warn("%s: key %08x invalidated\n",
+ __func__, key_serial(key));
+ up_read(&key->sem);
+ key_put(key);
+ return -EKEYREVOKED;
}
+
+ key_hash = nvme_dhchap_psk_hash(key);
if (key_hash > 0) {
/* Validate selected hash algorithm */
const char *hmac = nvme_auth_hmac_name(key_hash);
if (!crypto_has_shash(hmac, 0, 0)) {
pr_err("DH-HMAC-CHAP hash %s unsupported\n", hmac);
+ up_read(&key->sem);
+ key_put(key);
return -ENOTSUPP;
}
}
- dhchap_secret = kstrdup(secret, GFP_KERNEL);
- if (!dhchap_secret)
- return -ENOMEM;
- down_write(&nvmet_config_sem);
+ up_read(&key->sem);
+ nvmet_auth_revoke_key(host, set_ctrl);
if (set_ctrl) {
- kfree(host->dhchap_ctrl_secret);
- host->dhchap_ctrl_secret = strim(dhchap_secret);
- host->dhchap_ctrl_key_hash = key_hash;
+ host->dhchap_ctrl_key = key;
} else {
- kfree(host->dhchap_secret);
- host->dhchap_secret = strim(dhchap_secret);
- host->dhchap_key_hash = key_hash;
+ host->dhchap_key = key;
}
- up_write(&nvmet_config_sem);
return 0;
}
@@ -145,7 +167,8 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq)
int ret = 0;
struct nvmet_host_link *p;
struct nvmet_host *host = NULL;
- u8 host_hash, ctrl_hash;
+ struct key *key;
+ key_serial_t key_id;
down_read(&nvmet_config_sem);
if (nvmet_is_disc_subsys(ctrl->subsys))
@@ -179,7 +202,7 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq)
goto out_unlock;
}
- if (!host->dhchap_secret) {
+ if (!host->dhchap_key) {
pr_debug("No authentication provided\n");
goto out_unlock;
}
@@ -191,47 +214,68 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq)
ctrl->shash_id = host->dhchap_hash_id;
}
- key_put(ctrl->host_key);
- ctrl->host_key = nvme_auth_extract_key(NULL, host->dhchap_secret,
- strlen(host->dhchap_secret));
- if (IS_ERR(ctrl->host_key)) {
- ret = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
- ctrl->host_key = NULL;
- goto out_free_hash;
+ key = key_get(host->dhchap_key);
+ if (!key) {
+ pr_warn("%s: host key %08x not found\n",
+ __func__, key_serial(host->dhchap_key));
+ goto out_unlock;
}
- host_hash = nvme_dhchap_psk_hash(ctrl->host_key);
- pr_debug("%s: using hash %s key %u\n", __func__,
- ctrl_hash > 0 ?
- nvme_auth_hmac_name(ctrl_hash) : "none",
- key_serial(ctrl->host_key));
-
- key_put(ctrl->ctrl_key);
- if (!host->dhchap_ctrl_secret) {
- ctrl->ctrl_key = NULL;
+ down_read(&key->sem);
+ ret = key_validate(key);
+ if (!ret) {
+ if (ctrl->host_key) {
+ pr_debug("%s: drop host key %08x\n",
+ __func__, key_serial(ctrl->host_key));
+ key_put(ctrl->host_key);
+ }
+ ctrl->host_key = key;
+ }
+ key_id = key_serial(key);
+ up_read(&key->sem);
+ if (ret) {
+ pr_debug("key id %08x invalidated\n", key_id);
+ key_put(key);
+ key = ERR_PTR(-EKEYREVOKED);
+ }
+ pr_debug("%s: using dhchap hash %s key %08x\n", __func__,
+ nvme_auth_hmac_name(ctrl->shash_id), key_id);
+
+ if (!host->dhchap_ctrl_key) {
+ if (ctrl->ctrl_key) {
+ pr_debug("%s: drop ctrl key %08x\n",
+ __func__, key_serial(ctrl->ctrl_key));
+ key_put(ctrl->ctrl_key);
+ ctrl->ctrl_key = NULL;
+ }
goto out_unlock;
}
- ctrl->ctrl_key = nvme_auth_extract_key(NULL, host->dhchap_ctrl_secret,
- strlen(host->dhchap_ctrl_secret));
- if (IS_ERR(ctrl->ctrl_key)) {
- ret = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
- ctrl->ctrl_key = NULL;
- goto out_free_hash;
+ key = key_get(host->dhchap_ctrl_key);
+ if (!key) {
+ pr_warn("%s: ctrl key %08x not found\n",
+ __func__, key_serial(host->dhchap_ctrl_key));
+ goto out_unlock;
}
- ctrl_hash = nvme_dhchap_psk_hash(ctrl->ctrl_key);
- pr_debug("%s: using ctrl hash %s key %u\n", __func__,
- ctrl_hash > 0 ?
- nvme_auth_hmac_name(ctrl_hash) : "none",
- key_serial(ctrl->ctrl_key));
-
-out_free_hash:
- if (ret) {
- if (ctrl->host_key) {
- key_put(ctrl->host_key);
- ctrl->host_key = NULL;
+ down_read(&key->sem);
+ ret = key_validate(key);
+ if (!ret) {
+ if (ctrl->ctrl_key) {
+ pr_debug("%s: drop ctrl key %08x\n",
+ __func__, key_serial(ctrl->ctrl_key));
+ key_put(ctrl->ctrl_key);
}
- ctrl->shash_id = 0;
+ ctrl->ctrl_key = key;
}
+ key_id = key_serial(key);
+ up_read(&key->sem);
+ if (ret) {
+ pr_debug("ctrl key id %08x invalidated\n", key_id);
+ key_put(key);
+ goto out_unlock;
+ }
+ pr_debug("%s: using dhchap ctrl hash %s key %08x\n", __func__,
+ nvme_auth_hmac_name(ctrl->shash_id), key_id);
+
out_unlock:
up_read(&nvmet_config_sem);
@@ -265,12 +309,14 @@ void nvmet_destroy_auth(struct nvmet_ctrl *ctrl)
ctrl->dh_key = NULL;
if (ctrl->host_key) {
- key_revoke(ctrl->host_key);
+ pr_debug("%s: drop host key %08x\n",
+ __func__, key_serial(ctrl->host_key));
key_put(ctrl->host_key);
ctrl->host_key = NULL;
}
if (ctrl->ctrl_key) {
- key_revoke(ctrl->ctrl_key);
+ pr_debug("%s: drop ctrl key %08x\n",
+ __func__, key_serial(ctrl->ctrl_key));
key_put(ctrl->ctrl_key);
ctrl->ctrl_key = NULL;
}
diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index e44ef69dffc2..1b6ca46d1a64 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -14,6 +14,7 @@
#include <linux/pci-p2pdma.h>
#ifdef CONFIG_NVME_TARGET_AUTH
#include <linux/nvme-auth.h>
+#include <linux/key-type.h>
#endif
#include <linux/nvme-keyring.h>
#include <crypto/hash.h>
@@ -2100,15 +2101,29 @@ static struct config_group nvmet_ports_group;
static ssize_t nvmet_host_dhchap_key_show(struct config_item *item,
char *page)
{
- u8 *dhchap_secret;
+ struct nvmet_host *host = to_host(item);
+ struct key *key;
ssize_t ret;
down_read(&nvmet_config_sem);
- dhchap_secret = to_host(item)->dhchap_secret;
- if (!dhchap_secret)
- ret = sprintf(page, "\n");
- else
- ret = sprintf(page, "%s\n", dhchap_secret);
+ key = key_get(host->dhchap_key);
+ if (!key) {
+ page[0] = '\0';
+ ret = 0;
+ } else {
+ down_read(&key->sem);
+ if (key_validate(key))
+ ret = sprintf(page, "<invalidated>\n");
+ else {
+ ret = key->type->read(key, page, PAGE_SIZE);
+ if (ret > 0) {
+ page[ret] = '\n';
+ ret++;
+ }
+ }
+ up_read(&key->sem);
+ key_put(key);
+ }
up_read(&nvmet_config_sem);
return ret;
}
@@ -2133,15 +2148,29 @@ CONFIGFS_ATTR(nvmet_host_, dhchap_key);
static ssize_t nvmet_host_dhchap_ctrl_key_show(struct config_item *item,
char *page)
{
- u8 *dhchap_secret = to_host(item)->dhchap_ctrl_secret;
+ struct nvmet_host *host = to_host(item);
+ struct key *key;
ssize_t ret;
down_read(&nvmet_config_sem);
- dhchap_secret = to_host(item)->dhchap_ctrl_secret;
- if (!dhchap_secret)
- ret = sprintf(page, "\n");
- else
- ret = sprintf(page, "%s\n", dhchap_secret);
+ key = key_get(host->dhchap_ctrl_key);
+ if (!key) {
+ page[0] = '\0';
+ ret = 0;
+ } else {
+ down_read(&key->sem);
+ if (key_validate(key))
+ ret = sprintf(page, "<invalidated>\n");
+ else {
+ ret = key->type->read(key, page, PAGE_SIZE);
+ if (ret > 0) {
+ page[ret] = '\n';
+ ret++;
+ }
+ }
+ up_read(&key->sem);
+ key_put(key);
+ }
up_read(&nvmet_config_sem);
return ret;
}
@@ -2152,7 +2181,9 @@ static ssize_t nvmet_host_dhchap_ctrl_key_store(struct config_item *item,
struct nvmet_host *host = to_host(item);
int ret;
+ down_write(&nvmet_config_sem);
ret = nvmet_auth_set_key(host, page, true);
+ up_write(&nvmet_config_sem);
/*
* Re-authentication is a soft state, so keep the
* current authentication valid until the host
@@ -2233,8 +2264,8 @@ static void nvmet_host_release(struct config_item *item)
struct nvmet_host *host = to_host(item);
#ifdef CONFIG_NVME_TARGET_AUTH
- kfree(host->dhchap_secret);
- kfree(host->dhchap_ctrl_secret);
+ nvmet_auth_revoke_key(host, false);
+ nvmet_auth_revoke_key(host, true);
#endif
kfree(host);
}
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 1aa9d2176f75..d23f9d122777 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -374,10 +374,8 @@ static inline struct nvmet_subsys *namespaces_to_subsys(
struct nvmet_host {
struct config_group group;
- u8 *dhchap_secret;
- u8 *dhchap_ctrl_secret;
- u8 dhchap_key_hash;
- u8 dhchap_ctrl_key_hash;
+ struct key *dhchap_key;
+ struct key *dhchap_ctrl_key;
u8 dhchap_hash_id;
u8 dhchap_dhgroup_id;
};
@@ -880,6 +878,7 @@ u32 nvmet_auth_send_data_len(struct nvmet_req *req);
void nvmet_execute_auth_send(struct nvmet_req *req);
u32 nvmet_auth_receive_data_len(struct nvmet_req *req);
void nvmet_execute_auth_receive(struct nvmet_req *req);
+void nvmet_auth_revoke_key(struct nvmet_host *host, bool set_ctrl);
int nvmet_auth_set_key(struct nvmet_host *host, const char *secret,
bool set_ctrl);
int nvmet_auth_set_host_hash(struct nvmet_host *host, const char *hash);
--
2.35.3
More information about the Linux-nvme
mailing list