[PATCH 06/11] nvme: Implement In-Band authentication

Hannes Reinecke hare at suse.de
Wed Apr 27 23:05:38 PDT 2022


On 4/27/22 19:59, Nayak, Prashanth wrote:
> Hi Hannes
> 
>> On Mar 28, 2022, at 9:39 AM, Hannes Reinecke <hare at suse.de> wrote:
>>
>> 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 <hare at suse.de>
>> ---
>> drivers/nvme/host/Kconfig   |   11 +
>> drivers/nvme/host/Makefile  |    1 +
>> drivers/nvme/host/auth.c    | 1123 +++++++++++++++++++++++++++++++++++
>> 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, 1452 insertions(+), 7 deletions(-)
>> create mode 100644 drivers/nvme/host/auth.c
>> create mode 100644 drivers/nvme/host/auth.h
>>
[ .. ]
>> +static int nvme_auth_set_dhchap_failure2_data(struct nvme_ctrl *ctrl,
>> +		struct nvme_dhchap_queue_context *chap)
>> +{
>> +	struct nvmf_auth_dhchap_failure_data *data = chap->buf;
>> +	size_t size = sizeof(*data);
>> +
>> +	memset(chap->buf, 0, size);
>> +	data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
> 
> Auth_type should be NVME_AUTH_COMMON_MESSAGES.
> 

Right.

>> +	data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
>> +	data->t_id = cpu_to_le16(chap->transaction);
>> +	data->rescode = NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED;
>> +	data->rescode_exp = chap->status;
>> +
>> +	return size;
>> +}
>> +
>> +static int nvme_auth_dhchap_setup_host_response(struct nvme_ctrl *ctrl,
>> +		struct nvme_dhchap_queue_context *chap)
>> +{
>> +	SHASH_DESC_ON_STACK(shash, chap->shash_tfm);
>> +	u8 buf[4], *challenge = chap->c1;
>> +	int ret;
>> +
>> +	dev_dbg(ctrl->device, "%s: qid %d host response seq %u transaction %d\n",
>> +		__func__, chap->qid, chap->s1, chap->transaction);
>> +
>> +	if (!chap->host_response) {
>> +		chap->host_response = nvme_auth_transform_key(ctrl->host_key,
>> +						ctrl->opts->host->nqn);
>> +		if (IS_ERR(chap->host_response)) {
>> +			ret = PTR_ERR(chap->host_response);
>> +			chap->host_response = NULL;
>> +			return ret;
>> +		}
>> +	} else {
>> +		dev_dbg(ctrl->device, "%s: qid %d re-using host response\n",
>> +			__func__, chap->qid);
>> +	}
>> +
>> +	ret = crypto_shash_setkey(chap->shash_tfm,
>> +			chap->host_response, ctrl->host_key->len);
>> +	if (ret) {
>> +		dev_warn(ctrl->device, "qid %d: failed to set key, error %d\n",
>> +			 chap->qid, ret);
>> +		goto out;
>> +	}
>> +
>> +	shash->tfm = chap->shash_tfm;
>> +	ret = crypto_shash_init(shash);
>> +	if (ret)
>> +		goto out;
>> +	ret = crypto_shash_update(shash, challenge, chap->hash_len);
>> +	if (ret)
>> +		goto out;
>> +	put_unaligned_le32(chap->s1, buf);
>> +	ret = crypto_shash_update(shash, buf, 4);
>> +	if (ret)
>> +		goto out;
>> +	put_unaligned_le16(chap->transaction, buf);
>> +	ret = crypto_shash_update(shash, buf, 2);
>> +	if (ret)
>> +		goto out;
>> +	memset(buf, 0, sizeof(buf));
>> +	ret = crypto_shash_update(shash, buf, 1);
>> +	if (ret)
>> +		goto out;
>> +	ret = crypto_shash_update(shash, "HostHost", 8);
>> +	if (ret)
>> +		goto out;
>> +	ret = crypto_shash_update(shash, ctrl->opts->host->nqn,
>> +				  strlen(ctrl->opts->host->nqn));
>> +	if (ret)
>> +		goto out;
>> +	ret = crypto_shash_update(shash, buf, 1);
>> +	if (ret)
>> +		goto out;
>> +	ret = crypto_shash_update(shash, ctrl->opts->subsysnqn,
>> +			    strlen(ctrl->opts->subsysnqn));
>> +	if (ret)
>> +		goto out;
>> +	ret = crypto_shash_final(shash, chap->response);
>> +out:
>> +	if (challenge != chap->c1)
>> +		kfree(challenge);
>> +	return ret;
>> +}
>> +
>> +static int nvme_auth_dhchap_setup_ctrl_response(struct nvme_ctrl *ctrl,
>> +		struct nvme_dhchap_queue_context *chap)
>> +{
>> +	SHASH_DESC_ON_STACK(shash, chap->shash_tfm);
>> +	u8 *ctrl_response;
>> +	u8 buf[4], *challenge = chap->c2;
>> +	int ret;
>> +
>> +	ctrl_response = nvme_auth_transform_key(ctrl->ctrl_key,
>> +				ctrl->opts->subsysnqn);
>> +	if (IS_ERR(ctrl_response)) {
>> +		ret = PTR_ERR(ctrl_response);
>> +		return ret;
>> +	}
>> +	ret = crypto_shash_setkey(chap->shash_tfm,
>> +			ctrl_response, ctrl->ctrl_key->len);
>> +	if (ret) {
>> +		dev_warn(ctrl->device, "qid %d: failed to set key, error %d\n",
>> +			 chap->qid, ret);
>> +		goto out;
>> +	}
>> +
>> +	dev_dbg(ctrl->device, "%s: qid %d ctrl response seq %u transaction %d\n",
>> +		__func__, chap->qid, chap->s2, chap->transaction);
>> +	dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n",
>> +		__func__, chap->qid, (int)chap->hash_len, challenge);
>> +	dev_dbg(ctrl->device, "%s: qid %d subsysnqn %s\n",
>> +		__func__, chap->qid, ctrl->opts->subsysnqn);
>> +	dev_dbg(ctrl->device, "%s: qid %d hostnqn %s\n",
>> +		__func__, chap->qid, ctrl->opts->host->nqn);
>> +	shash->tfm = chap->shash_tfm;
>> +	ret = crypto_shash_init(shash);
>> +	if (ret)
>> +		goto out;
>> +	ret = crypto_shash_update(shash, challenge, chap->hash_len);
>> +	if (ret)
>> +		goto out;
>> +	put_unaligned_le32(chap->s2, buf);
>> +	ret = crypto_shash_update(shash, buf, 4);
>> +	if (ret)
>> +		goto out;
>> +	put_unaligned_le16(chap->transaction, buf);
>> +	ret = crypto_shash_update(shash, buf, 2);
>> +	if (ret)
>> +		goto out;
>> +	memset(buf, 0, 4);
>> +	ret = crypto_shash_update(shash, buf, 1);
>> +	if (ret)
>> +		goto out;
>> +	ret = crypto_shash_update(shash, "Controller", 10);
>> +	if (ret)
>> +		goto out;
>> +	ret = crypto_shash_update(shash, ctrl->opts->subsysnqn,
>> +				  strlen(ctrl->opts->subsysnqn));
>> +	if (ret)
>> +		goto out;
>> +	ret = crypto_shash_update(shash, buf, 1);
>> +	if (ret)
>> +		goto out;
>> +	ret = crypto_shash_update(shash, ctrl->opts->host->nqn,
>> +				  strlen(ctrl->opts->host->nqn));
>> +	if (ret)
>> +		goto out;
>> +	ret = crypto_shash_final(shash, chap->response);
>> +out:
>> +	if (challenge != chap->c2)
>> +		kfree(challenge);
>> +	return ret;
>> +}
>> +
>> +int nvme_auth_generate_key(u8 *secret, struct nvme_dhchap_key **ret_key)
>> +{
>> +	struct nvme_dhchap_key *key;
>> +	u8 key_hash;
>> +
>> +	if (!secret) {
>> +		*ret_key = NULL;
>> +		return 0;
>> +	}
>> +
>> +	if (sscanf(secret, "DHHC-1:%hhd:%*s:", &key_hash) != 1)
>> +		return -EINVAL;
>> +
>> +	/* Pass in the secret without the 'DHHC-1:XX:' prefix */
>> +	key = nvme_auth_extract_key(secret + 10, key_hash);
>> +	if (IS_ERR(key)) {
>> +		return PTR_ERR(key);
>> +	}
>> +
>> +	*ret_key = key;
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(nvme_auth_generate_key);
>> +
>> +static void __nvme_auth_reset(struct nvme_dhchap_queue_context *chap)
>> +{
>> +	chap->status = 0;
>> +	chap->error = 0;
>> +	chap->s1 = 0;
>> +	chap->s2 = 0;
>> +	chap->transaction = 0;
>> +	memset(chap->c1, 0, sizeof(chap->c1));
>> +	memset(chap->c2, 0, sizeof(chap->c2));
>> +}
>> +
>> +static void __nvme_auth_free(struct nvme_dhchap_queue_context *chap)
>> +{
>> +	__nvme_auth_reset(chap);
>> +	if (chap->shash_tfm)
>> +		crypto_free_shash(chap->shash_tfm);
>> +	kfree_sensitive(chap->host_response);
>> +	kfree(chap->buf);
>> +	kfree(chap);
>> +}
>> +
>> +static void __nvme_auth_work(struct work_struct *work)
>> +{
>> +	struct nvme_dhchap_queue_context *chap =
>> +		container_of(work, struct nvme_dhchap_queue_context, auth_work);
>> +	struct nvme_ctrl *ctrl = chap->ctrl;
>> +	size_t tl;
>> +	int ret = 0;
>> +
>> +	chap->transaction = ctrl->transaction++;
>> +
>> +	/* DH-HMAC-CHAP Step 1: send negotiate */
>> +	dev_dbg(ctrl->device, "%s: qid %d send negotiate\n",
>> +		__func__, chap->qid);
>> +	ret = nvme_auth_set_dhchap_negotiate_data(ctrl, chap);
>> +	if (ret < 0) {
>> +		chap->error = ret;
>> +		return;
>> +	}
>> +	tl = ret;
>> +	ret = nvme_auth_submit(ctrl, chap->qid, chap->buf, tl, true);
>> +	if (ret) {
>> +		chap->error = ret;
>> +		return;
>> +	}
>> +
>> +	/* DH-HMAC-CHAP Step 2: receive challenge */
>> +	dev_dbg(ctrl->device, "%s: qid %d receive challenge\n",
>> +		__func__, chap->qid);
>> +
>> +	memset(chap->buf, 0, chap->buf_size);
>> +	ret = nvme_auth_submit(ctrl, chap->qid, chap->buf, chap->buf_size, false);
>> +	if (ret) {
>> +		dev_warn(ctrl->device,
>> +			 "qid %d failed to receive challenge, %s %d\n",
>> +			 chap->qid, ret < 0 ? "error" : "nvme status", ret);
>> +		chap->error = ret;
>> +		return;
>> +	}
>> +	ret = nvme_auth_receive_validate(ctrl, chap->qid, chap->buf, chap->transaction,
>> +					 NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE);
>> +	if (ret) {
>> +		chap->status = ret;
>> +		chap->error = NVME_SC_AUTH_REQUIRED;
>> +		return;
>> +	}
>> +
>> +	ret = nvme_auth_process_dhchap_challenge(ctrl, chap);
>> +	if (ret) {
>> +		/* Invalid challenge parameters */
>> +		goto fail2;
>> +	}
>> +
>> +	dev_dbg(ctrl->device, "%s: qid %d host response\n",
>> +		__func__, chap->qid);
>> +	ret = nvme_auth_dhchap_setup_host_response(ctrl, chap);
>> +	if (ret)
>> +		goto fail2;
>> +
>> +	/* DH-HMAC-CHAP Step 3: send reply */
>> +	dev_dbg(ctrl->device, "%s: qid %d send reply\n",
>> +		__func__, chap->qid);
>> +	ret = nvme_auth_set_dhchap_reply_data(ctrl, chap);
>> +	if (ret < 0)
>> +		goto fail2;
>> +
>> +	tl = ret;
>> +	ret = nvme_auth_submit(ctrl, chap->qid, chap->buf, tl, true);
>> +	if (ret)
>> +		goto fail2;
>> +
>> +	/* DH-HMAC-CHAP Step 4: receive success1 */
>> +	dev_dbg(ctrl->device, "%s: qid %d receive success1\n",
>> +		__func__, chap->qid);
>> +
>> +	memset(chap->buf, 0, chap->buf_size);
>> +	ret = nvme_auth_submit(ctrl, chap->qid, chap->buf, chap->buf_size, false);
>> +	if (ret) {
>> +		dev_warn(ctrl->device,
>> +			 "qid %d failed to receive success1, %s %d\n",
>> +			 chap->qid, ret < 0 ? "error" : "nvme status", ret);
>> +		chap->error = ret;
>> +		return;
>> +	}
>> +	ret = nvme_auth_receive_validate(ctrl, chap->qid,
>> +					 chap->buf, chap->transaction,
>> +					 NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1);
>> +	if (ret) {
>> +		chap->status = ret;
>> +		chap->error = NVME_SC_AUTH_REQUIRED;
>> +		return;
>> +	}
>> +
>> +	if (ctrl->opts->dhchap_ctrl_secret) {
>> +		dev_dbg(ctrl->device,
>> +			"%s: qid %d controller response\n",
>> +			__func__, chap->qid);
>> +		ret = nvme_auth_dhchap_setup_ctrl_response(ctrl, chap);
>> +		if (ret) {
>> +			chap->error = ret;
>> +			goto fail2;
>> +		}
>> +	}
>> +
>> +	ret = nvme_auth_process_dhchap_success1(ctrl, chap);
>> +	if (ret) {
>> +		/* Controller authentication failed */
>> +		chap->error = NVME_SC_AUTH_REQUIRED;
>> +		goto fail2;
>> +	}
>> +
>> +	/* DH-HMAC-CHAP Step 5: send success2 */
>> +	dev_dbg(ctrl->device, "%s: qid %d send success2\n",
>> +		__func__, chap->qid);
>> +	tl = nvme_auth_set_dhchap_success2_data(ctrl, chap);
> 
> Host should send DH-HMAC-CHAP_Success2 message only if it requested
> bidirectional authentication and that authentication succeeded.
> 
Indeed. Will be fixing it in the next submission.

Cheers,

Hannes
-- 
Dr. Hannes Reinecke                Kernel Storage Architect
hare at suse.de                              +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer



More information about the Linux-nvme mailing list