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