nvmet-tcp: support secure channel concatenation

Evaluate the SC_C flag during DH-CHAP-HMAC negotiation to check if secure
concatenation as specified in the NVMe Base Specification v2.1, section
8.3.4.3: "Secure Channel Concatenationand" is requested. If requested the
generated PSK is inserted into the keyring once negotiation has finished
allowing for an encrypted connection once the admin queue is restarted.

Signed-off-by: Hannes Reinecke <hare@kernel.org>
Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
Signed-off-by: Keith Busch <kbusch@kernel.org>
This commit is contained in:
Hannes Reinecke
2025-02-24 13:38:17 +01:00
committed by Keith Busch
parent 5032167264
commit fa2e0f8bbc
6 changed files with 200 additions and 14 deletions

View File

@@ -15,6 +15,7 @@
#include <linux/ctype.h>
#include <linux/random.h>
#include <linux/nvme-auth.h>
#include <linux/nvme-keyring.h>
#include <linux/unaligned.h>
#include "nvmet.h"
@@ -165,6 +166,11 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq)
goto out_unlock;
}
if (nvmet_queue_tls_keyid(sq)) {
pr_debug("host %s tls enabled\n", ctrl->hostnqn);
goto out_unlock;
}
ret = nvmet_setup_dhgroup(ctrl, host->dhchap_dhgroup_id);
if (ret < 0) {
pr_warn("Failed to setup DH group");
@@ -233,6 +239,9 @@ out_unlock:
void nvmet_auth_sq_free(struct nvmet_sq *sq)
{
cancel_delayed_work(&sq->auth_expired_work);
#ifdef CONFIG_NVME_TARGET_TCP_TLS
sq->tls_key = 0;
#endif
kfree(sq->dhchap_c1);
sq->dhchap_c1 = NULL;
kfree(sq->dhchap_c2);
@@ -261,6 +270,12 @@ void nvmet_destroy_auth(struct nvmet_ctrl *ctrl)
nvme_auth_free_key(ctrl->ctrl_key);
ctrl->ctrl_key = NULL;
}
#ifdef CONFIG_NVME_TARGET_TCP_TLS
if (ctrl->tls_key) {
key_put(ctrl->tls_key);
ctrl->tls_key = NULL;
}
#endif
}
bool nvmet_check_auth_status(struct nvmet_req *req)
@@ -542,3 +557,58 @@ int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
return ret;
}
void nvmet_auth_insert_psk(struct nvmet_sq *sq)
{
int hash_len = nvme_auth_hmac_hash_len(sq->ctrl->shash_id);
u8 *psk, *digest, *tls_psk;
size_t psk_len;
int ret;
#ifdef CONFIG_NVME_TARGET_TCP_TLS
struct key *tls_key = NULL;
#endif
ret = nvme_auth_generate_psk(sq->ctrl->shash_id,
sq->dhchap_skey,
sq->dhchap_skey_len,
sq->dhchap_c1, sq->dhchap_c2,
hash_len, &psk, &psk_len);
if (ret) {
pr_warn("%s: ctrl %d qid %d failed to generate PSK, error %d\n",
__func__, sq->ctrl->cntlid, sq->qid, ret);
return;
}
ret = nvme_auth_generate_digest(sq->ctrl->shash_id, psk, psk_len,
sq->ctrl->subsysnqn,
sq->ctrl->hostnqn, &digest);
if (ret) {
pr_warn("%s: ctrl %d qid %d failed to generate digest, error %d\n",
__func__, sq->ctrl->cntlid, sq->qid, ret);
goto out_free_psk;
}
ret = nvme_auth_derive_tls_psk(sq->ctrl->shash_id, psk, psk_len,
digest, &tls_psk);
if (ret) {
pr_warn("%s: ctrl %d qid %d failed to derive TLS PSK, error %d\n",
__func__, sq->ctrl->cntlid, sq->qid, ret);
goto out_free_digest;
}
#ifdef CONFIG_NVME_TARGET_TCP_TLS
tls_key = nvme_tls_psk_refresh(NULL, sq->ctrl->hostnqn, sq->ctrl->subsysnqn,
sq->ctrl->shash_id, tls_psk, psk_len, digest);
if (IS_ERR(tls_key)) {
pr_warn("%s: ctrl %d qid %d failed to refresh key, error %ld\n",
__func__, sq->ctrl->cntlid, sq->qid, PTR_ERR(tls_key));
tls_key = NULL;
kfree_sensitive(tls_psk);
}
if (sq->ctrl->tls_key)
key_put(sq->ctrl->tls_key);
sq->ctrl->tls_key = tls_key;
#endif
out_free_digest:
kfree_sensitive(digest);
out_free_psk:
kfree_sensitive(psk);
}