Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/nvme/target/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 15 kB image not shown  

Quelle  auth.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * NVMe over Fabrics DH-HMAC-CHAP authentication.
 * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions.
 * All rights reserved.
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <crypto/hash.h>
#include <linux/crc32.h>
#include <linux/base64.h>
#include <linux/ctype.h>
#include <linux/random.h>
#include <linux/nvme-auth.h>
#include <linux/nvme-keyring.h>
#include <linux/unaligned.h>

#include "nvmet.h"

int nvmet_auth_set_key(struct nvmet_host *host, const char *secret,
         bool set_ctrl)
{
 unsigned char key_hash;
 char *dhchap_secret;

 if (!strlen(secret)) {
  if (set_ctrl) {
   kfree(host->dhchap_ctrl_secret);
   host->dhchap_ctrl_secret = NULL;
   host->dhchap_ctrl_key_hash = 0;
  } else {
   kfree(host->dhchap_secret);
   host->dhchap_secret = NULL;
   host->dhchap_key_hash = 0;
  }
  return 0;
 }
 if (sscanf(secret, "DHHC-1:%hhd:%*s", &key_hash) != 1)
  return -EINVAL;
 if (key_hash > 3) {
  pr_warn("Invalid DH-HMAC-CHAP hash id %d\n",
    key_hash);
  return -EINVAL;
 }
 if (key_hash > 0) {
  /* Validate selected hash algorithm */
  const char *hmac = nvme_auth_hmac_name(key_hash);

  if (!crypto_has_shash(hmac, 0, 0)) {
   pr_err("DH-HMAC-CHAP hash %s unsupported\n", hmac);
   return -ENOTSUPP;
  }
 }
 dhchap_secret = kstrdup(secret, GFP_KERNEL);
 if (!dhchap_secret)
  return -ENOMEM;
 down_write(&nvmet_config_sem);
 if (set_ctrl) {
  kfree(host->dhchap_ctrl_secret);
  host->dhchap_ctrl_secret = strim(dhchap_secret);
  host->dhchap_ctrl_key_hash = key_hash;
 } else {
  kfree(host->dhchap_secret);
  host->dhchap_secret = strim(dhchap_secret);
  host->dhchap_key_hash = key_hash;
 }
 up_write(&nvmet_config_sem);
 return 0;
}

int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, u8 dhgroup_id)
{
 const char *dhgroup_kpp;
 int ret = 0;

 pr_debug("%s: ctrl %d selecting dhgroup %d\n",
   __func__, ctrl->cntlid, dhgroup_id);

 if (ctrl->dh_tfm) {
  if (ctrl->dh_gid == dhgroup_id) {
   pr_debug("%s: ctrl %d reuse existing DH group %d\n",
     __func__, ctrl->cntlid, dhgroup_id);
   return 0;
  }
  crypto_free_kpp(ctrl->dh_tfm);
  ctrl->dh_tfm = NULL;
  ctrl->dh_gid = 0;
 }

 if (dhgroup_id == NVME_AUTH_DHGROUP_NULL)
  return 0;

 dhgroup_kpp = nvme_auth_dhgroup_kpp(dhgroup_id);
 if (!dhgroup_kpp) {
  pr_debug("%s: ctrl %d invalid DH group %d\n",
    __func__, ctrl->cntlid, dhgroup_id);
  return -EINVAL;
 }
 ctrl->dh_tfm = crypto_alloc_kpp(dhgroup_kpp, 0, 0);
 if (IS_ERR(ctrl->dh_tfm)) {
  pr_debug("%s: ctrl %d failed to setup DH group %d, err %ld\n",
    __func__, ctrl->cntlid, dhgroup_id,
    PTR_ERR(ctrl->dh_tfm));
  ret = PTR_ERR(ctrl->dh_tfm);
  ctrl->dh_tfm = NULL;
  ctrl->dh_gid = 0;
 } else {
  ctrl->dh_gid = dhgroup_id;
  pr_debug("%s: ctrl %d setup DH group %d\n",
    __func__, ctrl->cntlid, ctrl->dh_gid);
  ret = nvme_auth_gen_privkey(ctrl->dh_tfm, ctrl->dh_gid);
  if (ret < 0) {
   pr_debug("%s: ctrl %d failed to generate private key, err %d\n",
     __func__, ctrl->cntlid, ret);
   kfree_sensitive(ctrl->dh_key);
   ctrl->dh_key = NULL;
   return ret;
  }
  ctrl->dh_keysize = crypto_kpp_maxsize(ctrl->dh_tfm);
  kfree_sensitive(ctrl->dh_key);
  ctrl->dh_key = kzalloc(ctrl->dh_keysize, GFP_KERNEL);
  if (!ctrl->dh_key) {
   pr_warn("ctrl %d failed to allocate public key\n",
    ctrl->cntlid);
   return -ENOMEM;
  }
  ret = nvme_auth_gen_pubkey(ctrl->dh_tfm, ctrl->dh_key,
        ctrl->dh_keysize);
  if (ret < 0) {
   pr_warn("ctrl %d failed to generate public key\n",
    ctrl->cntlid);
   kfree(ctrl->dh_key);
   ctrl->dh_key = NULL;
  }
 }

 return ret;
}

