| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (c) 2020 Hannes Reinecke, SUSE Linux
- */
- #include <linux/crc32.h>
- #include <linux/base64.h>
- #include <linux/prandom.h>
- #include <linux/unaligned.h>
- #include <crypto/hash.h>
- #include <crypto/dh.h>
- #include "nvme.h"
- #include "fabrics.h"
- #include <linux/nvme-auth.h>
- #define CHAP_BUF_SIZE 4096
- static struct kmem_cache *nvme_chap_buf_cache;
- static mempool_t *nvme_chap_buf_pool;
- struct nvme_dhchap_queue_context {
- struct list_head entry;
- struct work_struct auth_work;
- struct nvme_ctrl *ctrl;
- struct crypto_shash *shash_tfm;
- struct crypto_kpp *dh_tfm;
- struct nvme_dhchap_key *transformed_key;
- void *buf;
- int qid;
- int error;
- u32 s1;
- u32 s2;
- bool bi_directional;
- u16 transaction;
- u8 status;
- u8 dhgroup_id;
- u8 hash_id;
- size_t hash_len;
- u8 c1[64];
- u8 c2[64];
- u8 response[64];
- u8 *ctrl_key;
- u8 *host_key;
- u8 *sess_key;
- int ctrl_key_len;
- int host_key_len;
- int sess_key_len;
- };
- static struct workqueue_struct *nvme_auth_wq;
- static inline int ctrl_max_dhchaps(struct nvme_ctrl *ctrl)
- {
- return ctrl->opts->nr_io_queues + ctrl->opts->nr_write_queues +
- ctrl->opts->nr_poll_queues + 1;
- }
- static int nvme_auth_submit(struct nvme_ctrl *ctrl, int qid,
- void *data, size_t data_len, bool auth_send)
- {
- struct nvme_command cmd = {};
- nvme_submit_flags_t flags = NVME_SUBMIT_RETRY;
- struct request_queue *q = ctrl->fabrics_q;
- int ret;
- if (qid != 0) {
- flags |= NVME_SUBMIT_NOWAIT | NVME_SUBMIT_RESERVED;
- q = ctrl->connect_q;
- }
- cmd.auth_common.opcode = nvme_fabrics_command;
- cmd.auth_common.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER;
- cmd.auth_common.spsp0 = 0x01;
- cmd.auth_common.spsp1 = 0x01;
- if (auth_send) {
- cmd.auth_send.fctype = nvme_fabrics_type_auth_send;
- cmd.auth_send.tl = cpu_to_le32(data_len);
- } else {
- cmd.auth_receive.fctype = nvme_fabrics_type_auth_receive;
- cmd.auth_receive.al = cpu_to_le32(data_len);
- }
- ret = __nvme_submit_sync_cmd(q, &cmd, NULL, data, data_len,
- qid == 0 ? NVME_QID_ANY : qid, flags);
- if (ret > 0)
- dev_warn(ctrl->device,
- "qid %d auth_send failed with status %d\n", qid, ret);
- else if (ret < 0)
- dev_err(ctrl->device,
- "qid %d auth_send failed with error %d\n", qid, ret);
- return ret;
- }
- static int nvme_auth_receive_validate(struct nvme_ctrl *ctrl, int qid,
- struct nvmf_auth_dhchap_failure_data *data,
- u16 transaction, u8 expected_msg)
- {
- dev_dbg(ctrl->device, "%s: qid %d auth_type %d auth_id %x\n",
- __func__, qid, data->auth_type, data->auth_id);
- if (data->auth_type == NVME_AUTH_COMMON_MESSAGES &&
- data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) {
- return data->rescode_exp;
- }
- if (data->auth_type != NVME_AUTH_DHCHAP_MESSAGES ||
- data->auth_id != expected_msg) {
- dev_warn(ctrl->device,
- "qid %d invalid message %02x/%02x\n",
- qid, data->auth_type, data->auth_id);
- return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_MESSAGE;
- }
- if (le16_to_cpu(data->t_id) != transaction) {
- dev_warn(ctrl->device,
- "qid %d invalid transaction ID %d\n",
- qid, le16_to_cpu(data->t_id));
- return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_MESSAGE;
- }
- return 0;
- }
- static int nvme_auth_set_dhchap_negotiate_data(struct nvme_ctrl *ctrl,
- struct nvme_dhchap_queue_context *chap)
- {
- struct nvmf_auth_dhchap_negotiate_data *data = chap->buf;
- size_t size = sizeof(*data) + sizeof(union nvmf_auth_protocol);
- if (size > CHAP_BUF_SIZE) {
- chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
- return -EINVAL;
- }
- memset((u8 *)chap->buf, 0, size);
- 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 */
- data->napd = 1;
- data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID;
- data->auth_protocol[0].dhchap.halen = 3;
- data->auth_protocol[0].dhchap.dhlen = 6;
- data->auth_protocol[0].dhchap.idlist[0] = NVME_AUTH_HASH_SHA256;
- data->auth_protocol[0].dhchap.idlist[1] = NVME_AUTH_HASH_SHA384;
- data->auth_protocol[0].dhchap.idlist[2] = NVME_AUTH_HASH_SHA512;
- data->auth_protocol[0].dhchap.idlist[30] = NVME_AUTH_DHGROUP_NULL;
- data->auth_protocol[0].dhchap.idlist[31] = NVME_AUTH_DHGROUP_2048;
- data->auth_protocol[0].dhchap.idlist[32] = NVME_AUTH_DHGROUP_3072;
- data->auth_protocol[0].dhchap.idlist[33] = NVME_AUTH_DHGROUP_4096;
- data->auth_protocol[0].dhchap.idlist[34] = NVME_AUTH_DHGROUP_6144;
- data->auth_protocol[0].dhchap.idlist[35] = NVME_AUTH_DHGROUP_8192;
- return size;
- }
- static int nvme_auth_process_dhchap_challenge(struct nvme_ctrl *ctrl,
- struct nvme_dhchap_queue_context *chap)
- {
- struct nvmf_auth_dhchap_challenge_data *data = chap->buf;
- u16 dhvlen = le16_to_cpu(data->dhvlen);
- size_t size = sizeof(*data) + data->hl + dhvlen;
- const char *gid_name = nvme_auth_dhgroup_name(data->dhgid);
- const char *hmac_name, *kpp_name;
- if (size > CHAP_BUF_SIZE) {
- chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
- return -EINVAL;
- }
- hmac_name = nvme_auth_hmac_name(data->hashid);
- if (!hmac_name) {
- dev_warn(ctrl->device,
- "qid %d: invalid HASH ID %d\n",
- chap->qid, data->hashid);
- chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
- return -EPROTO;
- }
- if (chap->hash_id == data->hashid && chap->shash_tfm &&
- !strcmp(crypto_shash_alg_name(chap->shash_tfm), hmac_name) &&
- crypto_shash_digestsize(chap->shash_tfm) == data->hl) {
- dev_dbg(ctrl->device,
- "qid %d: reuse existing hash %s\n",
- chap->qid, hmac_name);
- goto select_kpp;
- }
- /* Reset if hash cannot be reused */
- if (chap->shash_tfm) {
- crypto_free_shash(chap->shash_tfm);
- chap->hash_id = 0;
- chap->hash_len = 0;
- }
- chap->shash_tfm = crypto_alloc_shash(hmac_name, 0,
- CRYPTO_ALG_ALLOCATES_MEMORY);
- if (IS_ERR(chap->shash_tfm)) {
- dev_warn(ctrl->device,
- "qid %d: failed to allocate hash %s, error %ld\n",
- chap->qid, hmac_name, PTR_ERR(chap->shash_tfm));
- chap->shash_tfm = NULL;
- chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
- return -ENOMEM;
- }
- if (crypto_shash_digestsize(chap->shash_tfm) != data->hl) {
- dev_warn(ctrl->device,
- "qid %d: invalid hash length %d\n",
- chap->qid, data->hl);
- crypto_free_shash(chap->shash_tfm);
- chap->shash_tfm = NULL;
- chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
- return -EPROTO;
- }
- chap->hash_id = data->hashid;
- chap->hash_len = data->hl;
- dev_dbg(ctrl->device, "qid %d: selected hash %s\n",
- chap->qid, hmac_name);
- select_kpp:
- kpp_name = nvme_auth_dhgroup_kpp(data->dhgid);
- if (!kpp_name) {
- dev_warn(ctrl->device,
- "qid %d: invalid DH group id %d\n",
- chap->qid, data->dhgid);
- chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
- /* Leave previous dh_tfm intact */
- return -EPROTO;
- }
- if (chap->dhgroup_id == data->dhgid &&
- (data->dhgid == NVME_AUTH_DHGROUP_NULL || chap->dh_tfm)) {
- dev_dbg(ctrl->device,
- "qid %d: reuse existing DH group %s\n",
- chap->qid, gid_name);
- goto skip_kpp;
- }
- /* Reset dh_tfm if it can't be reused */
- if (chap->dh_tfm) {
- crypto_free_kpp(chap->dh_tfm);
- chap->dh_tfm = NULL;
- }
- if (data->dhgid != NVME_AUTH_DHGROUP_NULL) {
- if (dhvlen == 0) {
- dev_warn(ctrl->device,
- "qid %d: empty DH value\n",
- chap->qid);
- chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
- return -EPROTO;
- }
- chap->dh_tfm = crypto_alloc_kpp(kpp_name, 0, 0);
- if (IS_ERR(chap->dh_tfm)) {
- int ret = PTR_ERR(chap->dh_tfm);
- dev_warn(ctrl->device,
- "qid %d: error %d initializing DH group %s\n",
- chap->qid, ret, gid_name);
- chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
- chap->dh_tfm = NULL;
- return ret;
- }
- dev_dbg(ctrl->device, "qid %d: selected DH group %s\n",
- chap->qid, gid_name);
- } else if (dhvlen != 0) {
- dev_warn(ctrl->device,
- "qid %d: invalid DH value for NULL DH\n",
- chap->qid);
- chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
- return -EPROTO;
- }
- chap->dhgroup_id = data->dhgid;
- skip_kpp:
- chap->s1 = le32_to_cpu(data->seqnum);
- memcpy(chap->c1, data->cval, chap->hash_len);
- if (dhvlen) {
- chap->ctrl_key = kmalloc(dhvlen, GFP_KERNEL);
- if (!chap->ctrl_key) {
- chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
- return -ENOMEM;
- }
- chap->ctrl_key_len = dhvlen;
- memcpy(chap->ctrl_key, data->cval + chap->hash_len,
- dhvlen);
- dev_dbg(ctrl->device, "ctrl public key %*ph\n",
- (int)chap->ctrl_key_len, chap->ctrl_key);
- }
- return 0;
- }
- static int nvme_auth_set_dhchap_reply_data(struct nvme_ctrl *ctrl,
- struct nvme_dhchap_queue_context *chap)
- {
- struct nvmf_auth_dhchap_reply_data *data = chap->buf;
- size_t size = sizeof(*data);
- size += 2 * chap->hash_len;
- if (chap->host_key_len)
- size += chap->host_key_len;
- if (size > CHAP_BUF_SIZE) {
- chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
- return -EINVAL;
- }
- memset(chap->buf, 0, size);
- data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
- data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_REPLY;
- data->t_id = cpu_to_le16(chap->transaction);
- 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) {
- chap->bi_directional = true;
- get_random_bytes(chap->c2, chap->hash_len);
- data->cvalid = 1;
- memcpy(data->rval + chap->hash_len, chap->c2,
- chap->hash_len);
- dev_dbg(ctrl->device, "%s: qid %d ctrl challenge %*ph\n",
- __func__, chap->qid, (int)chap->hash_len, chap->c2);
- } else {
- memset(chap->c2, 0, chap->hash_len);
- }
- chap->s2 = nvme_auth_get_seqnum();
- data->seqnum = cpu_to_le32(chap->s2);
- if (chap->host_key_len) {
- dev_dbg(ctrl->device, "%s: qid %d host public key %*ph\n",
- __func__, chap->qid,
- chap->host_key_len, chap->host_key);
- memcpy(data->rval + 2 * chap->hash_len, chap->host_key,
- chap->host_key_len);
- }
- return size;
- }
- static int nvme_auth_process_dhchap_success1(struct nvme_ctrl *ctrl,
- struct nvme_dhchap_queue_context *chap)
- {
- struct nvmf_auth_dhchap_success1_data *data = chap->buf;
- size_t size = sizeof(*data) + chap->hash_len;
- if (size > CHAP_BUF_SIZE) {
- chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
- return -EINVAL;
- }
- if (data->hl != chap->hash_len) {
- dev_warn(ctrl->device,
- "qid %d: invalid hash length %u\n",
- chap->qid, data->hl);
- chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
- return -EPROTO;
- }
- /* Just print out information for the admin queue */
- if (chap->qid == 0)
- dev_info(ctrl->device,
- "qid 0: authenticated with hash %s dhgroup %s\n",
- nvme_auth_hmac_name(chap->hash_id),
- nvme_auth_dhgroup_name(chap->dhgroup_id));
- if (!data->rvalid)
- return 0;
- /* Validate controller response */
- if (memcmp(chap->response, data->rval, data->hl)) {
- dev_dbg(ctrl->device, "%s: qid %d ctrl response %*ph\n",
- __func__, chap->qid, (int)chap->hash_len, data->rval);
- dev_dbg(ctrl->device, "%s: qid %d host response %*ph\n",
- __func__, chap->qid, (int)chap->hash_len,
- chap->response);
- dev_warn(ctrl->device,
- "qid %d: controller authentication failed\n",
- chap->qid);
- chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
- return -ECONNREFUSED;
- }
- /* Just print out information for the admin queue */
- if (chap->qid == 0)
- dev_info(ctrl->device,
- "qid 0: controller authenticated\n");
- return 0;
- }
- static int nvme_auth_set_dhchap_success2_data(struct nvme_ctrl *ctrl,
- struct nvme_dhchap_queue_context *chap)
- {
- struct nvmf_auth_dhchap_success2_data *data = chap->buf;
- size_t size = sizeof(*data);
- memset(chap->buf, 0, size);
- data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
- data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2;
- data->t_id = cpu_to_le16(chap->transaction);
- return size;
- }
- 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_COMMON_MESSAGES;
- 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->transformed_key) {
- chap->transformed_key = nvme_auth_transform_key(ctrl->host_key,
- ctrl->opts->host->nqn);
- if (IS_ERR(chap->transformed_key)) {
- ret = PTR_ERR(chap->transformed_key);
- chap->transformed_key = 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->transformed_key->key, chap->transformed_key->len);
- if (ret) {
- dev_warn(ctrl->device, "qid %d: failed to set key, error %d\n",
- chap->qid, ret);
- goto out;
- }
- if (chap->dh_tfm) {
- challenge = kmalloc(chap->hash_len, GFP_KERNEL);
- if (!challenge) {
- ret = -ENOMEM;
- goto out;
- }
- ret = nvme_auth_augmented_challenge(chap->hash_id,
- chap->sess_key,
- chap->sess_key_len,
- chap->c1, challenge,
- chap->hash_len);
- if (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);
- struct nvme_dhchap_key *transformed_key;
- u8 buf[4], *challenge = chap->c2;
- int ret;
- transformed_key = nvme_auth_transform_key(ctrl->ctrl_key,
- ctrl->opts->subsysnqn);
- if (IS_ERR(transformed_key)) {
- ret = PTR_ERR(transformed_key);
- return ret;
- }
- ret = crypto_shash_setkey(chap->shash_tfm,
- transformed_key->key, transformed_key->len);
- if (ret) {
- dev_warn(ctrl->device, "qid %d: failed to set key, error %d\n",
- chap->qid, ret);
- goto out;
- }
- if (chap->dh_tfm) {
- challenge = kmalloc(chap->hash_len, GFP_KERNEL);
- if (!challenge) {
- ret = -ENOMEM;
- goto out;
- }
- ret = nvme_auth_augmented_challenge(chap->hash_id,
- chap->sess_key,
- chap->sess_key_len,
- chap->c2, challenge,
- chap->hash_len);
- if (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);
- nvme_auth_free_key(transformed_key);
- return ret;
- }
- static int nvme_auth_dhchap_exponential(struct nvme_ctrl *ctrl,
- struct nvme_dhchap_queue_context *chap)
- {
- int ret;
- if (chap->host_key && chap->host_key_len) {
- dev_dbg(ctrl->device,
- "qid %d: reusing host key\n", chap->qid);
- goto gen_sesskey;
- }
- ret = nvme_auth_gen_privkey(chap->dh_tfm, chap->dhgroup_id);
- if (ret < 0) {
- chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
- return ret;
- }
- chap->host_key_len = crypto_kpp_maxsize(chap->dh_tfm);
- chap->host_key = kzalloc(chap->host_key_len, GFP_KERNEL);
- if (!chap->host_key) {
- chap->host_key_len = 0;
- chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
- return -ENOMEM;
- }
- ret = nvme_auth_gen_pubkey(chap->dh_tfm,
- chap->host_key, chap->host_key_len);
- if (ret) {
- dev_dbg(ctrl->device,
- "failed to generate public key, error %d\n", ret);
- chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
- return ret;
- }
- gen_sesskey:
- chap->sess_key_len = chap->host_key_len;
- chap->sess_key = kmalloc(chap->sess_key_len, GFP_KERNEL);
- if (!chap->sess_key) {
- chap->sess_key_len = 0;
- chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
- return -ENOMEM;
- }
- ret = nvme_auth_gen_shared_secret(chap->dh_tfm,
- chap->ctrl_key, chap->ctrl_key_len,
- chap->sess_key, chap->sess_key_len);
- if (ret) {
- dev_dbg(ctrl->device,
- "failed to generate shared secret, error %d\n", ret);
- chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
- return ret;
- }
- dev_dbg(ctrl->device, "shared secret %*ph\n",
- (int)chap->sess_key_len, chap->sess_key);
- return 0;
- }
- static void nvme_auth_reset_dhchap(struct nvme_dhchap_queue_context *chap)
- {
- nvme_auth_free_key(chap->transformed_key);
- chap->transformed_key = NULL;
- kfree_sensitive(chap->host_key);
- chap->host_key = NULL;
- chap->host_key_len = 0;
- kfree_sensitive(chap->ctrl_key);
- chap->ctrl_key = NULL;
- chap->ctrl_key_len = 0;
- kfree_sensitive(chap->sess_key);
- chap->sess_key = NULL;
- chap->sess_key_len = 0;
- chap->status = 0;
- chap->error = 0;
- chap->s1 = 0;
- chap->s2 = 0;
- chap->bi_directional = false;
- chap->transaction = 0;
- memset(chap->c1, 0, sizeof(chap->c1));
- memset(chap->c2, 0, sizeof(chap->c2));
- mempool_free(chap->buf, nvme_chap_buf_pool);
- chap->buf = NULL;
- }
- static void nvme_auth_free_dhchap(struct nvme_dhchap_queue_context *chap)
- {
- nvme_auth_reset_dhchap(chap);
- if (chap->shash_tfm)
- crypto_free_shash(chap->shash_tfm);
- if (chap->dh_tfm)
- crypto_free_kpp(chap->dh_tfm);
- }
- static void nvme_queue_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;
- /*
- * Allocate a large enough buffer for the entire negotiation:
- * 4k is enough to ffdhe8192.
- */
- chap->buf = mempool_alloc(nvme_chap_buf_pool, GFP_KERNEL);
- if (!chap->buf) {
- chap->error = -ENOMEM;
- return;
- }
- 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 = -EKEYREJECTED;
- return;
- }
- ret = nvme_auth_process_dhchap_challenge(ctrl, chap);
- if (ret) {
- /* Invalid challenge parameters */
- chap->error = ret;
- goto fail2;
- }
- if (chap->ctrl_key_len) {
- dev_dbg(ctrl->device,
- "%s: qid %d DH exponential\n",
- __func__, chap->qid);
- ret = nvme_auth_dhchap_exponential(ctrl, chap);
- if (ret) {
- chap->error = ret;
- goto fail2;
- }
- }
- dev_dbg(ctrl->device, "%s: qid %d host response\n",
- __func__, chap->qid);
- mutex_lock(&ctrl->dhchap_auth_mutex);
- ret = nvme_auth_dhchap_setup_host_response(ctrl, chap);
- mutex_unlock(&ctrl->dhchap_auth_mutex);
- if (ret) {
- chap->error = 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) {
- chap->error = ret;
- goto fail2;
- }
- tl = ret;
- ret = nvme_auth_submit(ctrl, chap->qid, chap->buf, tl, true);
- if (ret) {
- chap->error = 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 = -EKEYREJECTED;
- return;
- }
- mutex_lock(&ctrl->dhchap_auth_mutex);
- if (ctrl->ctrl_key) {
- dev_dbg(ctrl->device,
- "%s: qid %d controller response\n",
- __func__, chap->qid);
- ret = nvme_auth_dhchap_setup_ctrl_response(ctrl, chap);
- if (ret) {
- mutex_unlock(&ctrl->dhchap_auth_mutex);
- chap->error = ret;
- goto fail2;
- }
- }
- mutex_unlock(&ctrl->dhchap_auth_mutex);
- ret = nvme_auth_process_dhchap_success1(ctrl, chap);
- if (ret) {
- /* Controller authentication failed */
- chap->error = -EKEYREJECTED;
- goto fail2;
- }
- if (chap->bi_directional) {
- /* 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);
- ret = nvme_auth_submit(ctrl, chap->qid, chap->buf, tl, true);
- if (ret)
- chap->error = ret;
- }
- if (!ret) {
- chap->error = 0;
- return;
- }
- fail2:
- if (chap->status == 0)
- chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
- dev_dbg(ctrl->device, "%s: qid %d send failure2, status %x\n",
- __func__, chap->qid, chap->status);
- tl = nvme_auth_set_dhchap_failure2_data(ctrl, chap);
- ret = nvme_auth_submit(ctrl, chap->qid, chap->buf, tl, true);
- /*
- * only update error if send failure2 failed and no other
- * error had been set during authentication.
- */
- if (ret && !chap->error)
- chap->error = ret;
- }
- int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
- {
- struct nvme_dhchap_queue_context *chap;
- if (!ctrl->host_key) {
- dev_warn(ctrl->device, "qid %d: no key\n", qid);
- return -ENOKEY;
- }
- if (ctrl->opts->dhchap_ctrl_secret && !ctrl->ctrl_key) {
- dev_warn(ctrl->device, "qid %d: invalid ctrl key\n", qid);
- return -ENOKEY;
- }
- chap = &ctrl->dhchap_ctxs[qid];
- cancel_work_sync(&chap->auth_work);
- queue_work(nvme_auth_wq, &chap->auth_work);
- return 0;
- }
- EXPORT_SYMBOL_GPL(nvme_auth_negotiate);
- int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid)
- {
- struct nvme_dhchap_queue_context *chap;
- int ret;
- chap = &ctrl->dhchap_ctxs[qid];
- flush_work(&chap->auth_work);
- ret = chap->error;
- /* clear sensitive info */
- nvme_auth_reset_dhchap(chap);
- return ret;
- }
- EXPORT_SYMBOL_GPL(nvme_auth_wait);
- static void nvme_ctrl_auth_work(struct work_struct *work)
- {
- struct nvme_ctrl *ctrl =
- container_of(work, struct nvme_ctrl, dhchap_auth_work);
- int ret, q;
- /*
- * If the ctrl is no connected, bail as reconnect will handle
- * authentication.
- */
- if (nvme_ctrl_state(ctrl) != NVME_CTRL_LIVE)
- return;
- /* Authenticate admin queue first */
- ret = nvme_auth_negotiate(ctrl, 0);
- if (ret) {
- dev_warn(ctrl->device,
- "qid 0: error %d setting up authentication\n", ret);
- return;
- }
- ret = nvme_auth_wait(ctrl, 0);
- if (ret) {
- dev_warn(ctrl->device,
- "qid 0: authentication failed\n");
- return;
- }
- for (q = 1; q < ctrl->queue_count; q++) {
- ret = nvme_auth_negotiate(ctrl, q);
- if (ret) {
- dev_warn(ctrl->device,
- "qid %d: error %d setting up authentication\n",
- q, ret);
- break;
- }
- }
- /*
- * Failure is a soft-state; credentials remain valid until
- * the controller terminates the connection.
- */
- for (q = 1; q < ctrl->queue_count; q++) {
- ret = nvme_auth_wait(ctrl, q);
- if (ret)
- dev_warn(ctrl->device,
- "qid %d: authentication failed\n", q);
- }
- }
- int nvme_auth_init_ctrl(struct nvme_ctrl *ctrl)
- {
- struct nvme_dhchap_queue_context *chap;
- int i, ret;
- mutex_init(&ctrl->dhchap_auth_mutex);
- INIT_WORK(&ctrl->dhchap_auth_work, nvme_ctrl_auth_work);
- if (!ctrl->opts)
- return 0;
- ret = nvme_auth_generate_key(ctrl->opts->dhchap_secret,
- &ctrl->host_key);
- if (ret)
- return ret;
- ret = nvme_auth_generate_key(ctrl->opts->dhchap_ctrl_secret,
- &ctrl->ctrl_key);
- if (ret)
- goto err_free_dhchap_secret;
- if (!ctrl->opts->dhchap_secret && !ctrl->opts->dhchap_ctrl_secret)
- return 0;
- ctrl->dhchap_ctxs = kvcalloc(ctrl_max_dhchaps(ctrl),
- sizeof(*chap), GFP_KERNEL);
- if (!ctrl->dhchap_ctxs) {
- ret = -ENOMEM;
- goto err_free_dhchap_ctrl_secret;
- }
- for (i = 0; i < ctrl_max_dhchaps(ctrl); i++) {
- chap = &ctrl->dhchap_ctxs[i];
- chap->qid = i;
- chap->ctrl = ctrl;
- INIT_WORK(&chap->auth_work, nvme_queue_auth_work);
- }
- return 0;
- err_free_dhchap_ctrl_secret:
- nvme_auth_free_key(ctrl->ctrl_key);
- ctrl->ctrl_key = NULL;
- err_free_dhchap_secret:
- nvme_auth_free_key(ctrl->host_key);
- ctrl->host_key = NULL;
- return ret;
- }
- EXPORT_SYMBOL_GPL(nvme_auth_init_ctrl);
- void nvme_auth_stop(struct nvme_ctrl *ctrl)
- {
- cancel_work_sync(&ctrl->dhchap_auth_work);
- }
- EXPORT_SYMBOL_GPL(nvme_auth_stop);
- void nvme_auth_free(struct nvme_ctrl *ctrl)
- {
- int i;
- if (ctrl->dhchap_ctxs) {
- for (i = 0; i < ctrl_max_dhchaps(ctrl); i++)
- nvme_auth_free_dhchap(&ctrl->dhchap_ctxs[i]);
- kfree(ctrl->dhchap_ctxs);
- }
- if (ctrl->host_key) {
- nvme_auth_free_key(ctrl->host_key);
- ctrl->host_key = NULL;
- }
- if (ctrl->ctrl_key) {
- nvme_auth_free_key(ctrl->ctrl_key);
- ctrl->ctrl_key = NULL;
- }
- }
- EXPORT_SYMBOL_GPL(nvme_auth_free);
- int __init nvme_init_auth(void)
- {
- nvme_auth_wq = alloc_workqueue("nvme-auth-wq",
- WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS, 0);
- if (!nvme_auth_wq)
- return -ENOMEM;
- nvme_chap_buf_cache = kmem_cache_create("nvme-chap-buf-cache",
- CHAP_BUF_SIZE, 0, SLAB_HWCACHE_ALIGN, NULL);
- if (!nvme_chap_buf_cache)
- goto err_destroy_workqueue;
- nvme_chap_buf_pool = mempool_create(16, mempool_alloc_slab,
- mempool_free_slab, nvme_chap_buf_cache);
- if (!nvme_chap_buf_pool)
- goto err_destroy_chap_buf_cache;
- return 0;
- err_destroy_chap_buf_cache:
- kmem_cache_destroy(nvme_chap_buf_cache);
- err_destroy_workqueue:
- destroy_workqueue(nvme_auth_wq);
- return -ENOMEM;
- }
- void __exit nvme_exit_auth(void)
- {
- mempool_destroy(nvme_chap_buf_pool);
- kmem_cache_destroy(nvme_chap_buf_cache);
- destroy_workqueue(nvme_auth_wq);
- }
|