[PATCH] nvmet: verify the hostid when looking up a controller

Chuyf26 <[email protected]>
Newsgroups org.infradead.lists.linux-nvme
Message-ID <[email protected]>
nvmet_ctrl_find_get() matches controllers by cntlid and hostnqn only.
The hostnqn is not a secret: any host that can reach the subsystem can
put an arbitrary NQN into its connect data.  A malicious host knowing
or guessing the NQN and controller ID of another host (controller IDs
are allocated sequentially) can therefore attach its queues to that
host's controller, issue commands on its behalf, or disturb its I/O by
tearing down the shared controller state.

The path is: an unauthenticated host sends a fabrics CONNECT command,
nvmet_execute_fabrics_connect() parses the connect data capsule and
hands subsysnqn, hostnqn and cntlid to nvmet_ctrl_find_get(), which
returns the first matching controller.  The connect data also carries
the host's hostid, but it is never compared.

Also require the hostid from the connect data to match the controller's
hostid.  The hostid identifies the host installation and is generated
locally, so it cannot be chosen by an attacker to impersonate another
host.  Controllers that were created without a hostid keep the old
behaviour.

Fixes: a07b4970f464 ("nvmet: add a generic NVMe target")
Reported-by: Abaci <[email protected]>
Assisted-by: abaci:qwen3.8-max
Signed-off-by: Chuyf26 <[email protected]>
---
 drivers/nvme/target/core.c        | 12 ++++++++++++
 drivers/nvme/target/fabrics-cmd.c |  2 +-
 drivers/nvme/target/nvmet.h       |  1 +
 3 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index 7b6309d866be..64a5fa951bca 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -1287,6 +1287,7 @@ static void nvmet_init_cap(struct nvmet_ctrl *ctrl)
 
 struct nvmet_ctrl *nvmet_ctrl_find_get(const char *subsysnqn,
 				       const char *hostnqn, u16 cntlid,
+				       const uuid_t *hostid,
 				       struct nvmet_req *req)
 {
 	struct nvmet_ctrl *ctrl = NULL;
@@ -1307,6 +1308,17 @@ struct nvmet_ctrl *nvmet_ctrl_find_get(const char *subsysnqn,
 				pr_warn("hostnqn mismatch.\n");
 				continue;
 			}
+			/* hostnqn alone does not identify a host: any host
+			 * able to connect to the subsystem can claim it.
+			 * Also require the hostid, which identifies the host
+			 * installation, to match.  Accept a nil hostid only
+			 * if the controller was created without one.
+			 */
+			if (!uuid_is_null(&ctrl->hostid) &&
+			    !uuid_equal(&ctrl->hostid, hostid)) {
+				pr_warn("hostid mismatch.\n");
+				continue;
+			}
 			if (!kref_get_unless_zero(&ctrl->ref))
 				continue;
 
diff --git a/drivers/nvme/target/fabrics-cmd.c b/drivers/nvme/target/fabrics-cmd.c
index e008f162ce68..9e0573b2aec9 100644
--- a/drivers/nvme/target/fabrics-cmd.c
+++ b/drivers/nvme/target/fabrics-cmd.c
@@ -311,7 +311,7 @@ static void nvmet_execute_io_connect(struct nvmet_req *req)
 	d->subsysnqn[NVMF_NQN_FIELD_LEN - 1] = '\0';
 	d->hostnqn[NVMF_NQN_FIELD_LEN - 1] = '\0';
 	ctrl = nvmet_ctrl_find_get(d->subsysnqn, d->hostnqn,
-				   le16_to_cpu(d->cntlid), req);
+				   le16_to_cpu(d->cntlid), &d->hostid, req);
 	if (!ctrl) {
 		status = NVME_SC_CONNECT_INVALID_PARAM | NVME_SC_DNR;
 		goto out;
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 9aef41c26415..137860ec4b28 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -532,6 +532,7 @@ u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn,
 		uuid_t *hostid);
 struct nvmet_ctrl *nvmet_ctrl_find_get(const char *subsysnqn,
 				       const char *hostnqn, u16 cntlid,
+				       const uuid_t *hostid,
 				       struct nvmet_req *req);
 void nvmet_ctrl_put(struct nvmet_ctrl *ctrl);
 u16 nvmet_check_ctrl_status(struct nvmet_req *req);
-- 
2.43.5
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.