u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq)
{
 int ret = 0;
 struct nvmet_host_link *p;
 struct nvmet_host *host = NULL;

 down_read(&nvmet_config_sem);
 if (nvmet_is_disc_subsys(ctrl->subsys))
  goto out_unlock;

 if (ctrl->subsys->allow_any_host)
  goto out_unlock;

 list_for_each_entry(p, &ctrl->subsys->hosts, entry) {
  pr_debug("check %s\n", nvmet_host_name(p->host));
  if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn))
   continue;
  host = p->host;
  break;
 }
 if (!host) {
  pr_debug("host %s not found\n", ctrl->hostnqn);
  ret = NVME_AUTH_DHCHAP_FAILURE_FAILED;
  goto out_unlock;
 }

 if (nvmet_queue_tls_keyid(sq)) {
  pr_debug("host %s tls enabled\n", ctrl->hostnqn);
  goto out_unlock;
 }

 ret = nvmet_setup_dhgroup(ctrl, host->dhchap_dhgroup_id);
 if (ret < 0) {
  pr_warn("Failed to setup DH group");
  ret = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
  goto out_unlock;
 }

 if (!host->dhchap_secret) {
  pr_debug("No authentication provided\n");
  goto out_unlock;
 }

 if (host->dhchap_hash_id == ctrl->shash_id) {
  pr_debug("Re-use existing hash ID %d\n",
    ctrl->shash_id);
 } else {
  ctrl->shash_id = host->dhchap_hash_id;
 }

 /* Skip the 'DHHC-1:XX:' prefix */
 nvme_auth_free_key(ctrl->host_key);
 ctrl->host_key = nvme_auth_extract_key(host->dhchap_secret + 10,
            host->dhchap_key_hash);
 if (IS_ERR(ctrl->host_key)) {
  ret = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
  ctrl->host_key = NULL;
  goto out_free_hash;
 }
 pr_debug("%s: using hash %s key %*ph\n", __func__,
   ctrl->host_key->hash > 0 ?
   nvme_auth_hmac_name(ctrl->host_key->hash) : "none",
   (int)ctrl->host_key->len, ctrl->host_key->key);

 nvme_auth_free_key(ctrl->ctrl_key);
 if (!host->dhchap_ctrl_secret) {
  ctrl->ctrl_key = NULL;
  goto out_unlock;
 }

 ctrl->ctrl_key = nvme_auth_extract_key(host->dhchap_ctrl_secret + 10,
            host->dhchap_ctrl_key_hash);
 if (IS_ERR(ctrl->ctrl_key)) {
  ret = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
  ctrl->ctrl_key = NULL;
  goto out_free_hash;
 }
 pr_debug("%s: using ctrl hash %s key %*ph\n", __func__,
   ctrl->ctrl_key->hash > 0 ?
   nvme_auth_hmac_name(ctrl->ctrl_key->hash) : "none",
   (int)ctrl->ctrl_key->len, ctrl->ctrl_key->key);

out_free_hash:
 if (ret) {
  if (ctrl->host_key) {
   nvme_auth_free_key(ctrl->host_key);
   ctrl->host_key = NULL;
  }
  ctrl->shash_id = 0;
 }
out_unlock:
 up_read(&nvmet_config_sem);

 return ret;
}

