File Index
Files and directories
Sign in
FILE

auth.c

File preview with syntax highlighting.

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