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