1// SPDX-License-Identifier: GPL-2.0
2/*
3 * NVMe over Fabrics DH-HMAC-CHAP authentication.
4 * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions.
5 * All rights reserved.
6 */
7#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
8#include <linux/module.h>
9#include <linux/init.h>
10#include <linux/slab.h>
11#include <linux/err.h>
12#include <crypto/hash.h>
13#include <linux/crc32.h>
14#include <linux/base64.h>
15#include <linux/ctype.h>
16#include <linux/random.h>
17#include <linux/nvme-auth.h>
18#include <asm/unaligned.h>
19
20#include "nvmet.h"
21
22int nvmet_auth_set_key(struct nvmet_host *host, const char *secret,
23		       bool set_ctrl)
24{
25	unsigned char key_hash;
26	char *dhchap_secret;
27
28	if (sscanf(secret, "DHHC-1:%hhd:%*s", &key_hash) != 1)
29		return -EINVAL;
30	if (key_hash > 3) {
31		pr_warn("Invalid DH-HMAC-CHAP hash id %d\n",
32			 key_hash);
33		return -EINVAL;
34	}
35	if (key_hash > 0) {
36		/* Validate selected hash algorithm */
37		const char *hmac = nvme_auth_hmac_name(key_hash);
38
39		if (!crypto_has_shash(hmac, 0, 0)) {
40			pr_err("DH-HMAC-CHAP hash %s unsupported\n", hmac);
41			return -ENOTSUPP;
42		}
43	}
44	dhchap_secret = kstrdup(secret, GFP_KERNEL);
45	if (!dhchap_secret)
46		return -ENOMEM;
47	if (set_ctrl) {
48		kfree(host->dhchap_ctrl_secret);
49		host->dhchap_ctrl_secret = strim(dhchap_secret);
50		host->dhchap_ctrl_key_hash = key_hash;
51	} else {
52		kfree(host->dhchap_secret);
53		host->dhchap_secret = strim(dhchap_secret);
54		host->dhchap_key_hash = key_hash;
55	}
56	return 0;
57}
58
59int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, u8 dhgroup_id)
60{
61	const char *dhgroup_kpp;
62	int ret = 0;
63
64	pr_debug("%s: ctrl %d selecting dhgroup %d\n",
65		 __func__, ctrl->cntlid, dhgroup_id);
66
67	if (ctrl->dh_tfm) {
68		if (ctrl->dh_gid == dhgroup_id) {
69			pr_debug("%s: ctrl %d reuse existing DH group %d\n",
70				 __func__, ctrl->cntlid, dhgroup_id);
71			return 0;
72		}
73		crypto_free_kpp(ctrl->dh_tfm);
74		ctrl->dh_tfm = NULL;
75		ctrl->dh_gid = 0;
76	}
77
78	if (dhgroup_id == NVME_AUTH_DHGROUP_NULL)
79		return 0;
80
81	dhgroup_kpp = nvme_auth_dhgroup_kpp(dhgroup_id);
82	if (!dhgroup_kpp) {
83		pr_debug("%s: ctrl %d invalid DH group %d\n",
84			 __func__, ctrl->cntlid, dhgroup_id);
85		return -EINVAL;
86	}
87	ctrl->dh_tfm = crypto_alloc_kpp(dhgroup_kpp, 0, 0);
88	if (IS_ERR(ctrl->dh_tfm)) {
89		pr_debug("%s: ctrl %d failed to setup DH group %d, err %ld\n",
90			 __func__, ctrl->cntlid, dhgroup_id,
91			 PTR_ERR(ctrl->dh_tfm));
92		ret = PTR_ERR(ctrl->dh_tfm);
93		ctrl->dh_tfm = NULL;
94		ctrl->dh_gid = 0;
95	} else {
96		ctrl->dh_gid = dhgroup_id;
97		pr_debug("%s: ctrl %d setup DH group %d\n",
98			 __func__, ctrl->cntlid, ctrl->dh_gid);
99		ret = nvme_auth_gen_privkey(ctrl->dh_tfm, ctrl->dh_gid);
100		if (ret < 0) {
101			pr_debug("%s: ctrl %d failed to generate private key, err %d\n",
102				 __func__, ctrl->cntlid, ret);
103			kfree_sensitive(ctrl->dh_key);
104			return ret;
105		}
106		ctrl->dh_keysize = crypto_kpp_maxsize(ctrl->dh_tfm);
107		kfree_sensitive(ctrl->dh_key);
108		ctrl->dh_key = kzalloc(ctrl->dh_keysize, GFP_KERNEL);
109		if (!ctrl->dh_key) {
110			pr_warn("ctrl %d failed to allocate public key\n",
111				ctrl->cntlid);
112			return -ENOMEM;
113		}
114		ret = nvme_auth_gen_pubkey(ctrl->dh_tfm, ctrl->dh_key,
115					   ctrl->dh_keysize);
116		if (ret < 0) {
117			pr_warn("ctrl %d failed to generate public key\n",
118				ctrl->cntlid);
119			kfree(ctrl->dh_key);
120			ctrl->dh_key = NULL;
121		}
122	}
123
124	return ret;
125}
126
127int nvmet_setup_auth(struct nvmet_ctrl *ctrl)
128{
129	int ret = 0;
130	struct nvmet_host_link *p;
131	struct nvmet_host *host = NULL;
132	const char *hash_name;
133
134	down_read(&nvmet_config_sem);
135	if (nvmet_is_disc_subsys(ctrl->subsys))
136		goto out_unlock;
137
138	if (ctrl->subsys->allow_any_host)
139		goto out_unlock;
140
141	list_for_each_entry(p, &ctrl->subsys->hosts, entry) {
142		pr_debug("check %s\n", nvmet_host_name(p->host));
143		if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn))
144			continue;
145		host = p->host;
146		break;
147	}
148	if (!host) {
149		pr_debug("host %s not found\n", ctrl->hostnqn);
150		ret = -EPERM;
151		goto out_unlock;
152	}
153
154	ret = nvmet_setup_dhgroup(ctrl, host->dhchap_dhgroup_id);
155	if (ret < 0)
156		pr_warn("Failed to setup DH group");
157
158	if (!host->dhchap_secret) {
159		pr_debug("No authentication provided\n");
160		goto out_unlock;
161	}
162
163	if (host->dhchap_hash_id == ctrl->shash_id) {
164		pr_debug("Re-use existing hash ID %d\n",
165			 ctrl->shash_id);
166	} else {
167		hash_name = nvme_auth_hmac_name(host->dhchap_hash_id);
168		if (!hash_name) {
169			pr_warn("Hash ID %d invalid\n", host->dhchap_hash_id);
170			ret = -EINVAL;
171			goto out_unlock;
172		}
173		ctrl->shash_id = host->dhchap_hash_id;
174	}
175
176	/* Skip the 'DHHC-1:XX:' prefix */
177	nvme_auth_free_key(ctrl->host_key);
178	ctrl->host_key = nvme_auth_extract_key(host->dhchap_secret + 10,
179					       host->dhchap_key_hash);
180	if (IS_ERR(ctrl->host_key)) {
181		ret = PTR_ERR(ctrl->host_key);
182		ctrl->host_key = NULL;
183		goto out_free_hash;
184	}
185	pr_debug("%s: using hash %s key %*ph\n", __func__,
186		 ctrl->host_key->hash > 0 ?
187		 nvme_auth_hmac_name(ctrl->host_key->hash) : "none",
188		 (int)ctrl->host_key->len, ctrl->host_key->key);
189
190	nvme_auth_free_key(ctrl->ctrl_key);
191	if (!host->dhchap_ctrl_secret) {
192		ctrl->ctrl_key = NULL;
193		goto out_unlock;
194	}
195
196	ctrl->ctrl_key = nvme_auth_extract_key(host->dhchap_ctrl_secret + 10,
197					       host->dhchap_ctrl_key_hash);
198	if (IS_ERR(ctrl->ctrl_key)) {
199		ret = PTR_ERR(ctrl->ctrl_key);
200		ctrl->ctrl_key = NULL;
201		goto out_free_hash;
202	}
203	pr_debug("%s: using ctrl hash %s key %*ph\n", __func__,
204		 ctrl->ctrl_key->hash > 0 ?
205		 nvme_auth_hmac_name(ctrl->ctrl_key->hash) : "none",
206		 (int)ctrl->ctrl_key->len, ctrl->ctrl_key->key);
207
208out_free_hash:
209	if (ret) {
210		if (ctrl->host_key) {
211			nvme_auth_free_key(ctrl->host_key);
212			ctrl->host_key = NULL;
213		}
214		ctrl->shash_id = 0;
215	}
216out_unlock:
217	up_read(&nvmet_config_sem);
218
219	return ret;
220}
221
222void nvmet_auth_sq_free(struct nvmet_sq *sq)
223{
224	cancel_delayed_work(&sq->auth_expired_work);
225	kfree(sq->dhchap_c1);
226	sq->dhchap_c1 = NULL;
227	kfree(sq->dhchap_c2);
228	sq->dhchap_c2 = NULL;
229	kfree(sq->dhchap_skey);
230	sq->dhchap_skey = NULL;
231}
232
233void nvmet_destroy_auth(struct nvmet_ctrl *ctrl)
234{
235	ctrl->shash_id = 0;
236
237	if (ctrl->dh_tfm) {
238		crypto_free_kpp(ctrl->dh_tfm);
239		ctrl->dh_tfm = NULL;
240		ctrl->dh_gid = 0;
241	}
242	kfree_sensitive(ctrl->dh_key);
243	ctrl->dh_key = NULL;
244
245	if (ctrl->host_key) {
246		nvme_auth_free_key(ctrl->host_key);
247		ctrl->host_key = NULL;
248	}
249	if (ctrl->ctrl_key) {
250		nvme_auth_free_key(ctrl->ctrl_key);
251		ctrl->ctrl_key = NULL;
252	}
253}
254
255bool nvmet_check_auth_status(struct nvmet_req *req)
256{
257	if (req->sq->ctrl->host_key &&
258	    !req->sq->authenticated)
259		return false;
260	return true;
261}
262
263int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
264			 unsigned int shash_len)
265{
266	struct crypto_shash *shash_tfm;
267	struct shash_desc *shash;
268	struct nvmet_ctrl *ctrl = req->sq->ctrl;
269	const char *hash_name;
270	u8 *challenge = req->sq->dhchap_c1;
271	struct nvme_dhchap_key *transformed_key;
272	u8 buf[4];
273	int ret;
274
275	hash_name = nvme_auth_hmac_name(ctrl->shash_id);
276	if (!hash_name) {
277		pr_warn("Hash ID %d invalid\n", ctrl->shash_id);
278		return -EINVAL;
279	}
280
281	shash_tfm = crypto_alloc_shash(hash_name, 0, 0);
282	if (IS_ERR(shash_tfm)) {
283		pr_err("failed to allocate shash %s\n", hash_name);
284		return PTR_ERR(shash_tfm);
285	}
286
287	if (shash_len != crypto_shash_digestsize(shash_tfm)) {
288		pr_debug("%s: hash len mismatch (len %d digest %d)\n",
289			 __func__, shash_len,
290			 crypto_shash_digestsize(shash_tfm));
291		ret = -EINVAL;
292		goto out_free_tfm;
293	}
294
295	transformed_key = nvme_auth_transform_key(ctrl->host_key,
296						  ctrl->hostnqn);
297	if (IS_ERR(transformed_key)) {
298		ret = PTR_ERR(transformed_key);
299		goto out_free_tfm;
300	}
301
302	ret = crypto_shash_setkey(shash_tfm, transformed_key->key,
303				  transformed_key->len);
304	if (ret)
305		goto out_free_response;
306
307	if (ctrl->dh_gid != NVME_AUTH_DHGROUP_NULL) {
308		challenge = kmalloc(shash_len, GFP_KERNEL);
309		if (!challenge) {
310			ret = -ENOMEM;
311			goto out_free_response;
312		}
313		ret = nvme_auth_augmented_challenge(ctrl->shash_id,
314						    req->sq->dhchap_skey,
315						    req->sq->dhchap_skey_len,
316						    req->sq->dhchap_c1,
317						    challenge, shash_len);
318		if (ret)
319			goto out_free_response;
320	}
321
322	pr_debug("ctrl %d qid %d host response seq %u transaction %d\n",
323		 ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1,
324		 req->sq->dhchap_tid);
325
326	shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(shash_tfm),
327			GFP_KERNEL);
328	if (!shash) {
329		ret = -ENOMEM;
330		goto out_free_response;
331	}
332	shash->tfm = shash_tfm;
333	ret = crypto_shash_init(shash);
334	if (ret)
335		goto out;
336	ret = crypto_shash_update(shash, challenge, shash_len);
337	if (ret)
338		goto out;
339	put_unaligned_le32(req->sq->dhchap_s1, buf);
340	ret = crypto_shash_update(shash, buf, 4);
341	if (ret)
342		goto out;
343	put_unaligned_le16(req->sq->dhchap_tid, buf);
344	ret = crypto_shash_update(shash, buf, 2);
345	if (ret)
346		goto out;
347	memset(buf, 0, 4);
348	ret = crypto_shash_update(shash, buf, 1);
349	if (ret)
350		goto out;
351	ret = crypto_shash_update(shash, "HostHost", 8);
352	if (ret)
353		goto out;
354	ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn));
355	if (ret)
356		goto out;
357	ret = crypto_shash_update(shash, buf, 1);
358	if (ret)
359		goto out;
360	ret = crypto_shash_update(shash, ctrl->subsysnqn,
361				  strlen(ctrl->subsysnqn));
362	if (ret)
363		goto out;
364	ret = crypto_shash_final(shash, response);
365out:
366	if (challenge != req->sq->dhchap_c1)
367		kfree(challenge);
368	kfree(shash);
369out_free_response:
370	nvme_auth_free_key(transformed_key);
371out_free_tfm:
372	crypto_free_shash(shash_tfm);
373	return 0;
374}
375
376int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,
377			 unsigned int shash_len)
378{
379	struct crypto_shash *shash_tfm;
380	struct shash_desc *shash;
381	struct nvmet_ctrl *ctrl = req->sq->ctrl;
382	const char *hash_name;
383	u8 *challenge = req->sq->dhchap_c2;
384	struct nvme_dhchap_key *transformed_key;
385	u8 buf[4];
386	int ret;
387
388	hash_name = nvme_auth_hmac_name(ctrl->shash_id);
389	if (!hash_name) {
390		pr_warn("Hash ID %d invalid\n", ctrl->shash_id);
391		return -EINVAL;
392	}
393
394	shash_tfm = crypto_alloc_shash(hash_name, 0, 0);
395	if (IS_ERR(shash_tfm)) {
396		pr_err("failed to allocate shash %s\n", hash_name);
397		return PTR_ERR(shash_tfm);
398	}
399
400	if (shash_len != crypto_shash_digestsize(shash_tfm)) {
401		pr_debug("%s: hash len mismatch (len %d digest %d)\n",
402			 __func__, shash_len,
403			 crypto_shash_digestsize(shash_tfm));
404		ret = -EINVAL;
405		goto out_free_tfm;
406	}
407
408	transformed_key = nvme_auth_transform_key(ctrl->ctrl_key,
409						ctrl->subsysnqn);
410	if (IS_ERR(transformed_key)) {
411		ret = PTR_ERR(transformed_key);
412		goto out_free_tfm;
413	}
414
415	ret = crypto_shash_setkey(shash_tfm, transformed_key->key,
416				  transformed_key->len);
417	if (ret)
418		goto out_free_response;
419
420	if (ctrl->dh_gid != NVME_AUTH_DHGROUP_NULL) {
421		challenge = kmalloc(shash_len, GFP_KERNEL);
422		if (!challenge) {
423			ret = -ENOMEM;
424			goto out_free_response;
425		}
426		ret = nvme_auth_augmented_challenge(ctrl->shash_id,
427						    req->sq->dhchap_skey,
428						    req->sq->dhchap_skey_len,
429						    req->sq->dhchap_c2,
430						    challenge, shash_len);
431		if (ret)
432			goto out_free_response;
433	}
434
435	shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(shash_tfm),
436			GFP_KERNEL);
437	if (!shash) {
438		ret = -ENOMEM;
439		goto out_free_response;
440	}
441	shash->tfm = shash_tfm;
442
443	ret = crypto_shash_init(shash);
444	if (ret)
445		goto out;
446	ret = crypto_shash_update(shash, challenge, shash_len);
447	if (ret)
448		goto out;
449	put_unaligned_le32(req->sq->dhchap_s2, buf);
450	ret = crypto_shash_update(shash, buf, 4);
451	if (ret)
452		goto out;
453	put_unaligned_le16(req->sq->dhchap_tid, buf);
454	ret = crypto_shash_update(shash, buf, 2);
455	if (ret)
456		goto out;
457	memset(buf, 0, 4);
458	ret = crypto_shash_update(shash, buf, 1);
459	if (ret)
460		goto out;
461	ret = crypto_shash_update(shash, "Controller", 10);
462	if (ret)
463		goto out;
464	ret = crypto_shash_update(shash, ctrl->subsysnqn,
465			    strlen(ctrl->subsysnqn));
466	if (ret)
467		goto out;
468	ret = crypto_shash_update(shash, buf, 1);
469	if (ret)
470		goto out;
471	ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn));
472	if (ret)
473		goto out;
474	ret = crypto_shash_final(shash, response);
475out:
476	if (challenge != req->sq->dhchap_c2)
477		kfree(challenge);
478	kfree(shash);
479out_free_response:
480	nvme_auth_free_key(transformed_key);
481out_free_tfm:
482	crypto_free_shash(shash_tfm);
483	return 0;
484}
485
486int nvmet_auth_ctrl_exponential(struct nvmet_req *req,
487				u8 *buf, int buf_size)
488{
489	struct nvmet_ctrl *ctrl = req->sq->ctrl;
490	int ret = 0;
491
492	if (!ctrl->dh_key) {
493		pr_warn("ctrl %d no DH public key!\n", ctrl->cntlid);
494		return -ENOKEY;
495	}
496	if (buf_size != ctrl->dh_keysize) {
497		pr_warn("ctrl %d DH public key size mismatch, need %zu is %d\n",
498			ctrl->cntlid, ctrl->dh_keysize, buf_size);
499		ret = -EINVAL;
500	} else {
501		memcpy(buf, ctrl->dh_key, buf_size);
502		pr_debug("%s: ctrl %d public key %*ph\n", __func__,
503			 ctrl->cntlid, (int)buf_size, buf);
504	}
505
506	return ret;
507}
508
509int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
510			    u8 *pkey, int pkey_size)
511{
512	struct nvmet_ctrl *ctrl = req->sq->ctrl;
513	int ret;
514
515	req->sq->dhchap_skey_len = ctrl->dh_keysize;
516	req->sq->dhchap_skey = kzalloc(req->sq->dhchap_skey_len, GFP_KERNEL);
517	if (!req->sq->dhchap_skey)
518		return -ENOMEM;
519	ret = nvme_auth_gen_shared_secret(ctrl->dh_tfm,
520					  pkey, pkey_size,
521					  req->sq->dhchap_skey,
522					  req->sq->dhchap_skey_len);
523	if (ret)
524		pr_debug("failed to compute shared secret, err %d\n", ret);
525	else
526		pr_debug("%s: shared secret %*ph\n", __func__,
527			 (int)req->sq->dhchap_skey_len,
528			 req->sq->dhchap_skey);
529
530	return ret;
531}
532