1db1312ddSHannes Reinecke // SPDX-License-Identifier: GPL-2.0
2db1312ddSHannes Reinecke /*
3db1312ddSHannes Reinecke * NVMe over Fabrics DH-HMAC-CHAP authentication command handling.
4db1312ddSHannes Reinecke * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions.
5db1312ddSHannes Reinecke * All rights reserved.
6db1312ddSHannes Reinecke */
7db1312ddSHannes Reinecke #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
8db1312ddSHannes Reinecke #include <linux/blkdev.h>
9db1312ddSHannes Reinecke #include <linux/random.h>
10db1312ddSHannes Reinecke #include <linux/nvme-auth.h>
11db1312ddSHannes Reinecke #include <crypto/hash.h>
12db1312ddSHannes Reinecke #include <crypto/kpp.h>
13db1312ddSHannes Reinecke #include "nvmet.h"
14db1312ddSHannes Reinecke
nvmet_auth_expired_work(struct work_struct * work)151a70200fSHannes Reinecke static void nvmet_auth_expired_work(struct work_struct *work)
161a70200fSHannes Reinecke {
171a70200fSHannes Reinecke struct nvmet_sq *sq = container_of(to_delayed_work(work),
181a70200fSHannes Reinecke struct nvmet_sq, auth_expired_work);
191a70200fSHannes Reinecke
201a70200fSHannes Reinecke pr_debug("%s: ctrl %d qid %d transaction %u expired, resetting\n",
211a70200fSHannes Reinecke __func__, sq->ctrl->cntlid, sq->qid, sq->dhchap_tid);
221a70200fSHannes Reinecke sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
231a70200fSHannes Reinecke sq->dhchap_tid = -1;
241a70200fSHannes Reinecke }
251a70200fSHannes Reinecke
nvmet_auth_sq_init(struct nvmet_sq * sq)261befd944SChristoph Hellwig void nvmet_auth_sq_init(struct nvmet_sq *sq)
27db1312ddSHannes Reinecke {
28db1312ddSHannes Reinecke /* Initialize in-band authentication */
291befd944SChristoph Hellwig INIT_DELAYED_WORK(&sq->auth_expired_work, nvmet_auth_expired_work);
301befd944SChristoph Hellwig sq->authenticated = false;
311befd944SChristoph Hellwig sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
32db1312ddSHannes Reinecke }
33db1312ddSHannes Reinecke
nvmet_auth_negotiate(struct nvmet_req * req,void * d)3444e3c25eSHannes Reinecke static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
35db1312ddSHannes Reinecke {
36db1312ddSHannes Reinecke struct nvmet_ctrl *ctrl = req->sq->ctrl;
37db1312ddSHannes Reinecke struct nvmf_auth_dhchap_negotiate_data *data = d;
387a277c37SHannes Reinecke int i, hash_id = 0, fallback_hash_id = 0, dhgid, fallback_dhgid;
39db1312ddSHannes Reinecke
40db1312ddSHannes Reinecke pr_debug("%s: ctrl %d qid %d: data sc_d %d napd %d authid %d halen %d dhlen %d\n",
41db1312ddSHannes Reinecke __func__, ctrl->cntlid, req->sq->qid,
42db1312ddSHannes Reinecke data->sc_c, data->napd, data->auth_protocol[0].dhchap.authid,
43db1312ddSHannes Reinecke data->auth_protocol[0].dhchap.halen,
44db1312ddSHannes Reinecke data->auth_protocol[0].dhchap.dhlen);
45db1312ddSHannes Reinecke req->sq->dhchap_tid = le16_to_cpu(data->t_id);
46db1312ddSHannes Reinecke if (data->sc_c)
47db1312ddSHannes Reinecke return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH;
48db1312ddSHannes Reinecke
49db1312ddSHannes Reinecke if (data->napd != 1)
50db1312ddSHannes Reinecke return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
51db1312ddSHannes Reinecke
52db1312ddSHannes Reinecke if (data->auth_protocol[0].dhchap.authid !=
53db1312ddSHannes Reinecke NVME_AUTH_DHCHAP_AUTH_ID)
54db1312ddSHannes Reinecke return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
55db1312ddSHannes Reinecke
56db1312ddSHannes Reinecke for (i = 0; i < data->auth_protocol[0].dhchap.halen; i++) {
57db1312ddSHannes Reinecke u8 host_hmac_id = data->auth_protocol[0].dhchap.idlist[i];
58db1312ddSHannes Reinecke
59db1312ddSHannes Reinecke if (!fallback_hash_id &&
60db1312ddSHannes Reinecke crypto_has_shash(nvme_auth_hmac_name(host_hmac_id), 0, 0))
61db1312ddSHannes Reinecke fallback_hash_id = host_hmac_id;
62db1312ddSHannes Reinecke if (ctrl->shash_id != host_hmac_id)
63db1312ddSHannes Reinecke continue;
64db1312ddSHannes Reinecke hash_id = ctrl->shash_id;
65db1312ddSHannes Reinecke break;
66db1312ddSHannes Reinecke }
67db1312ddSHannes Reinecke if (hash_id == 0) {
68db1312ddSHannes Reinecke if (fallback_hash_id == 0) {
69db1312ddSHannes Reinecke pr_debug("%s: ctrl %d qid %d: no usable hash found\n",
70db1312ddSHannes Reinecke __func__, ctrl->cntlid, req->sq->qid);
71db1312ddSHannes Reinecke return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
72db1312ddSHannes Reinecke }
73db1312ddSHannes Reinecke pr_debug("%s: ctrl %d qid %d: no usable hash found, falling back to %s\n",
74db1312ddSHannes Reinecke __func__, ctrl->cntlid, req->sq->qid,
75db1312ddSHannes Reinecke nvme_auth_hmac_name(fallback_hash_id));
76db1312ddSHannes Reinecke ctrl->shash_id = fallback_hash_id;
77db1312ddSHannes Reinecke }
78db1312ddSHannes Reinecke
79db1312ddSHannes Reinecke dhgid = -1;
807a277c37SHannes Reinecke fallback_dhgid = -1;
81db1312ddSHannes Reinecke for (i = 0; i < data->auth_protocol[0].dhchap.dhlen; i++) {
82db1312ddSHannes Reinecke int tmp_dhgid = data->auth_protocol[0].dhchap.idlist[i + 30];
83db1312ddSHannes Reinecke
847a277c37SHannes Reinecke if (tmp_dhgid != ctrl->dh_gid) {
85db1312ddSHannes Reinecke dhgid = tmp_dhgid;
86db1312ddSHannes Reinecke break;
87db1312ddSHannes Reinecke }
887a277c37SHannes Reinecke if (fallback_dhgid < 0) {
897a277c37SHannes Reinecke const char *kpp = nvme_auth_dhgroup_kpp(tmp_dhgid);
907a277c37SHannes Reinecke
917a277c37SHannes Reinecke if (crypto_has_kpp(kpp, 0, 0))
927a277c37SHannes Reinecke fallback_dhgid = tmp_dhgid;
937a277c37SHannes Reinecke }
94db1312ddSHannes Reinecke }
95db1312ddSHannes Reinecke if (dhgid < 0) {
967a277c37SHannes Reinecke if (fallback_dhgid < 0) {
97db1312ddSHannes Reinecke pr_debug("%s: ctrl %d qid %d: no usable DH group found\n",
98db1312ddSHannes Reinecke __func__, ctrl->cntlid, req->sq->qid);
99db1312ddSHannes Reinecke return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
100db1312ddSHannes Reinecke }
1017a277c37SHannes Reinecke pr_debug("%s: ctrl %d qid %d: configured DH group %s not found\n",
1027a277c37SHannes Reinecke __func__, ctrl->cntlid, req->sq->qid,
1037a277c37SHannes Reinecke nvme_auth_dhgroup_name(fallback_dhgid));
1047a277c37SHannes Reinecke ctrl->dh_gid = fallback_dhgid;
1057a277c37SHannes Reinecke }
106db1312ddSHannes Reinecke pr_debug("%s: ctrl %d qid %d: selected DH group %s (%d)\n",
107db1312ddSHannes Reinecke __func__, ctrl->cntlid, req->sq->qid,
1087a277c37SHannes Reinecke nvme_auth_dhgroup_name(ctrl->dh_gid), ctrl->dh_gid);
109db1312ddSHannes Reinecke return 0;
110db1312ddSHannes Reinecke }
111db1312ddSHannes Reinecke
nvmet_auth_reply(struct nvmet_req * req,void * d)11244e3c25eSHannes Reinecke static u8 nvmet_auth_reply(struct nvmet_req *req, void *d)
113db1312ddSHannes Reinecke {
114db1312ddSHannes Reinecke struct nvmet_ctrl *ctrl = req->sq->ctrl;
115db1312ddSHannes Reinecke struct nvmf_auth_dhchap_reply_data *data = d;
116db1312ddSHannes Reinecke u16 dhvlen = le16_to_cpu(data->dhvlen);
117db1312ddSHannes Reinecke u8 *response;
118db1312ddSHannes Reinecke
119db1312ddSHannes Reinecke pr_debug("%s: ctrl %d qid %d: data hl %d cvalid %d dhvlen %u\n",
120db1312ddSHannes Reinecke __func__, ctrl->cntlid, req->sq->qid,
121db1312ddSHannes Reinecke data->hl, data->cvalid, dhvlen);
122db1312ddSHannes Reinecke
123db1312ddSHannes Reinecke if (dhvlen) {
1247a277c37SHannes Reinecke if (!ctrl->dh_tfm)
125db1312ddSHannes Reinecke return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
1267a277c37SHannes Reinecke if (nvmet_auth_ctrl_sesskey(req, data->rval + 2 * data->hl,
1277a277c37SHannes Reinecke dhvlen) < 0)
1287a277c37SHannes Reinecke return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
129db1312ddSHannes Reinecke }
130db1312ddSHannes Reinecke
131db1312ddSHannes Reinecke response = kmalloc(data->hl, GFP_KERNEL);
132db1312ddSHannes Reinecke if (!response)
133db1312ddSHannes Reinecke return NVME_AUTH_DHCHAP_FAILURE_FAILED;
134db1312ddSHannes Reinecke
135db1312ddSHannes Reinecke if (!ctrl->host_key) {
136db1312ddSHannes Reinecke pr_warn("ctrl %d qid %d no host key\n",
137db1312ddSHannes Reinecke ctrl->cntlid, req->sq->qid);
138db1312ddSHannes Reinecke kfree(response);
139db1312ddSHannes Reinecke return NVME_AUTH_DHCHAP_FAILURE_FAILED;
140db1312ddSHannes Reinecke }
141db1312ddSHannes Reinecke if (nvmet_auth_host_hash(req, response, data->hl) < 0) {
142db1312ddSHannes Reinecke pr_debug("ctrl %d qid %d host hash failed\n",
143db1312ddSHannes Reinecke ctrl->cntlid, req->sq->qid);
144db1312ddSHannes Reinecke kfree(response);
145db1312ddSHannes Reinecke return NVME_AUTH_DHCHAP_FAILURE_FAILED;
146db1312ddSHannes Reinecke }
147db1312ddSHannes Reinecke
148db1312ddSHannes Reinecke if (memcmp(data->rval, response, data->hl)) {
149db1312ddSHannes Reinecke pr_info("ctrl %d qid %d host response mismatch\n",
150db1312ddSHannes Reinecke ctrl->cntlid, req->sq->qid);
151db1312ddSHannes Reinecke kfree(response);
152db1312ddSHannes Reinecke return NVME_AUTH_DHCHAP_FAILURE_FAILED;
153db1312ddSHannes Reinecke }
154db1312ddSHannes Reinecke kfree(response);
155db1312ddSHannes Reinecke pr_debug("%s: ctrl %d qid %d host authenticated\n",
156db1312ddSHannes Reinecke __func__, ctrl->cntlid, req->sq->qid);
157db1312ddSHannes Reinecke if (data->cvalid) {
15814446f9aSZhang Xiaoxu req->sq->dhchap_c2 = kmemdup(data->rval + data->hl, data->hl,
15914446f9aSZhang Xiaoxu GFP_KERNEL);
160db1312ddSHannes Reinecke if (!req->sq->dhchap_c2)
161db1312ddSHannes Reinecke return NVME_AUTH_DHCHAP_FAILURE_FAILED;
162db1312ddSHannes Reinecke
163db1312ddSHannes Reinecke pr_debug("%s: ctrl %d qid %d challenge %*ph\n",
164db1312ddSHannes Reinecke __func__, ctrl->cntlid, req->sq->qid, data->hl,
165db1312ddSHannes Reinecke req->sq->dhchap_c2);
166db1312ddSHannes Reinecke } else {
167db1312ddSHannes Reinecke req->sq->authenticated = true;
168db1312ddSHannes Reinecke req->sq->dhchap_c2 = NULL;
169db1312ddSHannes Reinecke }
1706f66d046SMark O'Donovan req->sq->dhchap_s2 = le32_to_cpu(data->seqnum);
171db1312ddSHannes Reinecke
172db1312ddSHannes Reinecke return 0;
173db1312ddSHannes Reinecke }
174db1312ddSHannes Reinecke
nvmet_auth_failure2(void * d)17544e3c25eSHannes Reinecke static u8 nvmet_auth_failure2(void *d)
176db1312ddSHannes Reinecke {
177db1312ddSHannes Reinecke struct nvmf_auth_dhchap_failure_data *data = d;
178db1312ddSHannes Reinecke
179db1312ddSHannes Reinecke return data->rescode_exp;
180db1312ddSHannes Reinecke }
181db1312ddSHannes Reinecke
nvmet_execute_auth_send(struct nvmet_req * req)182db1312ddSHannes Reinecke void nvmet_execute_auth_send(struct nvmet_req *req)
183db1312ddSHannes Reinecke {
184db1312ddSHannes Reinecke struct nvmet_ctrl *ctrl = req->sq->ctrl;
185db1312ddSHannes Reinecke struct nvmf_auth_dhchap_success2_data *data;
186db1312ddSHannes Reinecke void *d;
187db1312ddSHannes Reinecke u32 tl;
188db1312ddSHannes Reinecke u16 status = 0;
18944e3c25eSHannes Reinecke u8 dhchap_status;
190db1312ddSHannes Reinecke
191db1312ddSHannes Reinecke if (req->cmd->auth_send.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) {
192*dd0b0a4aSWeiwen Hu status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
193db1312ddSHannes Reinecke req->error_loc =
194db1312ddSHannes Reinecke offsetof(struct nvmf_auth_send_command, secp);
195db1312ddSHannes Reinecke goto done;
196db1312ddSHannes Reinecke }
197db1312ddSHannes Reinecke if (req->cmd->auth_send.spsp0 != 0x01) {
198*dd0b0a4aSWeiwen Hu status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
199db1312ddSHannes Reinecke req->error_loc =
200db1312ddSHannes Reinecke offsetof(struct nvmf_auth_send_command, spsp0);
201db1312ddSHannes Reinecke goto done;
202db1312ddSHannes Reinecke }
203db1312ddSHannes Reinecke if (req->cmd->auth_send.spsp1 != 0x01) {
204*dd0b0a4aSWeiwen Hu status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
205db1312ddSHannes Reinecke req->error_loc =
206db1312ddSHannes Reinecke offsetof(struct nvmf_auth_send_command, spsp1);
207db1312ddSHannes Reinecke goto done;
208db1312ddSHannes Reinecke }
209db1312ddSHannes Reinecke tl = le32_to_cpu(req->cmd->auth_send.tl);
210db1312ddSHannes Reinecke if (!tl) {
211*dd0b0a4aSWeiwen Hu status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
212db1312ddSHannes Reinecke req->error_loc =
213db1312ddSHannes Reinecke offsetof(struct nvmf_auth_send_command, tl);
214db1312ddSHannes Reinecke goto done;
215db1312ddSHannes Reinecke }
216db1312ddSHannes Reinecke if (!nvmet_check_transfer_len(req, tl)) {
217db1312ddSHannes Reinecke pr_debug("%s: transfer length mismatch (%u)\n", __func__, tl);
218db1312ddSHannes Reinecke return;
219db1312ddSHannes Reinecke }
220db1312ddSHannes Reinecke
221db1312ddSHannes Reinecke d = kmalloc(tl, GFP_KERNEL);
222db1312ddSHannes Reinecke if (!d) {
223db1312ddSHannes Reinecke status = NVME_SC_INTERNAL;
224db1312ddSHannes Reinecke goto done;
225db1312ddSHannes Reinecke }
226db1312ddSHannes Reinecke
227db1312ddSHannes Reinecke status = nvmet_copy_from_sgl(req, 0, d, tl);
22842147981SJackie Liu if (status)
22942147981SJackie Liu goto done_kfree;
230db1312ddSHannes Reinecke
231db1312ddSHannes Reinecke data = d;
232db1312ddSHannes Reinecke pr_debug("%s: ctrl %d qid %d type %d id %d step %x\n", __func__,
233db1312ddSHannes Reinecke ctrl->cntlid, req->sq->qid, data->auth_type, data->auth_id,
234db1312ddSHannes Reinecke req->sq->dhchap_step);
235db1312ddSHannes Reinecke if (data->auth_type != NVME_AUTH_COMMON_MESSAGES &&
236db1312ddSHannes Reinecke data->auth_type != NVME_AUTH_DHCHAP_MESSAGES)
237db1312ddSHannes Reinecke goto done_failure1;
238db1312ddSHannes Reinecke if (data->auth_type == NVME_AUTH_COMMON_MESSAGES) {
239db1312ddSHannes Reinecke if (data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE) {
240db1312ddSHannes Reinecke /* Restart negotiation */
24144e3c25eSHannes Reinecke pr_debug("%s: ctrl %d qid %d reset negotiation\n",
24244e3c25eSHannes Reinecke __func__, ctrl->cntlid, req->sq->qid);
243db1312ddSHannes Reinecke if (!req->sq->qid) {
24444e3c25eSHannes Reinecke dhchap_status = nvmet_setup_auth(ctrl);
24544e3c25eSHannes Reinecke if (dhchap_status) {
24644e3c25eSHannes Reinecke pr_err("ctrl %d qid 0 failed to setup re-authentication\n",
247db1312ddSHannes Reinecke ctrl->cntlid);
24844e3c25eSHannes Reinecke req->sq->dhchap_status = dhchap_status;
24944e3c25eSHannes Reinecke req->sq->dhchap_step =
25044e3c25eSHannes Reinecke NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
25144e3c25eSHannes Reinecke goto done_kfree;
252db1312ddSHannes Reinecke }
253db1312ddSHannes Reinecke }
25444e3c25eSHannes Reinecke req->sq->dhchap_step =
25544e3c25eSHannes Reinecke NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
256db1312ddSHannes Reinecke } else if (data->auth_id != req->sq->dhchap_step)
257db1312ddSHannes Reinecke goto done_failure1;
258db1312ddSHannes Reinecke /* Validate negotiation parameters */
25944e3c25eSHannes Reinecke dhchap_status = nvmet_auth_negotiate(req, d);
26044e3c25eSHannes Reinecke if (dhchap_status == 0)
261db1312ddSHannes Reinecke req->sq->dhchap_step =
262db1312ddSHannes Reinecke NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
263db1312ddSHannes Reinecke else {
264db1312ddSHannes Reinecke req->sq->dhchap_step =
265db1312ddSHannes Reinecke NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
26644e3c25eSHannes Reinecke req->sq->dhchap_status = dhchap_status;
267db1312ddSHannes Reinecke }
268db1312ddSHannes Reinecke goto done_kfree;
269db1312ddSHannes Reinecke }
270db1312ddSHannes Reinecke if (data->auth_id != req->sq->dhchap_step) {
271db1312ddSHannes Reinecke pr_debug("%s: ctrl %d qid %d step mismatch (%d != %d)\n",
272db1312ddSHannes Reinecke __func__, ctrl->cntlid, req->sq->qid,
273db1312ddSHannes Reinecke data->auth_id, req->sq->dhchap_step);
274db1312ddSHannes Reinecke goto done_failure1;
275db1312ddSHannes Reinecke }
276db1312ddSHannes Reinecke if (le16_to_cpu(data->t_id) != req->sq->dhchap_tid) {
277db1312ddSHannes Reinecke pr_debug("%s: ctrl %d qid %d invalid transaction %d (expected %d)\n",
278db1312ddSHannes Reinecke __func__, ctrl->cntlid, req->sq->qid,
279db1312ddSHannes Reinecke le16_to_cpu(data->t_id),
280db1312ddSHannes Reinecke req->sq->dhchap_tid);
281db1312ddSHannes Reinecke req->sq->dhchap_step =
282db1312ddSHannes Reinecke NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
283db1312ddSHannes Reinecke req->sq->dhchap_status =
284db1312ddSHannes Reinecke NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
285db1312ddSHannes Reinecke goto done_kfree;
286db1312ddSHannes Reinecke }
287db1312ddSHannes Reinecke
288db1312ddSHannes Reinecke switch (data->auth_id) {
289db1312ddSHannes Reinecke case NVME_AUTH_DHCHAP_MESSAGE_REPLY:
29044e3c25eSHannes Reinecke dhchap_status = nvmet_auth_reply(req, d);
29144e3c25eSHannes Reinecke if (dhchap_status == 0)
292db1312ddSHannes Reinecke req->sq->dhchap_step =
293db1312ddSHannes Reinecke NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1;
294db1312ddSHannes Reinecke else {
295db1312ddSHannes Reinecke req->sq->dhchap_step =
296db1312ddSHannes Reinecke NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
29744e3c25eSHannes Reinecke req->sq->dhchap_status = dhchap_status;
298db1312ddSHannes Reinecke }
299db1312ddSHannes Reinecke goto done_kfree;
300db1312ddSHannes Reinecke case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2:
301db1312ddSHannes Reinecke req->sq->authenticated = true;
302db1312ddSHannes Reinecke pr_debug("%s: ctrl %d qid %d ctrl authenticated\n",
303db1312ddSHannes Reinecke __func__, ctrl->cntlid, req->sq->qid);
304db1312ddSHannes Reinecke goto done_kfree;
305db1312ddSHannes Reinecke case NVME_AUTH_DHCHAP_MESSAGE_FAILURE2:
30644e3c25eSHannes Reinecke dhchap_status = nvmet_auth_failure2(d);
30744e3c25eSHannes Reinecke if (dhchap_status) {
308db1312ddSHannes Reinecke pr_warn("ctrl %d qid %d: authentication failed (%d)\n",
30944e3c25eSHannes Reinecke ctrl->cntlid, req->sq->qid, dhchap_status);
31044e3c25eSHannes Reinecke req->sq->dhchap_status = dhchap_status;
311db1312ddSHannes Reinecke req->sq->authenticated = false;
312db1312ddSHannes Reinecke }
313db1312ddSHannes Reinecke goto done_kfree;
314db1312ddSHannes Reinecke default:
315db1312ddSHannes Reinecke req->sq->dhchap_status =
316db1312ddSHannes Reinecke NVME_AUTH_DHCHAP_FAILURE_INCORRECT_MESSAGE;
317db1312ddSHannes Reinecke req->sq->dhchap_step =
318db1312ddSHannes Reinecke NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
319db1312ddSHannes Reinecke req->sq->authenticated = false;
320db1312ddSHannes Reinecke goto done_kfree;
321db1312ddSHannes Reinecke }
322db1312ddSHannes Reinecke done_failure1:
323db1312ddSHannes Reinecke req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_MESSAGE;
324db1312ddSHannes Reinecke req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
325db1312ddSHannes Reinecke
326db1312ddSHannes Reinecke done_kfree:
327db1312ddSHannes Reinecke kfree(d);
328db1312ddSHannes Reinecke done:
329db1312ddSHannes Reinecke pr_debug("%s: ctrl %d qid %d dhchap status %x step %x\n", __func__,
330db1312ddSHannes Reinecke ctrl->cntlid, req->sq->qid,
331db1312ddSHannes Reinecke req->sq->dhchap_status, req->sq->dhchap_step);
332db1312ddSHannes Reinecke if (status)
333db1312ddSHannes Reinecke pr_debug("%s: ctrl %d qid %d nvme status %x error loc %d\n",
334db1312ddSHannes Reinecke __func__, ctrl->cntlid, req->sq->qid,
335db1312ddSHannes Reinecke status, req->error_loc);
336db1312ddSHannes Reinecke if (req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 &&
3371a70200fSHannes Reinecke req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_FAILURE2) {
3381a70200fSHannes Reinecke unsigned long auth_expire_secs = ctrl->kato ? ctrl->kato : 120;
3391a70200fSHannes Reinecke
3401a70200fSHannes Reinecke mod_delayed_work(system_wq, &req->sq->auth_expired_work,
3411a70200fSHannes Reinecke auth_expire_secs * HZ);
342f965b281SMaurizio Lombardi goto complete;
3431a70200fSHannes Reinecke }
344db1312ddSHannes Reinecke /* Final states, clear up variables */
345db1312ddSHannes Reinecke nvmet_auth_sq_free(req->sq);
346db1312ddSHannes Reinecke if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE2)
347db1312ddSHannes Reinecke nvmet_ctrl_fatal_error(ctrl);
348f965b281SMaurizio Lombardi
349f965b281SMaurizio Lombardi complete:
350f965b281SMaurizio Lombardi nvmet_req_complete(req, status);
351db1312ddSHannes Reinecke }
352db1312ddSHannes Reinecke
nvmet_auth_challenge(struct nvmet_req * req,void * d,int al)353db1312ddSHannes Reinecke static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al)
354db1312ddSHannes Reinecke {
355db1312ddSHannes Reinecke struct nvmf_auth_dhchap_challenge_data *data = d;
356db1312ddSHannes Reinecke struct nvmet_ctrl *ctrl = req->sq->ctrl;
357db1312ddSHannes Reinecke int ret = 0;
358db1312ddSHannes Reinecke int hash_len = nvme_auth_hmac_hash_len(ctrl->shash_id);
359db1312ddSHannes Reinecke int data_size = sizeof(*d) + hash_len;
360db1312ddSHannes Reinecke
3617a277c37SHannes Reinecke if (ctrl->dh_tfm)
3627a277c37SHannes Reinecke data_size += ctrl->dh_keysize;
363db1312ddSHannes Reinecke if (al < data_size) {
364db1312ddSHannes Reinecke pr_debug("%s: buffer too small (al %d need %d)\n", __func__,
365db1312ddSHannes Reinecke al, data_size);
366db1312ddSHannes Reinecke return -EINVAL;
367db1312ddSHannes Reinecke }
368db1312ddSHannes Reinecke memset(data, 0, data_size);
369db1312ddSHannes Reinecke req->sq->dhchap_s1 = nvme_auth_get_seqnum();
370db1312ddSHannes Reinecke data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
371db1312ddSHannes Reinecke data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
372db1312ddSHannes Reinecke data->t_id = cpu_to_le16(req->sq->dhchap_tid);
373db1312ddSHannes Reinecke data->hashid = ctrl->shash_id;
374db1312ddSHannes Reinecke data->hl = hash_len;
375db1312ddSHannes Reinecke data->seqnum = cpu_to_le32(req->sq->dhchap_s1);
376db1312ddSHannes Reinecke req->sq->dhchap_c1 = kmalloc(data->hl, GFP_KERNEL);
377db1312ddSHannes Reinecke if (!req->sq->dhchap_c1)
378db1312ddSHannes Reinecke return -ENOMEM;
379db1312ddSHannes Reinecke get_random_bytes(req->sq->dhchap_c1, data->hl);
380db1312ddSHannes Reinecke memcpy(data->cval, req->sq->dhchap_c1, data->hl);
3817a277c37SHannes Reinecke if (ctrl->dh_tfm) {
3827a277c37SHannes Reinecke data->dhgid = ctrl->dh_gid;
3837a277c37SHannes Reinecke data->dhvlen = cpu_to_le16(ctrl->dh_keysize);
3847a277c37SHannes Reinecke ret = nvmet_auth_ctrl_exponential(req, data->cval + data->hl,
3857a277c37SHannes Reinecke ctrl->dh_keysize);
3867a277c37SHannes Reinecke }
3877a277c37SHannes Reinecke pr_debug("%s: ctrl %d qid %d seq %d transaction %d hl %d dhvlen %zu\n",
388db1312ddSHannes Reinecke __func__, ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1,
3897a277c37SHannes Reinecke req->sq->dhchap_tid, data->hl, ctrl->dh_keysize);
390db1312ddSHannes Reinecke return ret;
391db1312ddSHannes Reinecke }
392db1312ddSHannes Reinecke
nvmet_auth_success1(struct nvmet_req * req,void * d,int al)393db1312ddSHannes Reinecke static int nvmet_auth_success1(struct nvmet_req *req, void *d, int al)
394db1312ddSHannes Reinecke {
395db1312ddSHannes Reinecke struct nvmf_auth_dhchap_success1_data *data = d;
396db1312ddSHannes Reinecke struct nvmet_ctrl *ctrl = req->sq->ctrl;
397db1312ddSHannes Reinecke int hash_len = nvme_auth_hmac_hash_len(ctrl->shash_id);
398db1312ddSHannes Reinecke
399db1312ddSHannes Reinecke WARN_ON(al < sizeof(*data));
400db1312ddSHannes Reinecke memset(data, 0, sizeof(*data));
401db1312ddSHannes Reinecke data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
402db1312ddSHannes Reinecke data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1;
403db1312ddSHannes Reinecke data->t_id = cpu_to_le16(req->sq->dhchap_tid);
404db1312ddSHannes Reinecke data->hl = hash_len;
405db1312ddSHannes Reinecke if (req->sq->dhchap_c2) {
406db1312ddSHannes Reinecke if (!ctrl->ctrl_key) {
407db1312ddSHannes Reinecke pr_warn("ctrl %d qid %d no ctrl key\n",
408db1312ddSHannes Reinecke ctrl->cntlid, req->sq->qid);
409db1312ddSHannes Reinecke return NVME_AUTH_DHCHAP_FAILURE_FAILED;
410db1312ddSHannes Reinecke }
411db1312ddSHannes Reinecke if (nvmet_auth_ctrl_hash(req, data->rval, data->hl))
412db1312ddSHannes Reinecke return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
413db1312ddSHannes Reinecke data->rvalid = 1;
414db1312ddSHannes Reinecke pr_debug("ctrl %d qid %d response %*ph\n",
415db1312ddSHannes Reinecke ctrl->cntlid, req->sq->qid, data->hl, data->rval);
416db1312ddSHannes Reinecke }
417db1312ddSHannes Reinecke return 0;
418db1312ddSHannes Reinecke }
419db1312ddSHannes Reinecke
nvmet_auth_failure1(struct nvmet_req * req,void * d,int al)420db1312ddSHannes Reinecke static void nvmet_auth_failure1(struct nvmet_req *req, void *d, int al)
421db1312ddSHannes Reinecke {
422db1312ddSHannes Reinecke struct nvmf_auth_dhchap_failure_data *data = d;
423db1312ddSHannes Reinecke
424db1312ddSHannes Reinecke WARN_ON(al < sizeof(*data));
425db1312ddSHannes Reinecke data->auth_type = NVME_AUTH_COMMON_MESSAGES;
426db1312ddSHannes Reinecke data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
427db1312ddSHannes Reinecke data->t_id = cpu_to_le16(req->sq->dhchap_tid);
428db1312ddSHannes Reinecke data->rescode = NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED;
429db1312ddSHannes Reinecke data->rescode_exp = req->sq->dhchap_status;
430db1312ddSHannes Reinecke }
431db1312ddSHannes Reinecke
nvmet_execute_auth_receive(struct nvmet_req * req)432db1312ddSHannes Reinecke void nvmet_execute_auth_receive(struct nvmet_req *req)
433db1312ddSHannes Reinecke {
434db1312ddSHannes Reinecke struct nvmet_ctrl *ctrl = req->sq->ctrl;
435db1312ddSHannes Reinecke void *d;
436db1312ddSHannes Reinecke u32 al;
437db1312ddSHannes Reinecke u16 status = 0;
438db1312ddSHannes Reinecke
439db1312ddSHannes Reinecke if (req->cmd->auth_receive.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) {
440*dd0b0a4aSWeiwen Hu status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
441db1312ddSHannes Reinecke req->error_loc =
442db1312ddSHannes Reinecke offsetof(struct nvmf_auth_receive_command, secp);
443db1312ddSHannes Reinecke goto done;
444db1312ddSHannes Reinecke }
445db1312ddSHannes Reinecke if (req->cmd->auth_receive.spsp0 != 0x01) {
446*dd0b0a4aSWeiwen Hu status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
447db1312ddSHannes Reinecke req->error_loc =
448db1312ddSHannes Reinecke offsetof(struct nvmf_auth_receive_command, spsp0);
449db1312ddSHannes Reinecke goto done;
450db1312ddSHannes Reinecke }
451db1312ddSHannes Reinecke if (req->cmd->auth_receive.spsp1 != 0x01) {
452*dd0b0a4aSWeiwen Hu status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
453db1312ddSHannes Reinecke req->error_loc =
454db1312ddSHannes Reinecke offsetof(struct nvmf_auth_receive_command, spsp1);
455db1312ddSHannes Reinecke goto done;
456db1312ddSHannes Reinecke }
457db1312ddSHannes Reinecke al = le32_to_cpu(req->cmd->auth_receive.al);
458db1312ddSHannes Reinecke if (!al) {
459*dd0b0a4aSWeiwen Hu status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
460db1312ddSHannes Reinecke req->error_loc =
461db1312ddSHannes Reinecke offsetof(struct nvmf_auth_receive_command, al);
462db1312ddSHannes Reinecke goto done;
463db1312ddSHannes Reinecke }
464db1312ddSHannes Reinecke if (!nvmet_check_transfer_len(req, al)) {
465db1312ddSHannes Reinecke pr_debug("%s: transfer length mismatch (%u)\n", __func__, al);
466db1312ddSHannes Reinecke return;
467db1312ddSHannes Reinecke }
468db1312ddSHannes Reinecke
469db1312ddSHannes Reinecke d = kmalloc(al, GFP_KERNEL);
470db1312ddSHannes Reinecke if (!d) {
471db1312ddSHannes Reinecke status = NVME_SC_INTERNAL;
472db1312ddSHannes Reinecke goto done;
473db1312ddSHannes Reinecke }
474db1312ddSHannes Reinecke pr_debug("%s: ctrl %d qid %d step %x\n", __func__,
475db1312ddSHannes Reinecke ctrl->cntlid, req->sq->qid, req->sq->dhchap_step);
476db1312ddSHannes Reinecke switch (req->sq->dhchap_step) {
477db1312ddSHannes Reinecke case NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE:
478be2ada6dSChaitanya Kulkarni if (nvmet_auth_challenge(req, d, al) < 0) {
479db1312ddSHannes Reinecke pr_warn("ctrl %d qid %d: challenge error (%d)\n",
480db1312ddSHannes Reinecke ctrl->cntlid, req->sq->qid, status);
481db1312ddSHannes Reinecke status = NVME_SC_INTERNAL;
482db1312ddSHannes Reinecke break;
483db1312ddSHannes Reinecke }
484db1312ddSHannes Reinecke req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_REPLY;
485db1312ddSHannes Reinecke break;
486db1312ddSHannes Reinecke case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1:
487db1312ddSHannes Reinecke status = nvmet_auth_success1(req, d, al);
488db1312ddSHannes Reinecke if (status) {
489db1312ddSHannes Reinecke req->sq->dhchap_status = status;
490db1312ddSHannes Reinecke req->sq->authenticated = false;
491db1312ddSHannes Reinecke nvmet_auth_failure1(req, d, al);
492db1312ddSHannes Reinecke pr_warn("ctrl %d qid %d: success1 status (%x)\n",
493db1312ddSHannes Reinecke ctrl->cntlid, req->sq->qid,
494db1312ddSHannes Reinecke req->sq->dhchap_status);
495db1312ddSHannes Reinecke break;
496db1312ddSHannes Reinecke }
497db1312ddSHannes Reinecke req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2;
498db1312ddSHannes Reinecke break;
499db1312ddSHannes Reinecke case NVME_AUTH_DHCHAP_MESSAGE_FAILURE1:
500db1312ddSHannes Reinecke req->sq->authenticated = false;
501db1312ddSHannes Reinecke nvmet_auth_failure1(req, d, al);
502db1312ddSHannes Reinecke pr_warn("ctrl %d qid %d failure1 (%x)\n",
503db1312ddSHannes Reinecke ctrl->cntlid, req->sq->qid, req->sq->dhchap_status);
504db1312ddSHannes Reinecke break;
505db1312ddSHannes Reinecke default:
506db1312ddSHannes Reinecke pr_warn("ctrl %d qid %d unhandled step (%d)\n",
507db1312ddSHannes Reinecke ctrl->cntlid, req->sq->qid, req->sq->dhchap_step);
508db1312ddSHannes Reinecke req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
509db1312ddSHannes Reinecke req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
510db1312ddSHannes Reinecke nvmet_auth_failure1(req, d, al);
511db1312ddSHannes Reinecke status = 0;
512db1312ddSHannes Reinecke break;
513db1312ddSHannes Reinecke }
514db1312ddSHannes Reinecke
515db1312ddSHannes Reinecke status = nvmet_copy_to_sgl(req, 0, d, al);
516db1312ddSHannes Reinecke kfree(d);
517db1312ddSHannes Reinecke done:
518db1312ddSHannes Reinecke if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2)
519db1312ddSHannes Reinecke nvmet_auth_sq_free(req->sq);
520db1312ddSHannes Reinecke else if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) {
521db1312ddSHannes Reinecke nvmet_auth_sq_free(req->sq);
522db1312ddSHannes Reinecke nvmet_ctrl_fatal_error(ctrl);
523db1312ddSHannes Reinecke }
524f965b281SMaurizio Lombardi nvmet_req_complete(req, status);
525db1312ddSHannes Reinecke }
526