xref: /spdk/lib/nvme/nvme_auth.c (revision 2d65fd75787eb0bb41e61d598e380b026090c148)
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