[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