void nvmet_auth_sq_free(struct nvmet_sq *sq)
{
 cancel_delayed_work(&sq->auth_expired_work);
#ifdef CONFIG_NVME_TARGET_TCP_TLS
 sq->tls_key = NULL;
#endif
 kfree(sq->dhchap_c1);
 sq->dhchap_c1 = NULL;
 kfree(sq->dhchap_c2);
 sq->dhchap_c2 = NULL;
 kfree(sq->dhchap_skey);
 sq->dhchap_skey = NULL;
}

void nvmet_destroy_auth(struct nvmet_ctrl *ctrl)
{
 ctrl->shash_id = 0;

 if (ctrl->dh_tfm) {
  crypto_free_kpp(ctrl->dh_tfm);
  ctrl->dh_tfm = NULL;
  ctrl->dh_gid = 0;
 }
 kfree_sensitive(ctrl->dh_key);
 ctrl->dh_key = NULL;

 if (ctrl->host_key) {
  nvme_auth_free_key(ctrl->host_key);
  ctrl->host_key = NULL;
 }
 if (ctrl->ctrl_key) {
  nvme_auth_free_key(ctrl->ctrl_key);
  ctrl->ctrl_key = NULL;
 }
#ifdef CONFIG_NVME_TARGET_TCP_TLS
 if (ctrl->tls_key) {
  key_put(ctrl->tls_key);
  ctrl->tls_key = NULL;
 }
#endif
}

bool nvmet_check_auth_status(struct nvmet_req *req)
{
 if (req->sq->ctrl->host_key) {
  if (req->sq->qid > 0)
   return true;
  if (!req->sq->authenticated)
   return false;
 }
 return true;
}

int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
    unsigned int shash_len)
{
 struct crypto_shash *shash_tfm;
 SHASH_DESC_ON_STACK(shash, shash_tfm);
 struct nvmet_ctrl *ctrl = req->sq->ctrl;
 const char *hash_name;
 u8 *challenge = req->sq->dhchap_c1;
 struct nvme_dhchap_key *transformed_key;
 u8 buf[4], sc_c = ctrl->concat ? 1 : 0;
 int ret;

 hash_name = nvme_auth_hmac_name(ctrl->shash_id);
 if (!hash_name) {
  pr_warn("Hash ID %d invalid\n", ctrl->shash_id);
  return -EINVAL;
 }

 shash_tfm = crypto_alloc_shash(hash_name, 0, 0);
 if (IS_ERR(shash_tfm)) {
  pr_err("failed to allocate shash %s\n", hash_name);
  return PTR_ERR(shash_tfm);
 }

 if (shash_len != crypto_shash_digestsize(shash_tfm)) {
  pr_err("%s: hash len mismatch (len %d digest %d)\n",
   __func__, shash_len,
   crypto_shash_digestsize(shash_tfm));
  ret = -EINVAL;
  goto out_free_tfm;
 }

 transformed_key = nvme_auth_transform_key(ctrl->host_key,
        ctrl->hostnqn);
 if (IS_ERR(transformed_key)) {
  ret = PTR_ERR(transformed_key);
  goto out_free_tfm;
 }

 ret = crypto_shash_setkey(shash_tfm, transformed_key->key,
      transformed_key->len);
 if (ret)
  goto out_free_response;

 if (ctrl->dh_gid != NVME_AUTH_DHGROUP_NULL) {
  challenge = kmalloc(shash_len, GFP_KERNEL);
  if (!challenge) {
   ret = -ENOMEM;
   goto out_free_response;
  }
  ret = nvme_auth_augmented_challenge(ctrl->shash_id,
          req->sq->dhchap_skey,
          req->sq->dhchap_skey_len,
          req->sq->dhchap_c1,
          challenge, shash_len);
  if (ret)
   goto out;
 }

 pr_debug("ctrl %d qid %d host response seq %u transaction %d\n",
   ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1,
   req->sq->dhchap_tid);

 shash->tfm = shash_tfm;
 ret = crypto_shash_init(shash);
 if (ret)
  goto out;
 ret = crypto_shash_update(shash, challenge, shash_len);
 if (ret)
  goto out;
 put_unaligned_le32(req->sq->dhchap_s1, buf);
 ret = crypto_shash_update(shash, buf, 4);
 if (ret)
  goto out;
 put_unaligned_le16(req->sq->dhchap_tid, buf);
 ret = crypto_shash_update(shash, buf, 2);
 if (ret)
  goto out;
 *buf = sc_c;
 ret = crypto_shash_update(shash, buf, 1);
 if (ret)
  goto out;
 ret = crypto_shash_update(shash, "HostHost", 8);
 if (ret)
  goto out;
 memset(buf, 0, 4);
 ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn));
 if (ret)
  goto out;
 ret = crypto_shash_update(shash, buf, 1);
 if (ret)
  goto out;
 ret = crypto_shash_update(shash, ctrl->subsysnqn,
      strlen(ctrl->subsysnqn));
 if (ret)
  goto out;
 ret = crypto_shash_final(shash, response);
