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