LCOV - code coverage report
Current view: top level - lib/nvme - nvme_auth.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 0 27 0.0 %
Date: 2024-07-14 20:07:27 Functions: 0 7 0.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: BSD-3-Clause
       2             :  * Copyright (c) 2024 Intel Corporation.  All rights reserved.
       3             :  */
       4             : 
       5             : #include "spdk/base64.h"
       6             : #include "spdk/crc32.h"
       7             : #include "spdk/endian.h"
       8             : #include "spdk/log.h"
       9             : #include "spdk/string.h"
      10             : #include "spdk/util.h"
      11             : #include "spdk_internal/nvme.h"
      12             : #include "nvme_internal.h"
      13             : 
      14             : #ifdef SPDK_CONFIG_HAVE_EVP_MAC
      15             : #include <openssl/dh.h>
      16             : #include <openssl/evp.h>
      17             : #include <openssl/param_build.h>
      18             : #include <openssl/rand.h>
      19             : #endif
      20             : 
      21             : struct nvme_auth_digest {
      22             :         uint8_t         id;
      23             :         const char      *name;
      24             :         uint8_t         len;
      25             : };
      26             : 
      27             : struct nvme_auth_dhgroup {
      28             :         uint8_t         id;
      29             :         const char      *name;
      30             : };
      31             : 
      32             : #define NVME_AUTH_DATA_SIZE                     4096
      33             : #define NVME_AUTH_DH_KEY_MAX_SIZE               1024
      34             : #define NVME_AUTH_CHAP_KEY_MAX_SIZE             256
      35             : 
      36             : #define AUTH_DEBUGLOG(q, fmt, ...) \
      37             :         SPDK_DEBUGLOG(nvme_auth, "[%s:%s:%u] " fmt, (q)->ctrlr->trid.subnqn, \
      38             :                       (q)->ctrlr->opts.hostnqn, (q)->id, ## __VA_ARGS__)
      39             : #define AUTH_ERRLOG(q, fmt, ...) \
      40             :         SPDK_ERRLOG("[%s:%s:%u] " fmt, (q)->ctrlr->trid.subnqn, (q)->ctrlr->opts.hostnqn, \
      41             :                     (q)->id, ## __VA_ARGS__)
      42             : #define AUTH_LOGDUMP(msg, buf, len) \
      43             :         SPDK_LOGDUMP(nvme_auth, msg, buf, len)
      44             : 
      45             : static const struct nvme_auth_digest g_digests[] = {
      46             :         { SPDK_NVMF_DHCHAP_HASH_SHA256, "sha256", 32 },
      47             :         { SPDK_NVMF_DHCHAP_HASH_SHA384, "sha384", 48 },
      48             :         { SPDK_NVMF_DHCHAP_HASH_SHA512, "sha512", 64 },
      49             : };
      50             : 
      51             : static const struct nvme_auth_dhgroup g_dhgroups[] = {
      52             :         { SPDK_NVMF_DHCHAP_DHGROUP_NULL, "null" },
      53             :         { SPDK_NVMF_DHCHAP_DHGROUP_2048, "ffdhe2048" },
      54             :         { SPDK_NVMF_DHCHAP_DHGROUP_3072, "ffdhe3072" },
      55             :         { SPDK_NVMF_DHCHAP_DHGROUP_4096, "ffdhe4096" },
      56             :         { SPDK_NVMF_DHCHAP_DHGROUP_6144, "ffdhe6144" },
      57             :         { SPDK_NVMF_DHCHAP_DHGROUP_8192, "ffdhe8192" },
      58             : };
      59             : 
      60             : static const struct nvme_auth_digest *
      61           0 : nvme_auth_get_digest(int id)
      62             : {
      63             :         size_t i;
      64             : 
      65           0 :         for (i = 0; i < SPDK_COUNTOF(g_digests); ++i) {
      66           0 :                 if (g_digests[i].id == id) {
      67           0 :                         return &g_digests[i];
      68             :                 }
      69             :         }
      70             : 
      71           0 :         return NULL;
      72             : }
      73             : 
      74             : int
      75           0 : spdk_nvme_dhchap_get_digest_id(const char *digest)
      76             : {
      77             :         size_t i;
      78             : 
      79           0 :         for (i = 0; i < SPDK_COUNTOF(g_digests); ++i) {
      80           0 :                 if (strcmp(g_digests[i].name, digest) == 0) {
      81           0 :                         return g_digests[i].id;
      82             :                 }
      83             :         }
      84             : 
      85           0 :         return -EINVAL;
      86             : }
      87             : 
      88             : const char *
      89           0 : spdk_nvme_dhchap_get_digest_name(int id)
      90             : {
      91           0 :         const struct nvme_auth_digest *digest = nvme_auth_get_digest(id);
      92             : 
      93           0 :         return digest != NULL ? digest->name : NULL;
      94             : }
      95             : 
      96             : int
      97           0 : spdk_nvme_dhchap_get_dhgroup_id(const char *dhgroup)
      98             : {
      99             :         size_t i;
     100             : 
     101           0 :         for (i = 0; i < SPDK_COUNTOF(g_dhgroups); ++i) {
     102           0 :                 if (strcmp(g_dhgroups[i].name, dhgroup) == 0) {
     103           0 :                         return g_dhgroups[i].id;
     104             :                 }
     105             :         }
     106             : 
     107           0 :         return -EINVAL;
     108             : }
     109             : 
     110             : const char *
     111           0 : spdk_nvme_dhchap_get_dhgroup_name(int id)
     112             : {
     113             :         size_t i;
     114             : 
     115           0 :         for (i = 0; i < SPDK_COUNTOF(g_dhgroups); ++i) {
     116           0 :                 if (g_dhgroups[i].id == id) {
     117           0 :                         return g_dhgroups[i].name;
     118             :                 }
     119             :         }
     120             : 
     121           0 :         return NULL;
     122             : }
     123             : 
     124             : uint8_t
     125           0 : spdk_nvme_dhchap_get_digest_length(int id)
     126             : {
     127           0 :         const struct nvme_auth_digest *digest = nvme_auth_get_digest(id);
     128             : 
     129           0 :         return digest != NULL ? digest->len : 0;
     130             : }
     131             : 
     132             : #ifdef SPDK_CONFIG_HAVE_EVP_MAC
     133             : static bool
     134             : nvme_auth_digest_allowed(struct spdk_nvme_qpair *qpair, uint8_t digest)
     135             : {
     136             :         struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
     137             : 
     138             :         return ctrlr->opts.dhchap_digests & SPDK_BIT(digest);
     139             : }
     140             : 
     141             : static bool
     142             : nvme_auth_dhgroup_allowed(struct spdk_nvme_qpair *qpair, uint8_t dhgroup)
     143             : {
     144             :         struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
     145             : 
     146             :         return ctrlr->opts.dhchap_dhgroups & SPDK_BIT(dhgroup);
     147             : }
     148             : 
     149             : static void
     150             : nvme_auth_set_state(struct spdk_nvme_qpair *qpair, enum nvme_qpair_auth_state state)
     151             : {
     152             :         static const char *state_names[] __attribute__((unused)) = {
     153             :                 [NVME_QPAIR_AUTH_STATE_NEGOTIATE] = "negotiate",
     154             :                 [NVME_QPAIR_AUTH_STATE_AWAIT_NEGOTIATE] = "await-negotiate",
     155             :                 [NVME_QPAIR_AUTH_STATE_AWAIT_CHALLENGE] = "await-challenge",
     156             :                 [NVME_QPAIR_AUTH_STATE_AWAIT_REPLY] = "await-reply",
     157             :                 [NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS1] = "await-success1",
     158             :                 [NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS2] = "await-success2",
     159             :                 [NVME_QPAIR_AUTH_STATE_AWAIT_FAILURE2] = "await-failure2",
     160             :                 [NVME_QPAIR_AUTH_STATE_DONE] = "done",
     161             :         };
     162             : 
     163             :         AUTH_DEBUGLOG(qpair, "auth state: %s\n", state_names[state]);
     164             :         qpair->auth.state = state;
     165             : }
     166             : 
     167             : static void
     168             : nvme_auth_set_failure(struct spdk_nvme_qpair *qpair, int status, bool failure2)
     169             : {
     170             :         if (qpair->auth.status == 0) {
     171             :                 qpair->auth.status = status;
     172             :         }
     173             : 
     174             :         nvme_auth_set_state(qpair, failure2 ?
     175             :                             NVME_QPAIR_AUTH_STATE_AWAIT_FAILURE2 :
     176             :                             NVME_QPAIR_AUTH_STATE_DONE);
     177             : }
     178             : 
     179             : static void
     180             : nvme_auth_print_cpl(struct spdk_nvme_qpair *qpair, const char *msg)
     181             : {
     182             :         struct nvme_completion_poll_status *status = qpair->poll_status;
     183             : 
     184             :         AUTH_ERRLOG(qpair, "%s failed: sc=%d, sct=%d (timed out: %s)\n", msg, status->cpl.status.sc,
     185             :                     status->cpl.status.sct, status->timed_out ? "true" : "false");
     186             : }
     187             : 
     188             : static uint32_t
     189             : nvme_auth_get_seqnum(struct spdk_nvme_qpair *qpair)
     190             : {
     191             :         struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
     192             :         uint32_t seqnum;
     193             :         int rc;
     194             : 
     195             :         nvme_ctrlr_lock(ctrlr);
     196             :         if (ctrlr->auth_seqnum == 0) {
     197             :                 rc = RAND_bytes((void *)&ctrlr->auth_seqnum, sizeof(ctrlr->auth_seqnum));
     198             :                 if (rc != 1) {
     199             :                         nvme_ctrlr_unlock(ctrlr);
     200             :                         return 0;
     201             :                 }
     202             :         }
     203             :         if (++ctrlr->auth_seqnum == 0) {
     204             :                 ctrlr->auth_seqnum = 1;
     205             :         }
     206             :         seqnum = ctrlr->auth_seqnum;
     207             :         nvme_ctrlr_unlock(ctrlr);
     208             : 
     209             :         return seqnum;
     210             : }
     211             : 
     212             : static int
     213             : nvme_auth_transform_key(struct spdk_key *key, int hash, const char *nqn,
     214             :                         const void *keyin, size_t keylen, void *out, size_t outlen)
     215             : {
     216             :         EVP_MAC *hmac = NULL;
     217             :         EVP_MAC_CTX *ctx = NULL;
     218             :         OSSL_PARAM params[2];
     219             :         int rc;
     220             : 
     221             :         switch (hash) {
     222             :         case SPDK_NVMF_DHCHAP_HASH_NONE:
     223             :                 if (keylen > outlen) {
     224             :                         SPDK_ERRLOG("Key buffer too small: %zu < %zu (key=%s)\n", outlen, keylen,
     225             :                                     spdk_key_get_name(key));
     226             :                         return -ENOBUFS;
     227             :                 }
     228             :                 memcpy(out, keyin, keylen);
     229             :                 return keylen;
     230             :         case SPDK_NVMF_DHCHAP_HASH_SHA256:
     231             :         case SPDK_NVMF_DHCHAP_HASH_SHA384:
     232             :         case SPDK_NVMF_DHCHAP_HASH_SHA512:
     233             :                 break;
     234             :         default:
     235             :                 SPDK_ERRLOG("Unsupported key hash: 0x%x (key=%s)\n", hash, spdk_key_get_name(key));
     236             :                 return -EINVAL;
     237             :         }
     238             : 
     239             :         hmac = EVP_MAC_fetch(NULL, "hmac", NULL);
     240             :         if (hmac == NULL) {
     241             :                 return -EIO;
     242             :         }
     243             :         ctx = EVP_MAC_CTX_new(hmac);
     244             :         if (ctx == NULL) {
     245             :                 rc = -EIO;
     246             :                 goto out;
     247             :         }
     248             :         params[0] = OSSL_PARAM_construct_utf8_string("digest",
     249             :                         (char *)spdk_nvme_dhchap_get_digest_name(hash), 0);
     250             :         params[1] = OSSL_PARAM_construct_end();
     251             : 
     252             :         if (EVP_MAC_init(ctx, keyin, keylen, params) != 1) {
     253             :                 rc = -EIO;
     254             :                 goto out;
     255             :         }
     256             :         if (EVP_MAC_update(ctx, nqn, strlen(nqn)) != 1) {
     257             :                 rc = -EIO;
     258             :                 goto out;
     259             :         }
     260             :         if (EVP_MAC_update(ctx, "NVMe-over-Fabrics", strlen("NVMe-over-Fabrics")) != 1) {
     261             :                 rc = -EIO;
     262             :                 goto out;
     263             :         }
     264             :         if (EVP_MAC_final(ctx, out, &outlen, outlen) != 1) {
     265             :                 rc = -EIO;
     266             :                 goto out;
     267             :         }
     268             :         rc = (int)outlen;
     269             : out:
     270             :         EVP_MAC_CTX_free(ctx);
     271             :         EVP_MAC_free(hmac);
     272             : 
     273             :         return rc;
     274             : }
     275             : 
     276             : static int
     277             : nvme_auth_get_key(struct spdk_key *key, const char *nqn, void *buf, size_t buflen)
     278             : {
     279             :         char keystr[NVME_AUTH_CHAP_KEY_MAX_SIZE + 1] = {};
     280             :         char keyb64[NVME_AUTH_CHAP_KEY_MAX_SIZE] = {};
     281             :         char *tmp, *secret;
     282             :         int rc, hash;
     283             :         size_t keylen;
     284             : 
     285             :         rc = spdk_key_get_key(key, keystr, NVME_AUTH_CHAP_KEY_MAX_SIZE);
     286             :         if (rc < 0) {
     287             :                 SPDK_ERRLOG("Failed to load key=%s: %s\n", spdk_key_get_name(key),
     288             :                             spdk_strerror(-rc));
     289             :                 goto out;
     290             :         }
     291             : 
     292             :         rc = sscanf(keystr, "DHHC-1:%02x:", &hash);
     293             :         if (rc != 1) {
     294             :                 SPDK_ERRLOG("Invalid key format (key=%s)\n", spdk_key_get_name(key));
     295             :                 rc = -EINVAL;
     296             :                 goto out;
     297             : 
     298             :         }
     299             :         /* Start at the first character after second ":" and remove the trailing ":" */
     300             :         secret = &keystr[10];
     301             :         tmp = strstr(secret, ":");
     302             :         if (!tmp) {
     303             :                 SPDK_ERRLOG("Invalid key format (key=%s)\n", spdk_key_get_name(key));
     304             :                 rc = -EINVAL;
     305             :                 goto out;
     306             :         }
     307             : 
     308             :         *tmp = '\0';
     309             :         keylen = sizeof(keyb64);
     310             :         rc = spdk_base64_decode(keyb64, &keylen, secret);
     311             :         if (rc != 0) {
     312             :                 SPDK_ERRLOG("Invalid key format (key=%s)\n", spdk_key_get_name(key));
     313             :                 rc = -EINVAL;
     314             :                 goto out;
     315             :         }
     316             :         /* Only 32B, 48B, and 64B keys are supported (+ 4B, as they're followed by a crc32) */
     317             :         if (keylen != 36 && keylen != 52 && keylen != 68) {
     318             :                 SPDK_ERRLOG("Invalid key size=%zu (key=%s)\n", keylen, spdk_key_get_name(key));
     319             :                 rc = -EINVAL;
     320             :                 goto out;
     321             :         }
     322             : 
     323             :         keylen -= 4;
     324             :         if (~spdk_crc32_ieee_update(keyb64, keylen, ~0) != from_le32(&keyb64[keylen])) {
     325             :                 SPDK_ERRLOG("Invalid key checksum (key=%s)\n", spdk_key_get_name(key));
     326             :                 rc = -EINVAL;
     327             :                 goto out;
     328             :         }
     329             : 
     330             :         rc = nvme_auth_transform_key(key, hash, nqn, keyb64, keylen, buf, buflen);
     331             : out:
     332             :         spdk_memset_s(keystr, sizeof(keystr), 0, sizeof(keystr));
     333             :         spdk_memset_s(keyb64, sizeof(keyb64), 0, sizeof(keyb64));
     334             : 
     335             :         return rc;
     336             : }
     337             : 
     338             : static int
     339             : nvme_auth_augment_challenge(const void *cval, size_t clen, const void *key, size_t keylen,
     340             :                             void *caval, size_t *calen, enum spdk_nvmf_dhchap_hash hash)
     341             : {
     342             :         EVP_MAC *hmac = NULL;
     343             :         EVP_MAC_CTX *ctx = NULL;
     344             :         EVP_MD *md = NULL;
     345             :         OSSL_PARAM params[2];
     346             :         uint8_t keydgst[NVME_AUTH_DIGEST_MAX_SIZE];
     347             :         unsigned int dgstlen = sizeof(keydgst);
     348             :         int rc = 0;
     349             : 
     350             :         /* If there's no key, there's nothing to augment, cval == caval */
     351             :         if (key == NULL) {
     352             :                 assert(clen <= *calen);
     353             :                 memcpy(caval, cval, clen);
     354             :                 *calen = clen;
     355             :                 return 0;
     356             :         }
     357             : 
     358             :         md = EVP_MD_fetch(NULL, spdk_nvme_dhchap_get_digest_name(hash), NULL);
     359             :         if (!md) {
     360             :                 SPDK_ERRLOG("Failed to fetch digest function: %d\n", hash);
     361             :                 return -EINVAL;
     362             :         }
     363             :         if (EVP_Digest(key, keylen, keydgst, &dgstlen, md, NULL) != 1) {
     364             :                 rc = -EIO;
     365             :                 goto out;
     366             :         }
     367             : 
     368             :         hmac = EVP_MAC_fetch(NULL, "hmac", NULL);
     369             :         if (hmac == NULL) {
     370             :                 rc = -EIO;
     371             :                 goto out;
     372             :         }
     373             :         ctx = EVP_MAC_CTX_new(hmac);
     374             :         if (ctx == NULL) {
     375             :                 rc = -EIO;
     376             :                 goto out;
     377             :         }
     378             :         params[0] = OSSL_PARAM_construct_utf8_string("digest",
     379             :                         (char *)spdk_nvme_dhchap_get_digest_name(hash), 0);
     380             :         params[1] = OSSL_PARAM_construct_end();
     381             : 
     382             :         if (EVP_MAC_init(ctx, keydgst, dgstlen, params) != 1) {
     383             :                 rc = -EIO;
     384             :                 goto out;
     385             :         }
     386             :         if (EVP_MAC_update(ctx, cval, clen) != 1) {
     387             :                 rc = -EIO;
     388             :                 goto out;
     389             :         }
     390             :         if (EVP_MAC_final(ctx, caval, calen, *calen) != 1) {
     391             :                 rc = -EIO;
     392             :                 goto out;
     393             :         }
     394             : out:
     395             :         EVP_MD_free(md);
     396             :         EVP_MAC_CTX_free(ctx);
     397             :         EVP_MAC_free(hmac);
     398             : 
     399             :         return rc;
     400             : }
     401             : 
     402             : int
     403             : spdk_nvme_dhchap_calculate(struct spdk_key *key, enum spdk_nvmf_dhchap_hash hash,
     404             :                            const char *type, uint32_t seq, uint16_t tid, uint8_t scc,
     405             :                            const char *nqn1, const char *nqn2, const void *dhkey, size_t dhlen,
     406             :                            const void *cval, void *rval)
     407             : {
     408             :         EVP_MAC *hmac;
     409             :         EVP_MAC_CTX *ctx;
     410             :         OSSL_PARAM params[2];
     411             :         uint8_t keybuf[NVME_AUTH_CHAP_KEY_MAX_SIZE], term = 0;
     412             :         uint8_t caval[NVME_AUTH_DATA_SIZE];
     413             :         size_t hlen, calen = sizeof(caval);
     414             :         int rc, keylen;
     415             : 
     416             :         hlen = spdk_nvme_dhchap_get_digest_length(hash);
     417             :         rc = nvme_auth_augment_challenge(cval, hlen, dhkey, dhlen, caval, &calen, hash);
     418             :         if (rc != 0) {
     419             :                 return rc;
     420             :         }
     421             : 
     422             :         hmac = EVP_MAC_fetch(NULL, "hmac", NULL);
     423             :         if (hmac == NULL) {
     424             :                 return -EIO;
     425             :         }
     426             : 
     427             :         ctx = EVP_MAC_CTX_new(hmac);
     428             :         if (ctx == NULL) {
     429             :                 rc = -EIO;
     430             :                 goto out;
     431             :         }
     432             : 
     433             :         keylen = nvme_auth_get_key(key, nqn1, keybuf, sizeof(keybuf));
     434             :         if (keylen < 0) {
     435             :                 rc = keylen;
     436             :                 goto out;
     437             :         }
     438             : 
     439             :         params[0] = OSSL_PARAM_construct_utf8_string("digest",
     440             :                         (char *)spdk_nvme_dhchap_get_digest_name(hash), 0);
     441             :         params[1] = OSSL_PARAM_construct_end();
     442             : 
     443             :         rc = -EIO;
     444             :         if (EVP_MAC_init(ctx, keybuf, (size_t)keylen, params) != 1) {
     445             :                 goto out;
     446             :         }
     447             :         if (EVP_MAC_update(ctx, caval, calen) != 1) {
     448             :                 goto out;
     449             :         }
     450             :         if (EVP_MAC_update(ctx, (void *)&seq, sizeof(seq)) != 1) {
     451             :                 goto out;
     452             :         }
     453             :         if (EVP_MAC_update(ctx, (void *)&tid, sizeof(tid)) != 1) {
     454             :                 goto out;
     455             :         }
     456             :         if (EVP_MAC_update(ctx, (void *)&scc, sizeof(scc)) != 1) {
     457             :                 goto out;
     458             :         }
     459             :         if (EVP_MAC_update(ctx, (void *)type, strlen(type)) != 1) {
     460             :                 goto out;
     461             :         }
     462             :         if (EVP_MAC_update(ctx, (void *)nqn1, strlen(nqn1)) != 1) {
     463             :                 goto out;
     464             :         }
     465             :         if (EVP_MAC_update(ctx, (void *)&term, sizeof(term)) != 1) {
     466             :                 goto out;
     467             :         }
     468             :         if (EVP_MAC_update(ctx, (void *)nqn2, strlen(nqn2)) != 1) {
     469             :                 goto out;
     470             :         }
     471             :         if (EVP_MAC_final(ctx, rval, &hlen, hlen) != 1) {
     472             :                 goto out;
     473             :         }
     474             :         rc = 0;
     475             : out:
     476             :         spdk_memset_s(keybuf, sizeof(keybuf), 0, sizeof(keybuf));
     477             :         EVP_MAC_CTX_free(ctx);
     478             :         EVP_MAC_free(hmac);
     479             : 
     480             :         return rc;
     481             : }
     482             : 
     483             : struct spdk_nvme_dhchap_dhkey *
     484             : spdk_nvme_dhchap_generate_dhkey(enum spdk_nvmf_dhchap_dhgroup dhgroup)
     485             : {
     486             :         EVP_PKEY_CTX *ctx = NULL;
     487             :         EVP_PKEY *key = NULL;
     488             :         OSSL_PARAM params[2];
     489             : 
     490             :         ctx = EVP_PKEY_CTX_new_from_name(NULL, "DHX", NULL);
     491             :         if (ctx == NULL) {
     492             :                 goto error;
     493             :         }
     494             :         if (EVP_PKEY_keygen_init(ctx) != 1) {
     495             :                 goto error;
     496             :         }
     497             : 
     498             :         params[0] = OSSL_PARAM_construct_utf8_string("group",
     499             :                         (char *)spdk_nvme_dhchap_get_dhgroup_name(dhgroup), 0);
     500             :         params[1] = OSSL_PARAM_construct_end();
     501             :         if (EVP_PKEY_CTX_set_params(ctx, params) != 1) {
     502             :                 SPDK_ERRLOG("Failed to set dhkey's dhgroup: %s\n",
     503             :                             spdk_nvme_dhchap_get_dhgroup_name(dhgroup));
     504             :                 goto error;
     505             :         }
     506             :         if (EVP_PKEY_generate(ctx, &key) != 1) {
     507             :                 goto error;
     508             :         }
     509             : error:
     510             :         EVP_PKEY_CTX_free(ctx);
     511             :         return (void *)key;
     512             : }
     513             : 
     514             : void
     515             : spdk_nvme_dhchap_dhkey_free(struct spdk_nvme_dhchap_dhkey **key)
     516             : {
     517             :         if (key == NULL) {
     518             :                 return;
     519             :         }
     520             : 
     521             :         EVP_PKEY_free(*(EVP_PKEY **)key);
     522             :         *key = NULL;
     523             : }
     524             : 
     525             : int
     526             : spdk_nvme_dhchap_dhkey_get_pubkey(struct spdk_nvme_dhchap_dhkey *dhkey, void *pub, size_t *len)
     527             : {
     528             :         EVP_PKEY *key = (EVP_PKEY *)dhkey;
     529             :         BIGNUM *bn = NULL;
     530             :         int rc;
     531             : 
     532             :         if (EVP_PKEY_get_bn_param(key, "pub", &bn) != 1) {
     533             :                 rc = -EIO;
     534             :                 goto error;
     535             :         }
     536             :         if ((size_t)BN_num_bytes(bn) > *len) {
     537             :                 SPDK_ERRLOG("Insufficient key buffer size=%zu (needed=%d)",
     538             :                             *len, BN_num_bytes(bn));
     539             :                 rc = -EINVAL;
     540             :                 goto error;
     541             :         }
     542             :         rc = BN_bn2bin(bn, pub);
     543             :         if (rc <= 0) {
     544             :                 rc = -EIO;
     545             :                 goto error;
     546             :         }
     547             : 
     548             :         *len = (size_t)BN_num_bytes(bn);
     549             :         rc = 0;
     550             : error:
     551             :         BN_free(bn);
     552             :         return rc;
     553             : }
     554             : 
     555             : static EVP_PKEY *
     556             : nvme_auth_get_peerkey(const void *peerkey, size_t len, const char *dhgroup)
     557             : {
     558             :         EVP_PKEY_CTX *ctx = NULL;
     559             :         EVP_PKEY *result = NULL, *key = NULL;
     560             :         OSSL_PARAM_BLD *bld = NULL;
     561             :         OSSL_PARAM *params = NULL;
     562             :         BIGNUM *bn = NULL;
     563             : 
     564             :         ctx = EVP_PKEY_CTX_new_from_name(NULL, "DHX", NULL);
     565             :         if (ctx == NULL) {
     566             :                 goto error;
     567             :         }
     568             :         if (EVP_PKEY_fromdata_init(ctx) != 1) {
     569             :                 goto error;
     570             :         }
     571             : 
     572             :         bn = BN_bin2bn(peerkey, len, NULL);
     573             :         if (bn == NULL) {
     574             :                 goto error;
     575             :         }
     576             : 
     577             :         bld = OSSL_PARAM_BLD_new();
     578             :         if (bld == NULL) {
     579             :                 goto error;
     580             :         }
     581             :         if (OSSL_PARAM_BLD_push_BN(bld, "pub", bn) != 1) {
     582             :                 goto error;
     583             :         }
     584             :         if (OSSL_PARAM_BLD_push_utf8_string(bld, "group", dhgroup, 0) != 1) {
     585             :                 goto error;
     586             :         }
     587             : 
     588             :         params = OSSL_PARAM_BLD_to_param(bld);
     589             :         if (params == NULL) {
     590             :                 goto error;
     591             :         }
     592             :         if (EVP_PKEY_fromdata(ctx, &key, EVP_PKEY_PUBLIC_KEY, params) != 1) {
     593             :                 SPDK_ERRLOG("Failed to create dhkey peer key\n");
     594             :                 goto error;
     595             :         }
     596             : 
     597             :         result = EVP_PKEY_dup(key);
     598             : error:
     599             :         EVP_PKEY_free(key);
     600             :         EVP_PKEY_CTX_free(ctx);
     601             :         OSSL_PARAM_BLD_free(bld);
     602             :         OSSL_PARAM_free(params);
     603             :         BN_free(bn);
     604             : 
     605             :         return result;
     606             : }
     607             : 
     608             : int
     609             : spdk_nvme_dhchap_dhkey_derive_secret(struct spdk_nvme_dhchap_dhkey *dhkey,
     610             :                                      const void *peer, size_t peerlen, void *secret, size_t *seclen)
     611             : {
     612             :         EVP_PKEY *key = (EVP_PKEY *)dhkey;
     613             :         EVP_PKEY_CTX *ctx = NULL;
     614             :         EVP_PKEY *peerkey = NULL;
     615             :         char dhgroup[64] = {};
     616             :         int rc = 0;
     617             : 
     618             :         if (EVP_PKEY_get_utf8_string_param(key, "group", dhgroup,
     619             :                                            sizeof(dhgroup), NULL) != 1) {
     620             :                 return -EIO;
     621             :         }
     622             :         peerkey = nvme_auth_get_peerkey(peer, peerlen, dhgroup);
     623             :         if (peerkey == NULL) {
     624             :                 return -EINVAL;
     625             :         }
     626             :         ctx = EVP_PKEY_CTX_new(key, NULL);
     627             :         if (ctx == NULL) {
     628             :                 rc = -ENOMEM;
     629             :                 goto out;
     630             :         }
     631             :         if (EVP_PKEY_derive_init(ctx) != 1) {
     632             :                 rc = -EIO;
     633             :                 goto out;
     634             :         }
     635             :         if (EVP_PKEY_CTX_set_dh_pad(ctx, 1) <= 0) {
     636             :                 rc = -EIO;
     637             :                 goto out;
     638             :         }
     639             :         if (EVP_PKEY_derive_set_peer(ctx, peerkey) != 1) {
     640             :                 SPDK_ERRLOG("Failed to set dhsecret's peer key\n");
     641             :                 rc = -EINVAL;
     642             :                 goto out;
     643             :         }
     644             :         if (EVP_PKEY_derive(ctx, secret, seclen) != 1) {
     645             :                 SPDK_ERRLOG("Failed to derive dhsecret\n");
     646             :                 rc = -ENOBUFS;
     647             :                 goto out;
     648             :         }
     649             : out:
     650             :         EVP_PKEY_free(peerkey);
     651             :         EVP_PKEY_CTX_free(ctx);
     652             : 
     653             :         return rc;
     654             : }
     655             : 
     656             : static int
     657             : nvme_auth_submit_request(struct spdk_nvme_qpair *qpair,
     658             :                          enum spdk_nvmf_fabric_cmd_types type, uint32_t len)
     659             : {
     660             :         struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
     661             :         struct nvme_request *req = qpair->reserved_req;
     662             :         struct nvme_completion_poll_status *status = qpair->poll_status;
     663             :         struct spdk_nvmf_fabric_auth_recv_cmd rcmd = {};
     664             :         struct spdk_nvmf_fabric_auth_send_cmd scmd = {};
     665             : 
     666             :         assert(len <= NVME_AUTH_DATA_SIZE);
     667             :         memset(&status->cpl, 0, sizeof(status->cpl));
     668             :         status->timeout_tsc = ctrlr->opts.admin_timeout_ms * spdk_get_ticks_hz() / 1000 +
     669             :                               spdk_get_ticks();
     670             :         status->done = false;
     671             :         NVME_INIT_REQUEST(req, nvme_completion_poll_cb, status,
     672             :                           NVME_PAYLOAD_CONTIG(status->dma_data, NULL), len, 0);
     673             :         switch (type) {
     674             :         case SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND:
     675             :                 scmd.opcode = SPDK_NVME_OPC_FABRIC;
     676             :                 scmd.fctype = type;
     677             :                 scmd.spsp0 = 1;
     678             :                 scmd.spsp1 = 1;
     679             :                 scmd.secp = SPDK_NVMF_AUTH_SECP_NVME;
     680             :                 scmd.tl = len;
     681             :                 memcpy(&req->cmd, &scmd, sizeof(scmd));
     682             :                 break;
     683             :         case SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_RECV:
     684             :                 rcmd.opcode = SPDK_NVME_OPC_FABRIC;
     685             :                 rcmd.fctype = type;
     686             :                 rcmd.spsp0 = 1;
     687             :                 rcmd.spsp1 = 1;
     688             :                 rcmd.secp = SPDK_NVMF_AUTH_SECP_NVME;
     689             :                 rcmd.al = len;
     690             :                 memcpy(&req->cmd, &rcmd, sizeof(rcmd));
     691             :                 break;
     692             :         default:
     693             :                 assert(0 && "invalid command");
     694             :                 return -EINVAL;
     695             :         }
     696             : 
     697             :         return nvme_qpair_submit_request(qpair, req);
     698             : }
     699             : 
     700             : static int
     701             : nvme_auth_recv_message(struct spdk_nvme_qpair *qpair)
     702             : {
     703             :         memset(qpair->poll_status->dma_data, 0, NVME_AUTH_DATA_SIZE);
     704             :         return nvme_auth_submit_request(qpair, SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_RECV,
     705             :                                         NVME_AUTH_DATA_SIZE);
     706             : }
     707             : 
     708             : static bool
     709             : nvme_auth_send_failure2(struct spdk_nvme_qpair *qpair, enum spdk_nvmf_auth_failure_reason reason)
     710             : {
     711             :         struct spdk_nvmf_auth_failure *msg = qpair->poll_status->dma_data;
     712             :         struct nvme_auth *auth = &qpair->auth;
     713             : 
     714             :         memset(qpair->poll_status->dma_data, 0, NVME_AUTH_DATA_SIZE);
     715             :         msg->auth_type = SPDK_NVMF_AUTH_TYPE_COMMON_MESSAGE;
     716             :         msg->auth_id = SPDK_NVMF_AUTH_ID_FAILURE2;
     717             :         msg->t_id = auth->tid;
     718             :         msg->rc = SPDK_NVMF_AUTH_FAILURE;
     719             :         msg->rce = reason;
     720             : 
     721             :         return nvme_auth_submit_request(qpair, SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND,
     722             :                                         sizeof(*msg)) == 0;
     723             : }
     724             : 
     725             : static int
     726             : nvme_auth_check_message(struct spdk_nvme_qpair *qpair, enum spdk_nvmf_auth_id auth_id)
     727             : {
     728             :         struct spdk_nvmf_auth_failure *msg = qpair->poll_status->dma_data;
     729             :         const char *reason = NULL;
     730             :         const char *reasons[] = {
     731             :                 [SPDK_NVMF_AUTH_FAILED] = "authentication failed",
     732             :                 [SPDK_NVMF_AUTH_PROTOCOL_UNUSABLE] = "protocol not usable",
     733             :                 [SPDK_NVMF_AUTH_SCC_MISMATCH] = "secure channel concatenation mismatch",
     734             :                 [SPDK_NVMF_AUTH_HASH_UNUSABLE] = "hash not usable",
     735             :                 [SPDK_NVMF_AUTH_DHGROUP_UNUSABLE] = "dhgroup not usable",
     736             :                 [SPDK_NVMF_AUTH_INCORRECT_PAYLOAD] = "incorrect payload",
     737             :                 [SPDK_NVMF_AUTH_INCORRECT_PROTOCOL_MESSAGE] = "incorrect protocol message",
     738             :         };
     739             : 
     740             :         switch (msg->auth_type) {
     741             :         case SPDK_NVMF_AUTH_TYPE_DHCHAP:
     742             :                 if (msg->auth_id == auth_id) {
     743             :                         return 0;
     744             :                 }
     745             :                 AUTH_ERRLOG(qpair, "received unexpected DH-HMAC-CHAP message id: %u (expected: %u)\n",
     746             :                             msg->auth_id, auth_id);
     747             :                 break;
     748             :         case SPDK_NVMF_AUTH_TYPE_COMMON_MESSAGE:
     749             :                 /* The only common message that we can expect to receive is AUTH_failure1 */
     750             :                 if (msg->auth_id != SPDK_NVMF_AUTH_ID_FAILURE1) {
     751             :                         AUTH_ERRLOG(qpair, "received unexpected common message id: %u\n",
     752             :                                     msg->auth_id);
     753             :                         break;
     754             :                 }
     755             :                 if (msg->rc == SPDK_NVMF_AUTH_FAILURE && msg->rce < SPDK_COUNTOF(reasons)) {
     756             :                         reason = reasons[msg->rce];
     757             :                 }
     758             :                 AUTH_ERRLOG(qpair, "received AUTH_failure1: rc=%d, rce=%d (%s)\n",
     759             :                             msg->rc, msg->rce, reason);
     760             :                 nvme_auth_set_failure(qpair, -EACCES, false);
     761             :                 return -EACCES;
     762             :         default:
     763             :                 AUTH_ERRLOG(qpair, "received unknown message type: %u\n", msg->auth_type);
     764             :                 break;
     765             :         }
     766             : 
     767             :         nvme_auth_set_failure(qpair, -EACCES,
     768             :                               nvme_auth_send_failure2(qpair,
     769             :                                               SPDK_NVMF_AUTH_INCORRECT_PROTOCOL_MESSAGE));
     770             :         return -EACCES;
     771             : }
     772             : 
     773             : static int
     774             : nvme_auth_send_negotiate(struct spdk_nvme_qpair *qpair)
     775             : {
     776             :         struct nvme_auth *auth = &qpair->auth;
     777             :         struct spdk_nvmf_auth_negotiate *msg = qpair->poll_status->dma_data;
     778             :         struct spdk_nvmf_auth_descriptor *desc = msg->descriptors;
     779             :         size_t i;
     780             : 
     781             :         memset(qpair->poll_status->dma_data, 0, NVME_AUTH_DATA_SIZE);
     782             :         desc->auth_id = SPDK_NVMF_AUTH_TYPE_DHCHAP;
     783             :         assert(SPDK_COUNTOF(g_digests) <= sizeof(desc->hash_id_list));
     784             :         assert(SPDK_COUNTOF(g_dhgroups) <= sizeof(desc->dhg_id_list));
     785             : 
     786             :         for (i = 0; i < SPDK_COUNTOF(g_digests); ++i) {
     787             :                 if (!nvme_auth_digest_allowed(qpair, g_digests[i].id)) {
     788             :                         continue;
     789             :                 }
     790             :                 AUTH_DEBUGLOG(qpair, "digest: %u (%s)\n", g_digests[i].id,
     791             :                               spdk_nvme_dhchap_get_digest_name(g_digests[i].id));
     792             :                 desc->hash_id_list[desc->halen++] = g_digests[i].id;
     793             :         }
     794             :         for (i = 0; i < SPDK_COUNTOF(g_dhgroups); ++i) {
     795             :                 if (!nvme_auth_dhgroup_allowed(qpair, g_dhgroups[i].id)) {
     796             :                         continue;
     797             :                 }
     798             :                 AUTH_DEBUGLOG(qpair, "dhgroup: %u (%s)\n", g_dhgroups[i].id,
     799             :                               spdk_nvme_dhchap_get_dhgroup_name(g_dhgroups[i].id));
     800             :                 desc->dhg_id_list[desc->dhlen++] = g_dhgroups[i].id;
     801             :         }
     802             : 
     803             :         msg->auth_type = SPDK_NVMF_AUTH_TYPE_COMMON_MESSAGE;
     804             :         msg->auth_id = SPDK_NVMF_AUTH_ID_NEGOTIATE;
     805             :         msg->t_id = auth->tid;
     806             :         msg->sc_c = SPDK_NVMF_AUTH_SCC_DISABLED;
     807             :         msg->napd = 1;
     808             : 
     809             :         return nvme_auth_submit_request(qpair, SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND,
     810             :                                         sizeof(*msg) + msg->napd * sizeof(*desc));
     811             : }
     812             : 
     813             : static int
     814             : nvme_auth_check_challenge(struct spdk_nvme_qpair *qpair)
     815             : {
     816             :         struct spdk_nvmf_dhchap_challenge *challenge = qpair->poll_status->dma_data;
     817             :         struct nvme_auth *auth = &qpair->auth;
     818             :         uint8_t hl;
     819             :         int rc;
     820             : 
     821             :         rc = nvme_auth_check_message(qpair, SPDK_NVMF_AUTH_ID_DHCHAP_CHALLENGE);
     822             :         if (rc != 0) {
     823             :                 return rc;
     824             :         }
     825             : 
     826             :         if (challenge->t_id != auth->tid) {
     827             :                 AUTH_ERRLOG(qpair, "unexpected tid: received=%u, expected=%u\n",
     828             :                             challenge->t_id, auth->tid);
     829             :                 goto error;
     830             :         }
     831             : 
     832             :         if (challenge->seqnum == 0) {
     833             :                 AUTH_ERRLOG(qpair, "received challenge with seqnum=0\n");
     834             :                 goto error;
     835             :         }
     836             : 
     837             :         hl = spdk_nvme_dhchap_get_digest_length(challenge->hash_id);
     838             :         if (hl == 0) {
     839             :                 AUTH_ERRLOG(qpair, "unsupported hash function: 0x%x\n", challenge->hash_id);
     840             :                 goto error;
     841             :         }
     842             : 
     843             :         if (challenge->hl != hl) {
     844             :                 AUTH_ERRLOG(qpair, "unexpected hash length: received=%u, expected=%u\n",
     845             :                             challenge->hl, hl);
     846             :                 goto error;
     847             :         }
     848             : 
     849             :         switch (challenge->dhg_id) {
     850             :         case SPDK_NVMF_DHCHAP_DHGROUP_NULL:
     851             :                 if (challenge->dhvlen != 0) {
     852             :                         AUTH_ERRLOG(qpair, "unexpected dhvlen=%u for dhgroup 0\n",
     853             :                                     challenge->dhvlen);
     854             :                         goto error;
     855             :                 }
     856             :                 break;
     857             :         case SPDK_NVMF_DHCHAP_DHGROUP_2048:
     858             :         case SPDK_NVMF_DHCHAP_DHGROUP_3072:
     859             :         case SPDK_NVMF_DHCHAP_DHGROUP_4096:
     860             :         case SPDK_NVMF_DHCHAP_DHGROUP_6144:
     861             :         case SPDK_NVMF_DHCHAP_DHGROUP_8192:
     862             :                 if (sizeof(*challenge) + hl + challenge->dhvlen > NVME_AUTH_DATA_SIZE ||
     863             :                     challenge->dhvlen == 0) {
     864             :                         AUTH_ERRLOG(qpair, "invalid dhvlen=%u for dhgroup %u\n",
     865             :                                     challenge->dhvlen, challenge->dhg_id);
     866             :                         goto error;
     867             :                 }
     868             :                 break;
     869             :         default:
     870             :                 AUTH_ERRLOG(qpair, "unsupported dhgroup: 0x%x\n", challenge->dhg_id);
     871             :                 goto error;
     872             :         }
     873             : 
     874             :         if (!nvme_auth_digest_allowed(qpair, challenge->hash_id)) {
     875             :                 AUTH_ERRLOG(qpair, "received disallowed digest: %u (%s)\n", challenge->hash_id,
     876             :                             spdk_nvme_dhchap_get_digest_name(challenge->hash_id));
     877             :                 goto error;
     878             :         }
     879             : 
     880             :         if (!nvme_auth_dhgroup_allowed(qpair, challenge->dhg_id)) {
     881             :                 AUTH_ERRLOG(qpair, "received disallowed dhgroup: %u (%s)\n", challenge->dhg_id,
     882             :                             spdk_nvme_dhchap_get_dhgroup_name(challenge->dhg_id));
     883             :                 goto error;
     884             :         }
     885             : 
     886             :         return 0;
     887             : error:
     888             :         nvme_auth_set_failure(qpair, -EACCES,
     889             :                               nvme_auth_send_failure2(qpair, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD));
     890             :         return -EACCES;
     891             : }
     892             : 
     893             : static int
     894             : nvme_auth_send_reply(struct spdk_nvme_qpair *qpair)
     895             : {
     896             :         struct nvme_completion_poll_status *status = qpair->poll_status;
     897             :         struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
     898             :         struct spdk_nvmf_dhchap_challenge *challenge = status->dma_data;
     899             :         struct spdk_nvmf_dhchap_reply *reply = status->dma_data;
     900             :         struct nvme_auth *auth = &qpair->auth;
     901             :         struct spdk_nvme_dhchap_dhkey *dhkey;
     902             :         uint8_t hl, response[NVME_AUTH_DATA_SIZE];
     903             :         uint8_t pubkey[NVME_AUTH_DH_KEY_MAX_SIZE];
     904             :         uint8_t dhsec[NVME_AUTH_DH_KEY_MAX_SIZE];
     905             :         uint8_t ctrlr_challenge[NVME_AUTH_DIGEST_MAX_SIZE] = {};
     906             :         size_t dhseclen = 0, publen = 0;
     907             :         uint32_t seqnum = 0;
     908             :         int rc;
     909             : 
     910             :         auth->hash = challenge->hash_id;
     911             :         hl = spdk_nvme_dhchap_get_digest_length(challenge->hash_id);
     912             :         if (challenge->dhg_id != SPDK_NVMF_DHCHAP_DHGROUP_NULL) {
     913             :                 dhseclen = sizeof(dhsec);
     914             :                 publen = sizeof(pubkey);
     915             :                 AUTH_LOGDUMP("ctrlr pubkey:", &challenge->cval[hl], challenge->dhvlen);
     916             :                 dhkey = spdk_nvme_dhchap_generate_dhkey(
     917             :                                 (enum spdk_nvmf_dhchap_dhgroup)challenge->dhg_id);
     918             :                 if (dhkey == NULL) {
     919             :                         return -EINVAL;
     920             :                 }
     921             :                 rc = spdk_nvme_dhchap_dhkey_get_pubkey(dhkey, pubkey, &publen);
     922             :                 if (rc != 0) {
     923             :                         spdk_nvme_dhchap_dhkey_free(&dhkey);
     924             :                         return rc;
     925             :                 }
     926             :                 AUTH_LOGDUMP("host pubkey:", pubkey, publen);
     927             :                 rc = spdk_nvme_dhchap_dhkey_derive_secret(dhkey,
     928             :                                 &challenge->cval[hl], challenge->dhvlen, dhsec, &dhseclen);
     929             :                 spdk_nvme_dhchap_dhkey_free(&dhkey);
     930             :                 if (rc != 0) {
     931             :                         return rc;
     932             :                 }
     933             : 
     934             :                 AUTH_LOGDUMP("dh secret:", dhsec, dhseclen);
     935             :         }
     936             : 
     937             :         AUTH_DEBUGLOG(qpair, "key=%s, hash=%u, dhgroup=%u, seq=%u, tid=%u, subnqn=%s, hostnqn=%s, "
     938             :                       "len=%u\n", spdk_key_get_name(ctrlr->opts.dhchap_key),
     939             :                       challenge->hash_id, challenge->dhg_id, challenge->seqnum, auth->tid,
     940             :                       ctrlr->trid.subnqn, ctrlr->opts.hostnqn, hl);
     941             :         rc = spdk_nvme_dhchap_calculate(ctrlr->opts.dhchap_key,
     942             :                                         (enum spdk_nvmf_dhchap_hash)challenge->hash_id,
     943             :                                         "HostHost", challenge->seqnum, auth->tid, 0,
     944             :                                         ctrlr->opts.hostnqn, ctrlr->trid.subnqn,
     945             :                                         dhseclen > 0 ? dhsec : NULL, dhseclen,
     946             :                                         challenge->cval, response);
     947             :         if (rc != 0) {
     948             :                 AUTH_ERRLOG(qpair, "failed to calculate response: %s\n", spdk_strerror(-rc));
     949             :                 return rc;
     950             :         }
     951             : 
     952             :         if (ctrlr->opts.dhchap_ctrlr_key != NULL) {
     953             :                 seqnum = nvme_auth_get_seqnum(qpair);
     954             :                 if (seqnum == 0) {
     955             :                         return -EIO;
     956             :                 }
     957             : 
     958             :                 assert(sizeof(ctrlr_challenge) >= hl);
     959             :                 rc = RAND_bytes(ctrlr_challenge, hl);
     960             :                 if (rc != 1) {
     961             :                         return -EIO;
     962             :                 }
     963             : 
     964             :                 rc = spdk_nvme_dhchap_calculate(ctrlr->opts.dhchap_ctrlr_key,
     965             :                                                 (enum spdk_nvmf_dhchap_hash)challenge->hash_id,
     966             :                                                 "Controller", seqnum, auth->tid, 0,
     967             :                                                 ctrlr->trid.subnqn, ctrlr->opts.hostnqn,
     968             :                                                 dhseclen > 0 ? dhsec : NULL, dhseclen,
     969             :                                                 ctrlr_challenge, auth->challenge);
     970             :                 if (rc != 0) {
     971             :                         AUTH_ERRLOG(qpair, "failed to calculate controller's response: %s\n",
     972             :                                     spdk_strerror(-rc));
     973             :                         return rc;
     974             :                 }
     975             :         }
     976             : 
     977             :         /* Now that the response has been calculated, send the reply */
     978             :         memset(qpair->poll_status->dma_data, 0, NVME_AUTH_DATA_SIZE);
     979             :         assert(sizeof(*reply) + 2 * hl + publen <= NVME_AUTH_DATA_SIZE);
     980             :         memcpy(reply->rval, response, hl);
     981             :         memcpy(&reply->rval[1 * hl], ctrlr_challenge, hl);
     982             :         memcpy(&reply->rval[2 * hl], pubkey, publen);
     983             : 
     984             :         reply->auth_type = SPDK_NVMF_AUTH_TYPE_DHCHAP;
     985             :         reply->auth_id = SPDK_NVMF_AUTH_ID_DHCHAP_REPLY;
     986             :         reply->t_id = auth->tid;
     987             :         reply->hl = hl;
     988             :         reply->cvalid = ctrlr->opts.dhchap_ctrlr_key != NULL;
     989             :         reply->dhvlen = publen;
     990             :         reply->seqnum = seqnum;
     991             : 
     992             :         /* The 2 * reply->hl below is because the spec says that both rval[hl] and cval[hl] must
     993             :          * always be part of the reply message, even cvalid is zero.
     994             :          */
     995             :         return nvme_auth_submit_request(qpair, SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND,
     996             :                                         sizeof(*reply) + 2 * reply->hl + publen);
     997             : }
     998             : 
     999             : static int
    1000             : nvme_auth_check_success1(struct spdk_nvme_qpair *qpair)
    1001             : {
    1002             :         struct spdk_nvmf_dhchap_success1 *msg = qpair->poll_status->dma_data;
    1003             :         struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
    1004             :         struct nvme_auth *auth = &qpair->auth;
    1005             :         uint8_t hl;
    1006             :         int rc, status;
    1007             : 
    1008             :         rc = nvme_auth_check_message(qpair, SPDK_NVMF_AUTH_ID_DHCHAP_SUCCESS1);
    1009             :         if (rc != 0) {
    1010             :                 return rc;
    1011             :         }
    1012             : 
    1013             :         if (msg->t_id != auth->tid) {
    1014             :                 AUTH_ERRLOG(qpair, "unexpected tid: received=%u, expected=%u\n",
    1015             :                             msg->t_id, auth->tid);
    1016             :                 status = SPDK_NVMF_AUTH_INCORRECT_PAYLOAD;
    1017             :                 goto error;
    1018             :         }
    1019             : 
    1020             :         if (ctrlr->opts.dhchap_ctrlr_key != NULL) {
    1021             :                 if (!msg->rvalid) {
    1022             :                         AUTH_ERRLOG(qpair, "received rvalid=0, expected response\n");
    1023             :                         status = SPDK_NVMF_AUTH_INCORRECT_PAYLOAD;
    1024             :                         goto error;
    1025             :                 }
    1026             : 
    1027             :                 hl = spdk_nvme_dhchap_get_digest_length(auth->hash);
    1028             :                 if (msg->hl != hl) {
    1029             :                         AUTH_ERRLOG(qpair, "received invalid hl=%u, expected=%u\n", msg->hl, hl);
    1030             :                         status = SPDK_NVMF_AUTH_INCORRECT_PAYLOAD;
    1031             :                         goto error;
    1032             :                 }
    1033             : 
    1034             :                 if (memcmp(msg->rval, auth->challenge, hl) != 0) {
    1035             :                         AUTH_ERRLOG(qpair, "controller challenge mismatch\n");
    1036             :                         AUTH_LOGDUMP("received:", msg->rval, hl);
    1037             :                         AUTH_LOGDUMP("expected:", auth->challenge, hl);
    1038             :                         status = SPDK_NVMF_AUTH_FAILED;
    1039             :                         goto error;
    1040             :                 }
    1041             :         }
    1042             : 
    1043             :         return 0;
    1044             : error:
    1045             :         nvme_auth_set_failure(qpair, -EACCES, nvme_auth_send_failure2(qpair, status));
    1046             : 
    1047             :         return -EACCES;
    1048             : }
    1049             : 
    1050             : static int
    1051             : nvme_auth_send_success2(struct spdk_nvme_qpair *qpair)
    1052             : {
    1053             :         struct spdk_nvmf_dhchap_success2 *msg = qpair->poll_status->dma_data;
    1054             :         struct nvme_auth *auth = &qpair->auth;
    1055             : 
    1056             :         memset(qpair->poll_status->dma_data, 0, NVME_AUTH_DATA_SIZE);
    1057             :         msg->auth_type = SPDK_NVMF_AUTH_TYPE_DHCHAP;
    1058             :         msg->auth_id = SPDK_NVMF_AUTH_ID_DHCHAP_SUCCESS2;
    1059             :         msg->t_id = auth->tid;
    1060             : 
    1061             :         return nvme_auth_submit_request(qpair, SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND,
    1062             :                                         sizeof(*msg));
    1063             : }
    1064             : 
    1065             : int
    1066             : nvme_fabric_qpair_authenticate_poll(struct spdk_nvme_qpair *qpair)
    1067             : {
    1068             :         struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
    1069             :         struct nvme_auth *auth = &qpair->auth;
    1070             :         struct nvme_completion_poll_status *status = qpair->poll_status;
    1071             :         enum nvme_qpair_auth_state prev_state;
    1072             :         int rc;
    1073             : 
    1074             :         do {
    1075             :                 prev_state = auth->state;
    1076             : 
    1077             :                 switch (auth->state) {
    1078             :                 case NVME_QPAIR_AUTH_STATE_NEGOTIATE:
    1079             :                         rc = nvme_auth_send_negotiate(qpair);
    1080             :                         if (rc != 0) {
    1081             :                                 nvme_auth_set_failure(qpair, rc, false);
    1082             :                                 AUTH_ERRLOG(qpair, "failed to send AUTH_negotiate: %s\n",
    1083             :                                             spdk_strerror(-rc));
    1084             :                                 break;
    1085             :                         }
    1086             :                         nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_AWAIT_NEGOTIATE);
    1087             :                         break;
    1088             :                 case NVME_QPAIR_AUTH_STATE_AWAIT_NEGOTIATE:
    1089             :                         rc = nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL);
    1090             :                         if (rc != 0) {
    1091             :                                 if (rc != -EAGAIN) {
    1092             :                                         nvme_auth_print_cpl(qpair, "AUTH_negotiate");
    1093             :                                         nvme_auth_set_failure(qpair, rc, false);
    1094             :                                 }
    1095             :                                 break;
    1096             :                         }
    1097             :                         /* Negotiate has been sent, try to receive the challenge */
    1098             :                         rc = nvme_auth_recv_message(qpair);
    1099             :                         if (rc != 0) {
    1100             :                                 nvme_auth_set_failure(qpair, rc, false);
    1101             :                                 AUTH_ERRLOG(qpair, "failed to recv DH-HMAC-CHAP_challenge: %s\n",
    1102             :                                             spdk_strerror(-rc));
    1103             :                                 break;
    1104             :                         }
    1105             :                         nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_AWAIT_CHALLENGE);
    1106             :                         break;
    1107             :                 case NVME_QPAIR_AUTH_STATE_AWAIT_CHALLENGE:
    1108             :                         rc = nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL);
    1109             :                         if (rc != 0) {
    1110             :                                 if (rc != -EAGAIN) {
    1111             :                                         nvme_auth_print_cpl(qpair, "DH-HMAC-CHAP_challenge");
    1112             :                                         nvme_auth_set_failure(qpair, rc, false);
    1113             :                                 }
    1114             :                                 break;
    1115             :                         }
    1116             :                         rc = nvme_auth_check_challenge(qpair);
    1117             :                         if (rc != 0) {
    1118             :                                 break;
    1119             :                         }
    1120             :                         rc = nvme_auth_send_reply(qpair);
    1121             :                         if (rc != 0) {
    1122             :                                 nvme_auth_set_failure(qpair, rc, false);
    1123             :                                 AUTH_ERRLOG(qpair, "failed to send DH-HMAC-CHAP_reply: %s\n",
    1124             :                                             spdk_strerror(-rc));
    1125             :                                 break;
    1126             :                         }
    1127             :                         nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_AWAIT_REPLY);
    1128             :                         break;
    1129             :                 case NVME_QPAIR_AUTH_STATE_AWAIT_REPLY:
    1130             :                         rc = nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL);
    1131             :                         if (rc != 0) {
    1132             :                                 if (rc != -EAGAIN) {
    1133             :                                         nvme_auth_print_cpl(qpair, "DH-HMAC-CHAP_reply");
    1134             :                                         nvme_auth_set_failure(qpair, rc, false);
    1135             :                                 }
    1136             :                                 break;
    1137             :                         }
    1138             :                         /* Reply has been sent, try to receive response */
    1139             :                         rc = nvme_auth_recv_message(qpair);
    1140             :                         if (rc != 0) {
    1141             :                                 nvme_auth_set_failure(qpair, rc, false);
    1142             :                                 AUTH_ERRLOG(qpair, "failed to recv DH-HMAC-CHAP_success1: %s\n",
    1143             :                                             spdk_strerror(-rc));
    1144             :                                 break;
    1145             :                         }
    1146             :                         nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS1);
    1147             :                         break;
    1148             :                 case NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS1:
    1149             :                         rc = nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL);
    1150             :                         if (rc != 0) {
    1151             :                                 if (rc != -EAGAIN) {
    1152             :                                         nvme_auth_print_cpl(qpair, "DH-HMAC-CHAP_success1");
    1153             :                                         nvme_auth_set_failure(qpair, rc, false);
    1154             :                                 }
    1155             :                                 break;
    1156             :                         }
    1157             :                         rc = nvme_auth_check_success1(qpair);
    1158             :                         if (rc != 0) {
    1159             :                                 break;
    1160             :                         }
    1161             :                         AUTH_DEBUGLOG(qpair, "authentication completed successfully\n");
    1162             :                         if (ctrlr->opts.dhchap_ctrlr_key != NULL) {
    1163             :                                 rc = nvme_auth_send_success2(qpair);
    1164             :                                 if (rc != 0) {
    1165             :                                         AUTH_ERRLOG(qpair, "failed to send DH-HMAC-CHAP_success2: "
    1166             :                                                     "%s\n", spdk_strerror(rc));
    1167             :                                         nvme_auth_set_failure(qpair, rc, false);
    1168             :                                         break;
    1169             :                                 }
    1170             :                                 nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS2);
    1171             :                                 break;
    1172             :                         }
    1173             :                         nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_DONE);
    1174             :                         break;
    1175             :                 case NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS2:
    1176             :                 case NVME_QPAIR_AUTH_STATE_AWAIT_FAILURE2:
    1177             :                         rc = nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL);
    1178             :                         if (rc == -EAGAIN) {
    1179             :                                 break;
    1180             :                         }
    1181             :                         nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_DONE);
    1182             :                         break;
    1183             :                 case NVME_QPAIR_AUTH_STATE_DONE:
    1184             :                         if (qpair->poll_status != NULL && !status->timed_out) {
    1185             :                                 qpair->poll_status = NULL;
    1186             :                                 spdk_free(status->dma_data);
    1187             :                                 free(status);
    1188             :                         }
    1189             :                         return auth->status;
    1190             :                 default:
    1191             :                         assert(0 && "invalid state");
    1192             :                         return -EINVAL;
    1193             :                 }
    1194             :         } while (auth->state != prev_state);
    1195             : 
    1196             :         return -EAGAIN;
    1197             : }
    1198             : 
    1199             : int
    1200             : nvme_fabric_qpair_authenticate_async(struct spdk_nvme_qpair *qpair)
    1201             : {
    1202             :         struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
    1203             :         struct nvme_completion_poll_status *status;
    1204             :         struct nvme_auth *auth = &qpair->auth;
    1205             :         int rc;
    1206             : 
    1207             :         if (ctrlr->opts.dhchap_key == NULL) {
    1208             :                 AUTH_ERRLOG(qpair, "missing DH-HMAC-CHAP key\n");
    1209             :                 return -ENOKEY;
    1210             :         }
    1211             : 
    1212             :         if (qpair->auth.flags & NVME_QPAIR_AUTH_FLAG_ASCR) {
    1213             :                 AUTH_ERRLOG(qpair, "secure channel concatentation is not supported\n");
    1214             :                 return -EINVAL;
    1215             :         }
    1216             : 
    1217             :         status = calloc(1, sizeof(*qpair->poll_status));
    1218             :         if (!status) {
    1219             :                 AUTH_ERRLOG(qpair, "failed to allocate poll status\n");
    1220             :                 return -ENOMEM;
    1221             :         }
    1222             : 
    1223             :         status->dma_data = spdk_zmalloc(NVME_AUTH_DATA_SIZE, 0, NULL, SPDK_ENV_LCORE_ID_ANY,
    1224             :                                         SPDK_MALLOC_DMA);
    1225             :         if (!status->dma_data) {
    1226             :                 AUTH_ERRLOG(qpair, "failed to allocate poll status\n");
    1227             :                 free(status);
    1228             :                 return -ENOMEM;
    1229             :         }
    1230             : 
    1231             :         assert(qpair->poll_status == NULL);
    1232             :         qpair->poll_status = status;
    1233             : 
    1234             :         nvme_ctrlr_lock(ctrlr);
    1235             :         auth->tid = ctrlr->auth_tid++;
    1236             :         nvme_ctrlr_unlock(ctrlr);
    1237             : 
    1238             :         nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_NEGOTIATE);
    1239             : 
    1240             :         /* Do the initial poll to kick-start the state machine */
    1241             :         rc = nvme_fabric_qpair_authenticate_poll(qpair);
    1242             :         return rc != -EAGAIN ? rc : 0;
    1243             : }
    1244             : #endif /* SPDK_CONFIG_EVP_MAC */
    1245             : 
    1246           0 : SPDK_LOG_REGISTER_COMPONENT(nvme_auth)

Generated by: LCOV version 1.15