out:
 if (challenge != req->sq->dhchap_c1)
  kfree(challenge);
out_free_response:
 nvme_auth_free_key(transformed_key);
out_free_tfm:
 crypto_free_shash(shash_tfm);
 return ret;
}

int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,
    unsigned int shash_len)
{
 struct crypto_shash *shash_tfm;
 struct shash_desc *shash;
 struct nvmet_ctrl *ctrl = req->sq->ctrl;
 const char *hash_name;
 u8 *challenge = req->sq->dhchap_c2;
 struct nvme_dhchap_key *transformed_key;
 u8 buf[4];
 int ret;

 hash_name = nvme_auth_hmac_name(ctrl->shash_id);
 if (!hash_name) {
  pr_warn("Hash ID %d invalid\n", ctrl->shash_id);
  return -EINVAL;
 }

 shash_tfm = crypto_alloc_shash(hash_name, 0, 0);
 if (IS_ERR(shash_tfm)) {
  pr_err("failed to allocate shash %s\n", hash_name);
  return PTR_ERR(shash_tfm);
 }

 if (shash_len != crypto_shash_digestsize(shash_tfm)) {
  pr_debug("%s: hash len mismatch (len %d digest %d)\n",
    __func__, shash_len,
    crypto_shash_digestsize(shash_tfm));
  ret = -EINVAL;
  goto out_free_tfm;
 }

 transformed_key = nvme_auth_transform_key(ctrl->ctrl_key,
      ctrl->subsysnqn);
 if (IS_ERR(transformed_key)) {
  ret = PTR_ERR(transformed_key);
  goto out_free_tfm;
 }

 ret = crypto_shash_setkey(shash_tfm, transformed_key->key,
      transformed_key->len);
 if (ret)
  goto out_free_response;

 if (ctrl->dh_gid != NVME_AUTH_DHGROUP_NULL) {
  challenge = kmalloc(shash_len, GFP_KERNEL);
  if (!challenge) {
   ret = -ENOMEM;
   goto out_free_response;
  }
  ret = nvme_auth_augmented_challenge(ctrl->shash_id,
          req->sq->dhchap_skey,
          req->sq->dhchap_skey_len,
          req->sq->dhchap_c2,
          challenge, shash_len);
  if (ret)
   goto out_free_challenge;
 }

 shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(shash_tfm),
   GFP_KERNEL);
 if (!shash) {
  ret = -ENOMEM;
  goto out_free_challenge;
 }
 shash->tfm = shash_tfm;

 ret = crypto_shash_init(shash);
 if (ret)
  goto out;
 ret = crypto_shash_update(shash, challenge, shash_len);
 if (ret)
  goto out;
 put_unaligned_le32(req->sq->dhchap_s2, buf);
 ret = crypto_shash_update(shash, buf, 4);
 if (ret)
  goto out;
 put_unaligned_le16(req->sq->dhchap_tid, buf);
 ret = crypto_shash_update(shash, buf, 2);
 if (ret)
  goto out;
 memset(buf, 0, 4);
 ret = crypto_shash_update(shash, buf, 1);
 if (ret)
  goto out;
 ret = crypto_shash_update(shash, "Controller", 10);
 if (ret)
  goto out;
 ret = crypto_shash_update(shash, ctrl->subsysnqn,
       strlen(ctrl->subsysnqn));
 if (ret)
  goto out;
 ret = crypto_shash_update(shash, buf, 1);
 if (ret)
  goto out;
 ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn));
 if (ret)
  goto out;
 ret = crypto_shash_final(shash, response);
out:
 kfree(shash);
out_free_challenge:
 if (challenge != req->sq->dhchap_c2)
  kfree(challenge);
out_free_response:
 nvme_auth_free_key(transformed_key);
