xref: /linux/drivers/nvme/target/fabrics-cmd-auth.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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