Commit e88a7595 authored by Hannes Reinecke's avatar Hannes Reinecke Committed by Keith Busch
Browse files

nvme-tcp: request secure channel concatenation



Add a fabrics option 'concat' to request secure channel concatenation as
specified the NVME Base Specification v2.1, section 8.3.4.3: Secure Channel
Concatenation.
When secure channel concatenation is enabled a 'generated PSK' is inserted
into the keyring such that it's available after reset.

Signed-off-by: default avatarHannes Reinecke <hare@kernel.org>
Reviewed-by: default avatarSagi Grimberg <sagi@grimberg.me>
Signed-off-by: default avatarKeith Busch <kbusch@kernel.org>
parent 62eb8932
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -109,7 +109,7 @@ config NVME_HOST_AUTH
	bool "NVMe over Fabrics In-Band Authentication in host side"
	depends on NVME_CORE
	select NVME_AUTH
	select NVME_KEYRING if NVME_TCP_TLS
	select NVME_KEYRING
	help
	  This provides support for NVMe over Fabrics In-Band Authentication in
	  host side.
+112 −3
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include "nvme.h"
#include "fabrics.h"
#include <linux/nvme-auth.h>
#include <linux/nvme-keyring.h>

#define CHAP_BUF_SIZE 4096
static struct kmem_cache *nvme_chap_buf_cache;
@@ -131,7 +132,13 @@ static int nvme_auth_set_dhchap_negotiate_data(struct nvme_ctrl *ctrl,
	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 */
	if (ctrl->opts->concat && chap->qid == 0) {
		if (ctrl->opts->tls_key)
			data->sc_c = NVME_AUTH_SECP_REPLACETLSPSK;
		else
			data->sc_c = NVME_AUTH_SECP_NEWTLSPSK;
	} else
		data->sc_c = NVME_AUTH_SECP_NOSC;
	data->napd = 1;
	data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID;
	data->auth_protocol[0].dhchap.halen = 3;
@@ -311,8 +318,9 @@ static int nvme_auth_set_dhchap_reply_data(struct nvme_ctrl *ctrl,
	data->hl = chap->hash_len;
	data->dhvlen = cpu_to_le16(chap->host_key_len);
	memcpy(data->rval, chap->response, chap->hash_len);
	if (ctrl->ctrl_key) {
	if (ctrl->ctrl_key)
		chap->bi_directional = true;
	if (ctrl->ctrl_key || ctrl->opts->concat) {
		get_random_bytes(chap->c2, chap->hash_len);
		data->cvalid = 1;
		memcpy(data->rval + chap->hash_len, chap->c2,
@@ -322,6 +330,9 @@ static int nvme_auth_set_dhchap_reply_data(struct nvme_ctrl *ctrl,
	} else {
		memset(chap->c2, 0, chap->hash_len);
	}
	if (ctrl->opts->concat)
		chap->s2 = 0;
	else
		chap->s2 = nvme_auth_get_seqnum();
	data->seqnum = cpu_to_le32(chap->s2);
	if (chap->host_key_len) {
@@ -677,6 +688,92 @@ static void nvme_auth_free_dhchap(struct nvme_dhchap_queue_context *chap)
		crypto_free_kpp(chap->dh_tfm);
}

void nvme_auth_revoke_tls_key(struct nvme_ctrl *ctrl)
{
	dev_dbg(ctrl->device, "Wipe generated TLS PSK %08x\n",
		key_serial(ctrl->opts->tls_key));
	key_revoke(ctrl->opts->tls_key);
	key_put(ctrl->opts->tls_key);
	ctrl->opts->tls_key = NULL;
}
EXPORT_SYMBOL_GPL(nvme_auth_revoke_tls_key);

static int nvme_auth_secure_concat(struct nvme_ctrl *ctrl,
				   struct nvme_dhchap_queue_context *chap)
{
	u8 *psk, *digest, *tls_psk;
	struct key *tls_key;
	size_t psk_len;
	int ret = 0;

	if (!chap->sess_key) {
		dev_warn(ctrl->device,
			 "%s: qid %d no session key negotiated\n",
			 __func__, chap->qid);
		return -ENOKEY;
	}

	if (chap->qid) {
		dev_warn(ctrl->device,
			 "qid %d: secure concatenation not supported on I/O queues\n",
			 chap->qid);
		return -EINVAL;
	}
	ret = nvme_auth_generate_psk(chap->hash_id, chap->sess_key,
				     chap->sess_key_len,
				     chap->c1, chap->c2,
				     chap->hash_len, &psk, &psk_len);
	if (ret) {
		dev_warn(ctrl->device,
			 "%s: qid %d failed to generate PSK, error %d\n",
			 __func__, chap->qid, ret);
		return ret;
	}
	dev_dbg(ctrl->device,
		  "%s: generated psk %*ph\n", __func__, (int)psk_len, psk);

	ret = nvme_auth_generate_digest(chap->hash_id, psk, psk_len,
					ctrl->opts->subsysnqn,
					ctrl->opts->host->nqn, &digest);
	if (ret) {
		dev_warn(ctrl->device,
			 "%s: qid %d failed to generate digest, error %d\n",
			 __func__, chap->qid, ret);
		goto out_free_psk;
	};
	dev_dbg(ctrl->device, "%s: generated digest %s\n",
		 __func__, digest);
	ret = nvme_auth_derive_tls_psk(chap->hash_id, psk, psk_len,
				       digest, &tls_psk);
	if (ret) {
		dev_warn(ctrl->device,
			 "%s: qid %d failed to derive TLS psk, error %d\n",
			 __func__, chap->qid, ret);
		goto out_free_digest;
	};

	tls_key = nvme_tls_psk_refresh(ctrl->opts->keyring,
				       ctrl->opts->host->nqn,
				       ctrl->opts->subsysnqn, chap->hash_id,
				       tls_psk, psk_len, digest);
	if (IS_ERR(tls_key)) {
		ret = PTR_ERR(tls_key);
		dev_warn(ctrl->device,
			 "%s: qid %d failed to insert generated key, error %d\n",
			 __func__, chap->qid, ret);
		tls_key = NULL;
	}
	kfree_sensitive(tls_psk);
	if (ctrl->opts->tls_key)
		nvme_auth_revoke_tls_key(ctrl);
	ctrl->opts->tls_key = tls_key;
out_free_digest:
	kfree_sensitive(digest);
out_free_psk:
	kfree_sensitive(psk);
	return ret;
}

static void nvme_queue_auth_work(struct work_struct *work)
{
	struct nvme_dhchap_queue_context *chap =
@@ -833,6 +930,13 @@ static void nvme_queue_auth_work(struct work_struct *work)
	}
	if (!ret) {
		chap->error = 0;
		if (ctrl->opts->concat &&
		    (ret = nvme_auth_secure_concat(ctrl, chap))) {
			dev_warn(ctrl->device,
				 "%s: qid %d failed to enable secure concatenation\n",
				 __func__, chap->qid);
			chap->error = ret;
		}
		return;
	}

@@ -912,6 +1016,11 @@ static void nvme_ctrl_auth_work(struct work_struct *work)
			 "qid 0: authentication failed\n");
		return;
	}
	/*
	 * Only run authentication on the admin queue for secure concatenation.
	 */
	if (ctrl->opts->concat)
		return;

	for (q = 1; q < ctrl->queue_count; q++) {
		ret = nvme_auth_negotiate(ctrl, q);
+31 −3
Original line number Diff line number Diff line
@@ -472,8 +472,9 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl)
	result = le32_to_cpu(res.u32);
	ctrl->cntlid = result & 0xFFFF;
	if (result & (NVME_CONNECT_AUTHREQ_ATR | NVME_CONNECT_AUTHREQ_ASCR)) {
		/* Secure concatenation is not implemented */
		if (result & NVME_CONNECT_AUTHREQ_ASCR) {
		/* Check for secure concatenation */
		if ((result & NVME_CONNECT_AUTHREQ_ASCR) &&
		    !ctrl->opts->concat) {
			dev_warn(ctrl->device,
				 "qid 0: secure concatenation is not supported\n");
			ret = -EOPNOTSUPP;
@@ -550,7 +551,7 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid)
		/* Secure concatenation is not implemented */
		if (result & NVME_CONNECT_AUTHREQ_ASCR) {
			dev_warn(ctrl->device,
				 "qid 0: secure concatenation is not supported\n");
				 "qid %d: secure concatenation is not supported\n", qid);
			ret = -EOPNOTSUPP;
			goto out_free_data;
		}
@@ -706,6 +707,7 @@ static const match_table_t opt_tokens = {
#endif
#ifdef CONFIG_NVME_TCP_TLS
	{ NVMF_OPT_TLS,			"tls"			},
	{ NVMF_OPT_CONCAT,		"concat"		},
#endif
	{ NVMF_OPT_ERR,			NULL			}
};
@@ -735,6 +737,7 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
	opts->tls = false;
	opts->tls_key = NULL;
	opts->keyring = NULL;
	opts->concat = false;

	options = o = kstrdup(buf, GFP_KERNEL);
	if (!options)
@@ -1053,6 +1056,14 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
			}
			opts->tls = true;
			break;
		case NVMF_OPT_CONCAT:
			if (!IS_ENABLED(CONFIG_NVME_TCP_TLS)) {
				pr_err("TLS is not supported\n");
				ret = -EINVAL;
				goto out;
			}
			opts->concat = true;
			break;
		default:
			pr_warn("unknown parameter or missing value '%s' in ctrl creation request\n",
				p);
@@ -1079,6 +1090,23 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
			pr_warn("failfast tmo (%d) larger than controller loss tmo (%d)\n",
				opts->fast_io_fail_tmo, ctrl_loss_tmo);
	}
	if (opts->concat) {
		if (opts->tls) {
			pr_err("Secure concatenation over TLS is not supported\n");
			ret = -EINVAL;
			goto out;
		}
		if (opts->tls_key) {
			pr_err("Cannot specify a TLS key for secure concatenation\n");
			ret = -EINVAL;
			goto out;
		}
		if (!opts->dhchap_secret) {
			pr_err("Need to enable DH-CHAP for secure concatenation\n");
			ret = -EINVAL;
			goto out;
		}
	}

	opts->host = nvmf_host_add(hostnqn, &hostid);
	if (IS_ERR(opts->host)) {
+3 −0
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ enum {
	NVMF_OPT_TLS		= 1 << 25,
	NVMF_OPT_KEYRING	= 1 << 26,
	NVMF_OPT_TLS_KEY	= 1 << 27,
	NVMF_OPT_CONCAT		= 1 << 28,
};

/**
@@ -101,6 +102,7 @@ enum {
 * @keyring:    Keyring to use for key lookups
 * @tls_key:    TLS key for encrypted connections (TCP)
 * @tls:        Start TLS encrypted connections (TCP)
 * @concat:     Enabled Secure channel concatenation (TCP)
 * @disable_sqflow: disable controller sq flow control
 * @hdr_digest: generate/verify header digest (TCP)
 * @data_digest: generate/verify data digest (TCP)
@@ -130,6 +132,7 @@ struct nvmf_ctrl_options {
	struct key		*keyring;
	struct key		*tls_key;
	bool			tls;
	bool			concat;
	bool			disable_sqflow;
	bool			hdr_digest;
	bool			data_digest;
+2 −0
Original line number Diff line number Diff line
@@ -1147,6 +1147,7 @@ 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);
void nvme_auth_revoke_tls_key(struct nvme_ctrl *ctrl);
#else
static inline int nvme_auth_init_ctrl(struct nvme_ctrl *ctrl)
{
@@ -1169,6 +1170,7 @@ static inline int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid)
	return -EPROTONOSUPPORT;
}
static inline void nvme_auth_free(struct nvme_ctrl *ctrl) {};
static inline void nvme_auth_revoke_tls_key(struct nvme_ctrl *ctrl) {};
#endif

u32 nvme_command_effects(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
Loading