out_free_tfm:
 crypto_free_shash(shash_tfm);
 return ret;
}

int nvmet_auth_ctrl_exponential(struct nvmet_req *req,
    u8 *buf, int buf_size)
{
 struct nvmet_ctrl *ctrl = req->sq->ctrl;
 int ret = 0;

 if (!ctrl->dh_key) {
  pr_warn("ctrl %d no DH public key!\n", ctrl->cntlid);
  return -ENOKEY;
 }
 if (buf_size != ctrl->dh_keysize) {
  pr_warn("ctrl %d DH public key size mismatch, need %zu is %d\n",
   ctrl->cntlid, ctrl->dh_keysize, buf_size);
  ret = -EINVAL;
 } else {
  memcpy(buf, ctrl->dh_key, buf_size);
  pr_debug("%s: ctrl %d public key %*ph\n", __func__,
    ctrl->cntlid, (int)buf_size, buf);
 }

 return ret;
}

int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
       u8 *pkey, int pkey_size)
{
 struct nvmet_ctrl *ctrl = req->sq->ctrl;
 int ret;

 req->sq->dhchap_skey_len = ctrl->dh_keysize;
 req->sq->dhchap_skey = kzalloc(req->sq->dhchap_skey_len, GFP_KERNEL);
 if (!req->sq->dhchap_skey)
  return -ENOMEM;
 ret = nvme_auth_gen_shared_secret(ctrl->dh_tfm,
       pkey, pkey_size,
       req->sq->dhchap_skey,
       req->sq->dhchap_skey_len);
 if (ret)
  pr_debug("failed to compute shared secret, err %d\n", ret);
 else
  pr_debug("%s: shared secret %*ph\n", __func__,
    (int)req->sq->dhchap_skey_len,
    req->sq->dhchap_skey);

 return ret;
}

void nvmet_auth_insert_psk(struct nvmet_sq *sq)
{
 int hash_len = nvme_auth_hmac_hash_len(sq->ctrl->shash_id);
 u8 *psk, *digest, *tls_psk;
 size_t psk_len;
 int ret;
#ifdef CONFIG_NVME_TARGET_TCP_TLS
 struct key *tls_key = NULL;
#endif

 ret = nvme_auth_generate_psk(sq->ctrl->shash_id,
         sq->dhchap_skey,
         sq->dhchap_skey_len,
         sq->dhchap_c1, sq->dhchap_c2,
         hash_len, &psk, &psk_len);
 if (ret) {
  pr_warn("%s: ctrl %d qid %d failed to generate PSK, error %d\n",
   __func__, sq->ctrl->cntlid, sq->qid, ret);
  return;
 }
 ret = nvme_auth_generate_digest(sq->ctrl->shash_id, psk, psk_len,
     sq->ctrl->subsysnqn,
     sq->ctrl->hostnqn, &digest);
 if (ret) {
  pr_warn("%s: ctrl %d qid %d failed to generate digest, error %d\n",
   __func__, sq->ctrl->cntlid, sq->qid, ret);
  goto out_free_psk;
 }
 ret = nvme_auth_derive_tls_psk(sq->ctrl->shash_id, psk, psk_len,
           digest, &tls_psk);
 if (ret) {
  pr_warn("%s: ctrl %d qid %d failed to derive TLS PSK, error %d\n",
   __func__, sq->ctrl->cntlid, sq->qid, ret);
  goto out_free_digest;
 }
#ifdef CONFIG_NVME_TARGET_TCP_TLS
 tls_key = nvme_tls_psk_refresh(NULL, sq->ctrl->hostnqn, sq->ctrl->subsysnqn,
           sq->ctrl->shash_id, tls_psk, psk_len, digest);
 if (IS_ERR(tls_key)) {
  pr_warn("%s: ctrl %d qid %d failed to refresh key, error %ld\n",
   __func__, sq->ctrl->cntlid, sq->qid, PTR_ERR(tls_key));
  tls_key = NULL;
 }
 if (sq->ctrl->tls_key)
  key_put(sq->ctrl->tls_key);
 sq->ctrl->tls_key = tls_key;
#endif
 kfree_sensitive(tls_psk);
out_free_digest:
 kfree_sensitive(digest);
out_free_psk:
 kfree_sensitive(psk);
}

Messung V0.5
C=95 H=91 G=92

¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.