Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * NVMe over Fabrics DH-HMAC-CHAP authentication.
0004  * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions.
0005  * All rights reserved.
0006  */
0007 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0008 #include <linux/module.h>
0009 #include <linux/init.h>
0010 #include <linux/slab.h>
0011 #include <linux/err.h>
0012 #include <crypto/hash.h>
0013 #include <linux/crc32.h>
0014 #include <linux/base64.h>
0015 #include <linux/ctype.h>
0016 #include <linux/random.h>
0017 #include <linux/nvme-auth.h>
0018 #include <asm/unaligned.h>
0019 
0020 #include "nvmet.h"
0021 
0022 int nvmet_auth_set_key(struct nvmet_host *host, const char *secret,
0023                bool set_ctrl)
0024 {
0025     unsigned char key_hash;
0026     char *dhchap_secret;
0027 
0028     if (sscanf(secret, "DHHC-1:%hhd:%*s", &key_hash) != 1)
0029         return -EINVAL;
0030     if (key_hash > 3) {
0031         pr_warn("Invalid DH-HMAC-CHAP hash id %d\n",
0032              key_hash);
0033         return -EINVAL;
0034     }
0035     if (key_hash > 0) {
0036         /* Validate selected hash algorithm */
0037         const char *hmac = nvme_auth_hmac_name(key_hash);
0038 
0039         if (!crypto_has_shash(hmac, 0, 0)) {
0040             pr_err("DH-HMAC-CHAP hash %s unsupported\n", hmac);
0041             return -ENOTSUPP;
0042         }
0043     }
0044     dhchap_secret = kstrdup(secret, GFP_KERNEL);
0045     if (!dhchap_secret)
0046         return -ENOMEM;
0047     if (set_ctrl) {
0048         host->dhchap_ctrl_secret = strim(dhchap_secret);
0049         host->dhchap_ctrl_key_hash = key_hash;
0050     } else {
0051         host->dhchap_secret = strim(dhchap_secret);
0052         host->dhchap_key_hash = key_hash;
0053     }
0054     return 0;
0055 }
0056 
0057 int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, u8 dhgroup_id)
0058 {
0059     const char *dhgroup_kpp;
0060     int ret = 0;
0061 
0062     pr_debug("%s: ctrl %d selecting dhgroup %d\n",
0063          __func__, ctrl->cntlid, dhgroup_id);
0064 
0065     if (ctrl->dh_tfm) {
0066         if (ctrl->dh_gid == dhgroup_id) {
0067             pr_debug("%s: ctrl %d reuse existing DH group %d\n",
0068                  __func__, ctrl->cntlid, dhgroup_id);
0069             return 0;
0070         }
0071         crypto_free_kpp(ctrl->dh_tfm);
0072         ctrl->dh_tfm = NULL;
0073         ctrl->dh_gid = 0;
0074     }
0075 
0076     if (dhgroup_id == NVME_AUTH_DHGROUP_NULL)
0077         return 0;
0078 
0079     dhgroup_kpp = nvme_auth_dhgroup_kpp(dhgroup_id);
0080     if (!dhgroup_kpp) {
0081         pr_debug("%s: ctrl %d invalid DH group %d\n",
0082              __func__, ctrl->cntlid, dhgroup_id);
0083         return -EINVAL;
0084     }
0085     ctrl->dh_tfm = crypto_alloc_kpp(dhgroup_kpp, 0, 0);
0086     if (IS_ERR(ctrl->dh_tfm)) {
0087         pr_debug("%s: ctrl %d failed to setup DH group %d, err %ld\n",
0088              __func__, ctrl->cntlid, dhgroup_id,
0089              PTR_ERR(ctrl->dh_tfm));
0090         ret = PTR_ERR(ctrl->dh_tfm);
0091         ctrl->dh_tfm = NULL;
0092         ctrl->dh_gid = 0;
0093     } else {
0094         ctrl->dh_gid = dhgroup_id;
0095         pr_debug("%s: ctrl %d setup DH group %d\n",
0096              __func__, ctrl->cntlid, ctrl->dh_gid);
0097         ret = nvme_auth_gen_privkey(ctrl->dh_tfm, ctrl->dh_gid);
0098         if (ret < 0) {
0099             pr_debug("%s: ctrl %d failed to generate private key, err %d\n",
0100                  __func__, ctrl->cntlid, ret);
0101             kfree_sensitive(ctrl->dh_key);
0102             return ret;
0103         }
0104         ctrl->dh_keysize = crypto_kpp_maxsize(ctrl->dh_tfm);
0105         kfree_sensitive(ctrl->dh_key);
0106         ctrl->dh_key = kzalloc(ctrl->dh_keysize, GFP_KERNEL);
0107         if (!ctrl->dh_key) {
0108             pr_warn("ctrl %d failed to allocate public key\n",
0109                 ctrl->cntlid);
0110             return -ENOMEM;
0111         }
0112         ret = nvme_auth_gen_pubkey(ctrl->dh_tfm, ctrl->dh_key,
0113                        ctrl->dh_keysize);
0114         if (ret < 0) {
0115             pr_warn("ctrl %d failed to generate public key\n",
0116                 ctrl->cntlid);
0117             kfree(ctrl->dh_key);
0118             ctrl->dh_key = NULL;
0119         }
0120     }
0121 
0122     return ret;
0123 }
0124 
0125 int nvmet_setup_auth(struct nvmet_ctrl *ctrl)
0126 {
0127     int ret = 0;
0128     struct nvmet_host_link *p;
0129     struct nvmet_host *host = NULL;
0130     const char *hash_name;
0131 
0132     down_read(&nvmet_config_sem);
0133     if (nvmet_is_disc_subsys(ctrl->subsys))
0134         goto out_unlock;
0135 
0136     if (ctrl->subsys->allow_any_host)
0137         goto out_unlock;
0138 
0139     list_for_each_entry(p, &ctrl->subsys->hosts, entry) {
0140         pr_debug("check %s\n", nvmet_host_name(p->host));
0141         if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn))
0142             continue;
0143         host = p->host;
0144         break;
0145     }
0146     if (!host) {
0147         pr_debug("host %s not found\n", ctrl->hostnqn);
0148         ret = -EPERM;
0149         goto out_unlock;
0150     }
0151 
0152     ret = nvmet_setup_dhgroup(ctrl, host->dhchap_dhgroup_id);
0153     if (ret < 0)
0154         pr_warn("Failed to setup DH group");
0155 
0156     if (!host->dhchap_secret) {
0157         pr_debug("No authentication provided\n");
0158         goto out_unlock;
0159     }
0160 
0161     if (host->dhchap_hash_id == ctrl->shash_id) {
0162         pr_debug("Re-use existing hash ID %d\n",
0163              ctrl->shash_id);
0164     } else {
0165         hash_name = nvme_auth_hmac_name(host->dhchap_hash_id);
0166         if (!hash_name) {
0167             pr_warn("Hash ID %d invalid\n", host->dhchap_hash_id);
0168             ret = -EINVAL;
0169             goto out_unlock;
0170         }
0171         ctrl->shash_id = host->dhchap_hash_id;
0172     }
0173 
0174     /* Skip the 'DHHC-1:XX:' prefix */
0175     nvme_auth_free_key(ctrl->host_key);
0176     ctrl->host_key = nvme_auth_extract_key(host->dhchap_secret + 10,
0177                            host->dhchap_key_hash);
0178     if (IS_ERR(ctrl->host_key)) {
0179         ret = PTR_ERR(ctrl->host_key);
0180         ctrl->host_key = NULL;
0181         goto out_free_hash;
0182     }
0183     pr_debug("%s: using hash %s key %*ph\n", __func__,
0184          ctrl->host_key->hash > 0 ?
0185          nvme_auth_hmac_name(ctrl->host_key->hash) : "none",
0186          (int)ctrl->host_key->len, ctrl->host_key->key);
0187 
0188     nvme_auth_free_key(ctrl->ctrl_key);
0189     if (!host->dhchap_ctrl_secret) {
0190         ctrl->ctrl_key = NULL;
0191         goto out_unlock;
0192     }
0193 
0194     ctrl->ctrl_key = nvme_auth_extract_key(host->dhchap_ctrl_secret + 10,
0195                            host->dhchap_ctrl_key_hash);
0196     if (IS_ERR(ctrl->ctrl_key)) {
0197         ret = PTR_ERR(ctrl->ctrl_key);
0198         ctrl->ctrl_key = NULL;
0199         goto out_free_hash;
0200     }
0201     pr_debug("%s: using ctrl hash %s key %*ph\n", __func__,
0202          ctrl->ctrl_key->hash > 0 ?
0203          nvme_auth_hmac_name(ctrl->ctrl_key->hash) : "none",
0204          (int)ctrl->ctrl_key->len, ctrl->ctrl_key->key);
0205 
0206 out_free_hash:
0207     if (ret) {
0208         if (ctrl->host_key) {
0209             nvme_auth_free_key(ctrl->host_key);
0210             ctrl->host_key = NULL;
0211         }
0212         ctrl->shash_id = 0;
0213     }
0214 out_unlock:
0215     up_read(&nvmet_config_sem);
0216 
0217     return ret;
0218 }
0219 
0220 void nvmet_auth_sq_free(struct nvmet_sq *sq)
0221 {
0222     cancel_delayed_work(&sq->auth_expired_work);
0223     kfree(sq->dhchap_c1);
0224     sq->dhchap_c1 = NULL;
0225     kfree(sq->dhchap_c2);
0226     sq->dhchap_c2 = NULL;
0227     kfree(sq->dhchap_skey);
0228     sq->dhchap_skey = NULL;
0229 }
0230 
0231 void nvmet_destroy_auth(struct nvmet_ctrl *ctrl)
0232 {
0233     ctrl->shash_id = 0;
0234 
0235     if (ctrl->dh_tfm) {
0236         crypto_free_kpp(ctrl->dh_tfm);
0237         ctrl->dh_tfm = NULL;
0238         ctrl->dh_gid = 0;
0239     }
0240     kfree_sensitive(ctrl->dh_key);
0241     ctrl->dh_key = NULL;
0242 
0243     if (ctrl->host_key) {
0244         nvme_auth_free_key(ctrl->host_key);
0245         ctrl->host_key = NULL;
0246     }
0247     if (ctrl->ctrl_key) {
0248         nvme_auth_free_key(ctrl->ctrl_key);
0249         ctrl->ctrl_key = NULL;
0250     }
0251 }
0252 
0253 bool nvmet_check_auth_status(struct nvmet_req *req)
0254 {
0255     if (req->sq->ctrl->host_key &&
0256         !req->sq->authenticated)
0257         return false;
0258     return true;
0259 }
0260 
0261 int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
0262              unsigned int shash_len)
0263 {
0264     struct crypto_shash *shash_tfm;
0265     struct shash_desc *shash;
0266     struct nvmet_ctrl *ctrl = req->sq->ctrl;
0267     const char *hash_name;
0268     u8 *challenge = req->sq->dhchap_c1, *host_response;
0269     u8 buf[4];
0270     int ret;
0271 
0272     hash_name = nvme_auth_hmac_name(ctrl->shash_id);
0273     if (!hash_name) {
0274         pr_warn("Hash ID %d invalid\n", ctrl->shash_id);
0275         return -EINVAL;
0276     }
0277 
0278     shash_tfm = crypto_alloc_shash(hash_name, 0, 0);
0279     if (IS_ERR(shash_tfm)) {
0280         pr_err("failed to allocate shash %s\n", hash_name);
0281         return PTR_ERR(shash_tfm);
0282     }
0283 
0284     if (shash_len != crypto_shash_digestsize(shash_tfm)) {
0285         pr_debug("%s: hash len mismatch (len %d digest %d)\n",
0286              __func__, shash_len,
0287              crypto_shash_digestsize(shash_tfm));
0288         ret = -EINVAL;
0289         goto out_free_tfm;
0290     }
0291 
0292     host_response = nvme_auth_transform_key(ctrl->host_key, ctrl->hostnqn);
0293     if (IS_ERR(host_response)) {
0294         ret = PTR_ERR(host_response);
0295         goto out_free_tfm;
0296     }
0297 
0298     ret = crypto_shash_setkey(shash_tfm, host_response,
0299                   ctrl->host_key->len);
0300     if (ret)
0301         goto out_free_response;
0302 
0303     if (ctrl->dh_gid != NVME_AUTH_DHGROUP_NULL) {
0304         challenge = kmalloc(shash_len, GFP_KERNEL);
0305         if (!challenge) {
0306             ret = -ENOMEM;
0307             goto out_free_response;
0308         }
0309         ret = nvme_auth_augmented_challenge(ctrl->shash_id,
0310                             req->sq->dhchap_skey,
0311                             req->sq->dhchap_skey_len,
0312                             req->sq->dhchap_c1,
0313                             challenge, shash_len);
0314         if (ret)
0315             goto out_free_response;
0316     }
0317 
0318     pr_debug("ctrl %d qid %d host response seq %u transaction %d\n",
0319          ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1,
0320          req->sq->dhchap_tid);
0321 
0322     shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(shash_tfm),
0323             GFP_KERNEL);
0324     if (!shash) {
0325         ret = -ENOMEM;
0326         goto out_free_response;
0327     }
0328     shash->tfm = shash_tfm;
0329     ret = crypto_shash_init(shash);
0330     if (ret)
0331         goto out;
0332     ret = crypto_shash_update(shash, challenge, shash_len);
0333     if (ret)
0334         goto out;
0335     put_unaligned_le32(req->sq->dhchap_s1, buf);
0336     ret = crypto_shash_update(shash, buf, 4);
0337     if (ret)
0338         goto out;
0339     put_unaligned_le16(req->sq->dhchap_tid, buf);
0340     ret = crypto_shash_update(shash, buf, 2);
0341     if (ret)
0342         goto out;
0343     memset(buf, 0, 4);
0344     ret = crypto_shash_update(shash, buf, 1);
0345     if (ret)
0346         goto out;
0347     ret = crypto_shash_update(shash, "HostHost", 8);
0348     if (ret)
0349         goto out;
0350     ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn));
0351     if (ret)
0352         goto out;
0353     ret = crypto_shash_update(shash, buf, 1);
0354     if (ret)
0355         goto out;
0356     ret = crypto_shash_update(shash, ctrl->subsysnqn,
0357                   strlen(ctrl->subsysnqn));
0358     if (ret)
0359         goto out;
0360     ret = crypto_shash_final(shash, response);
0361 out:
0362     if (challenge != req->sq->dhchap_c1)
0363         kfree(challenge);
0364     kfree(shash);
0365 out_free_response:
0366     kfree_sensitive(host_response);
0367 out_free_tfm:
0368     crypto_free_shash(shash_tfm);
0369     return 0;
0370 }
0371 
0372 int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,
0373              unsigned int shash_len)
0374 {
0375     struct crypto_shash *shash_tfm;
0376     struct shash_desc *shash;
0377     struct nvmet_ctrl *ctrl = req->sq->ctrl;
0378     const char *hash_name;
0379     u8 *challenge = req->sq->dhchap_c2, *ctrl_response;
0380     u8 buf[4];
0381     int ret;
0382 
0383     hash_name = nvme_auth_hmac_name(ctrl->shash_id);
0384     if (!hash_name) {
0385         pr_warn("Hash ID %d invalid\n", ctrl->shash_id);
0386         return -EINVAL;
0387     }
0388 
0389     shash_tfm = crypto_alloc_shash(hash_name, 0, 0);
0390     if (IS_ERR(shash_tfm)) {
0391         pr_err("failed to allocate shash %s\n", hash_name);
0392         return PTR_ERR(shash_tfm);
0393     }
0394 
0395     if (shash_len != crypto_shash_digestsize(shash_tfm)) {
0396         pr_debug("%s: hash len mismatch (len %d digest %d)\n",
0397              __func__, shash_len,
0398              crypto_shash_digestsize(shash_tfm));
0399         ret = -EINVAL;
0400         goto out_free_tfm;
0401     }
0402 
0403     ctrl_response = nvme_auth_transform_key(ctrl->ctrl_key,
0404                         ctrl->subsysnqn);
0405     if (IS_ERR(ctrl_response)) {
0406         ret = PTR_ERR(ctrl_response);
0407         goto out_free_tfm;
0408     }
0409 
0410     ret = crypto_shash_setkey(shash_tfm, ctrl_response,
0411                   ctrl->ctrl_key->len);
0412     if (ret)
0413         goto out_free_response;
0414 
0415     if (ctrl->dh_gid != NVME_AUTH_DHGROUP_NULL) {
0416         challenge = kmalloc(shash_len, GFP_KERNEL);
0417         if (!challenge) {
0418             ret = -ENOMEM;
0419             goto out_free_response;
0420         }
0421         ret = nvme_auth_augmented_challenge(ctrl->shash_id,
0422                             req->sq->dhchap_skey,
0423                             req->sq->dhchap_skey_len,
0424                             req->sq->dhchap_c2,
0425                             challenge, shash_len);
0426         if (ret)
0427             goto out_free_response;
0428     }
0429 
0430     shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(shash_tfm),
0431             GFP_KERNEL);
0432     if (!shash) {
0433         ret = -ENOMEM;
0434         goto out_free_response;
0435     }
0436     shash->tfm = shash_tfm;
0437 
0438     ret = crypto_shash_init(shash);
0439     if (ret)
0440         goto out;
0441     ret = crypto_shash_update(shash, challenge, shash_len);
0442     if (ret)
0443         goto out;
0444     put_unaligned_le32(req->sq->dhchap_s2, buf);
0445     ret = crypto_shash_update(shash, buf, 4);
0446     if (ret)
0447         goto out;
0448     put_unaligned_le16(req->sq->dhchap_tid, buf);
0449     ret = crypto_shash_update(shash, buf, 2);
0450     if (ret)
0451         goto out;
0452     memset(buf, 0, 4);
0453     ret = crypto_shash_update(shash, buf, 1);
0454     if (ret)
0455         goto out;
0456     ret = crypto_shash_update(shash, "Controller", 10);
0457     if (ret)
0458         goto out;
0459     ret = crypto_shash_update(shash, ctrl->subsysnqn,
0460                 strlen(ctrl->subsysnqn));
0461     if (ret)
0462         goto out;
0463     ret = crypto_shash_update(shash, buf, 1);
0464     if (ret)
0465         goto out;
0466     ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn));
0467     if (ret)
0468         goto out;
0469     ret = crypto_shash_final(shash, response);
0470 out:
0471     if (challenge != req->sq->dhchap_c2)
0472         kfree(challenge);
0473     kfree(shash);
0474 out_free_response:
0475     kfree_sensitive(ctrl_response);
0476 out_free_tfm:
0477     crypto_free_shash(shash_tfm);
0478     return 0;
0479 }
0480 
0481 int nvmet_auth_ctrl_exponential(struct nvmet_req *req,
0482                 u8 *buf, int buf_size)
0483 {
0484     struct nvmet_ctrl *ctrl = req->sq->ctrl;
0485     int ret = 0;
0486 
0487     if (!ctrl->dh_key) {
0488         pr_warn("ctrl %d no DH public key!\n", ctrl->cntlid);
0489         return -ENOKEY;
0490     }
0491     if (buf_size != ctrl->dh_keysize) {
0492         pr_warn("ctrl %d DH public key size mismatch, need %zu is %d\n",
0493             ctrl->cntlid, ctrl->dh_keysize, buf_size);
0494         ret = -EINVAL;
0495     } else {
0496         memcpy(buf, ctrl->dh_key, buf_size);
0497         pr_debug("%s: ctrl %d public key %*ph\n", __func__,
0498              ctrl->cntlid, (int)buf_size, buf);
0499     }
0500 
0501     return ret;
0502 }
0503 
0504 int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
0505                 u8 *pkey, int pkey_size)
0506 {
0507     struct nvmet_ctrl *ctrl = req->sq->ctrl;
0508     int ret;
0509 
0510     req->sq->dhchap_skey_len = ctrl->dh_keysize;
0511     req->sq->dhchap_skey = kzalloc(req->sq->dhchap_skey_len, GFP_KERNEL);
0512     if (!req->sq->dhchap_skey)
0513         return -ENOMEM;
0514     ret = nvme_auth_gen_shared_secret(ctrl->dh_tfm,
0515                       pkey, pkey_size,
0516                       req->sq->dhchap_skey,
0517                       req->sq->dhchap_skey_len);
0518     if (ret)
0519         pr_debug("failed to compute shared secret, err %d\n", ret);
0520     else
0521         pr_debug("%s: shared secret %*ph\n", __func__,
0522              (int)req->sq->dhchap_skey_len,
0523              req->sq->dhchap_skey);
0524 
0525     return ret;
0526 }