xref: /spdk/lib/nvme/nvme_auth.c (revision 58a284326f11a5810b3767a2dcc32c859e36bde9)
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 #include <openssl/evp.h>
13 
14 #define NVME_AUTH_DATA_SIZE		4096
15 #define NVME_AUTH_CHAP_KEY_MAX_SIZE	256
16 
17 #define AUTH_DEBUGLOG(q, fmt, ...) \
18 	SPDK_DEBUGLOG(nvme_auth, "[%s:%s:%u] " fmt, (q)->ctrlr->trid.subnqn, \
19 		      (q)->ctrlr->opts.hostnqn, (q)->id, ## __VA_ARGS__)
20 #define AUTH_ERRLOG(q, fmt, ...) \
21 	SPDK_ERRLOG("[%s:%s:%u] " fmt, (q)->ctrlr->trid.subnqn, (q)->ctrlr->opts.hostnqn, \
22 		    (q)->id, ## __VA_ARGS__)
23 
24 static const char *
25 nvme_auth_get_digest_name(uint8_t id)
26 {
27 	const char *names[] = {
28 		[SPDK_NVMF_DHCHAP_HASH_SHA256] = "sha256",
29 		[SPDK_NVMF_DHCHAP_HASH_SHA384] = "sha384",
30 		[SPDK_NVMF_DHCHAP_HASH_SHA512] = "sha512",
31 	};
32 
33 	if (id >= SPDK_COUNTOF(names)) {
34 		return NULL;
35 	}
36 
37 	return names[id];
38 }
39 
40 static uint8_t
41 nvme_auth_get_digest_len(uint8_t id)
42 {
43 	uint8_t hlen[] = {
44 		[SPDK_NVMF_DHCHAP_HASH_SHA256] = 32,
45 		[SPDK_NVMF_DHCHAP_HASH_SHA384] = 48,
46 		[SPDK_NVMF_DHCHAP_HASH_SHA512] = 64,
47 	};
48 
49 	if (id >= SPDK_COUNTOF(hlen)) {
50 		return 0;
51 	}
52 
53 	return hlen[id];
54 }
55 
56 static void
57 nvme_auth_set_state(struct spdk_nvme_qpair *qpair, enum nvme_qpair_auth_state state)
58 {
59 	static const char *state_names[] __attribute__((unused)) = {
60 		[NVME_QPAIR_AUTH_STATE_NEGOTIATE] = "negotiate",
61 		[NVME_QPAIR_AUTH_STATE_AWAIT_NEGOTIATE] = "await-negotiate",
62 		[NVME_QPAIR_AUTH_STATE_AWAIT_CHALLENGE] = "await-challenge",
63 		[NVME_QPAIR_AUTH_STATE_AWAIT_REPLY] = "await-reply",
64 		[NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS1] = "await-success1",
65 		[NVME_QPAIR_AUTH_STATE_AWAIT_FAILURE2] = "await-failure2",
66 		[NVME_QPAIR_AUTH_STATE_DONE] = "done",
67 	};
68 
69 	AUTH_DEBUGLOG(qpair, "auth state: %s\n", state_names[state]);
70 	qpair->auth.state = state;
71 }
72 
73 static void
74 nvme_auth_set_failure(struct spdk_nvme_qpair *qpair, int status, bool failure2)
75 {
76 	if (qpair->auth.status == 0) {
77 		qpair->auth.status = status;
78 	}
79 
80 	nvme_auth_set_state(qpair, failure2 ?
81 			    NVME_QPAIR_AUTH_STATE_AWAIT_FAILURE2 :
82 			    NVME_QPAIR_AUTH_STATE_DONE);
83 }
84 
85 static void
86 nvme_auth_print_cpl(struct spdk_nvme_qpair *qpair, const char *msg)
87 {
88 	struct nvme_completion_poll_status *status = qpair->poll_status;
89 
90 	AUTH_ERRLOG(qpair, "%s failed: sc=%d, sct=%d (timed out: %s)\n", msg, status->cpl.status.sc,
91 		    status->cpl.status.sct, status->timed_out ? "true" : "false");
92 }
93 
94 static int
95 nvme_auth_transform_key(struct spdk_key *key, int hash, const void *keyin, size_t keylen,
96 			void *out, size_t outlen)
97 {
98 	switch (hash) {
99 	case SPDK_NVMF_DHCHAP_HASH_NONE:
100 		if (keylen > outlen) {
101 			SPDK_ERRLOG("Key buffer too small: %zu < %zu (key=%s)\n", outlen, keylen,
102 				    spdk_key_get_name(key));
103 			return -ENOBUFS;
104 		}
105 		memcpy(out, keyin, keylen);
106 		return keylen;
107 	case SPDK_NVMF_DHCHAP_HASH_SHA256:
108 	case SPDK_NVMF_DHCHAP_HASH_SHA384:
109 	case SPDK_NVMF_DHCHAP_HASH_SHA512:
110 		SPDK_ERRLOG("Key transformation is not supported\n");
111 		return -EINVAL;
112 	default:
113 		SPDK_ERRLOG("Unsupported key hash: 0x%x (key=%s)\n", hash, spdk_key_get_name(key));
114 		return -EINVAL;
115 	}
116 }
117 
118 static int
119 nvme_auth_get_key(struct spdk_key *key, void *buf, size_t buflen)
120 {
121 	char keystr[NVME_AUTH_CHAP_KEY_MAX_SIZE + 1] = {};
122 	char keyb64[NVME_AUTH_CHAP_KEY_MAX_SIZE] = {};
123 	char *tmp, *secret;
124 	int rc, hash;
125 	size_t keylen;
126 
127 	rc = spdk_key_get_key(key, keystr, NVME_AUTH_CHAP_KEY_MAX_SIZE);
128 	if (rc < 0) {
129 		SPDK_ERRLOG("Failed to load key=%s: %s\n", spdk_key_get_name(key),
130 			    spdk_strerror(-rc));
131 		goto out;
132 	}
133 
134 	rc = sscanf(keystr, "DHHC-1:%02x:", &hash);
135 	if (rc != 1) {
136 		SPDK_ERRLOG("Invalid key format (key=%s)\n", spdk_key_get_name(key));
137 		rc = -EINVAL;
138 		goto out;
139 
140 	}
141 	/* Start at the first character after second ":" and remove the trailing ":" */
142 	secret = &keystr[10];
143 	tmp = strstr(secret, ":");
144 	if (!tmp) {
145 		SPDK_ERRLOG("Invalid key format (key=%s)\n", spdk_key_get_name(key));
146 		rc = -EINVAL;
147 		goto out;
148 	}
149 
150 	*tmp = '\0';
151 	keylen = sizeof(keyb64);
152 	rc = spdk_base64_decode(keyb64, &keylen, secret);
153 	if (rc != 0) {
154 		SPDK_ERRLOG("Invalid key format (key=%s)\n", spdk_key_get_name(key));
155 		rc = -EINVAL;
156 		goto out;
157 	}
158 	/* Only 32B, 48B, and 64B keys are supported (+ 4B, as they're followed by a crc32) */
159 	if (keylen != 36 && keylen != 52 && keylen != 68) {
160 		SPDK_ERRLOG("Invalid key size=%zu (key=%s)\n", keylen, spdk_key_get_name(key));
161 		rc = -EINVAL;
162 		goto out;
163 	}
164 
165 	keylen -= 4;
166 	if (~spdk_crc32_ieee_update(keyb64, keylen, ~0) != from_le32(&keyb64[keylen])) {
167 		SPDK_ERRLOG("Invalid key checksum (key=%s)\n", spdk_key_get_name(key));
168 		rc = -EINVAL;
169 		goto out;
170 	}
171 
172 	rc = nvme_auth_transform_key(key, hash, keyb64, keylen, buf, buflen);
173 out:
174 	spdk_memset_s(keystr, sizeof(keystr), 0, sizeof(keystr));
175 	spdk_memset_s(keyb64, sizeof(keyb64), 0, sizeof(keyb64));
176 
177 	return rc;
178 }
179 
180 static int
181 nvme_auth_calc_response(struct spdk_key *key, enum spdk_nvmf_dhchap_hash hash,
182 			const char *type, uint32_t seq, uint16_t tid, uint8_t scc,
183 			const char *nqn1, const char *nqn2, const void *dhkey, size_t dhlen,
184 			const void *cval, void *rval)
185 {
186 	EVP_MAC *hmac;
187 	EVP_MAC_CTX *ctx;
188 	OSSL_PARAM params[2];
189 	uint8_t keybuf[NVME_AUTH_CHAP_KEY_MAX_SIZE], term = 0;
190 	size_t hlen;
191 	int rc, keylen;
192 
193 	assert(dhkey == NULL && dhlen == 0);
194 	hmac = EVP_MAC_fetch(NULL, "hmac", NULL);
195 	if (hmac == NULL) {
196 		return -EIO;
197 	}
198 
199 	ctx = EVP_MAC_CTX_new(hmac);
200 	if (ctx == NULL) {
201 		rc = -EIO;
202 		goto out;
203 	}
204 
205 	keylen = nvme_auth_get_key(key, keybuf, sizeof(keybuf));
206 	if (keylen < 0) {
207 		rc = keylen;
208 		goto out;
209 	}
210 
211 	hlen = nvme_auth_get_digest_len(hash);
212 	params[0] = OSSL_PARAM_construct_utf8_string("digest",
213 			(char *)nvme_auth_get_digest_name(hash), 0);
214 	params[1] = OSSL_PARAM_construct_end();
215 
216 	rc = -EIO;
217 	if (EVP_MAC_init(ctx, keybuf, (size_t)keylen, params) != 1) {
218 		goto out;
219 	}
220 	if (EVP_MAC_update(ctx, cval, hlen) != 1) {
221 		goto out;
222 	}
223 	if (EVP_MAC_update(ctx, (void *)&seq, sizeof(seq)) != 1) {
224 		goto out;
225 	}
226 	if (EVP_MAC_update(ctx, (void *)&tid, sizeof(tid)) != 1) {
227 		goto out;
228 	}
229 	if (EVP_MAC_update(ctx, (void *)&scc, sizeof(scc)) != 1) {
230 		goto out;
231 	}
232 	if (EVP_MAC_update(ctx, (void *)type, strlen(type)) != 1) {
233 		goto out;
234 	}
235 	if (EVP_MAC_update(ctx, (void *)nqn1, strlen(nqn1)) != 1) {
236 		goto out;
237 	}
238 	if (EVP_MAC_update(ctx, (void *)&term, sizeof(term)) != 1) {
239 		goto out;
240 	}
241 	if (EVP_MAC_update(ctx, (void *)nqn2, strlen(nqn2)) != 1) {
242 		goto out;
243 	}
244 	if (EVP_MAC_final(ctx, rval, &hlen, hlen) != 1) {
245 		goto out;
246 	}
247 	rc = 0;
248 out:
249 	spdk_memset_s(keybuf, sizeof(keybuf), 0, sizeof(keybuf));
250 	EVP_MAC_CTX_free(ctx);
251 	EVP_MAC_free(hmac);
252 
253 	return rc;
254 }
255 
256 static int
257 nvme_auth_submit_request(struct spdk_nvme_qpair *qpair,
258 			 enum spdk_nvmf_fabric_cmd_types type, uint32_t len)
259 {
260 	struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
261 	struct nvme_request *req = qpair->reserved_req;
262 	struct nvme_completion_poll_status *status = qpair->poll_status;
263 	struct spdk_nvmf_fabric_auth_recv_cmd rcmd = {};
264 	struct spdk_nvmf_fabric_auth_send_cmd scmd = {};
265 
266 	assert(len <= NVME_AUTH_DATA_SIZE);
267 	memset(&status->cpl, 0, sizeof(status->cpl));
268 	status->timeout_tsc = ctrlr->opts.admin_timeout_ms * spdk_get_ticks_hz() / 1000 +
269 			      spdk_get_ticks();
270 	status->done = false;
271 	NVME_INIT_REQUEST(req, nvme_completion_poll_cb, status,
272 			  NVME_PAYLOAD_CONTIG(status->dma_data, NULL), len, 0);
273 	switch (type) {
274 	case SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND:
275 		scmd.opcode = SPDK_NVME_OPC_FABRIC;
276 		scmd.fctype = type;
277 		scmd.spsp0 = 1;
278 		scmd.spsp1 = 1;
279 		scmd.secp = SPDK_NVMF_AUTH_SECP_NVME;
280 		scmd.tl = len;
281 		memcpy(&req->cmd, &scmd, sizeof(scmd));
282 		break;
283 	case SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_RECV:
284 		rcmd.opcode = SPDK_NVME_OPC_FABRIC;
285 		rcmd.fctype = type;
286 		rcmd.spsp0 = 1;
287 		rcmd.spsp1 = 1;
288 		rcmd.secp = SPDK_NVMF_AUTH_SECP_NVME;
289 		rcmd.al = len;
290 		memcpy(&req->cmd, &rcmd, sizeof(rcmd));
291 		break;
292 	default:
293 		assert(0 && "invalid command");
294 		return -EINVAL;
295 	}
296 
297 	return nvme_qpair_submit_request(qpair, req);
298 }
299 
300 static int
301 nvme_auth_recv_message(struct spdk_nvme_qpair *qpair)
302 {
303 	memset(qpair->poll_status->dma_data, 0, NVME_AUTH_DATA_SIZE);
304 	return nvme_auth_submit_request(qpair, SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_RECV,
305 					NVME_AUTH_DATA_SIZE);
306 }
307 
308 static bool
309 nvme_auth_send_failure2(struct spdk_nvme_qpair *qpair, enum spdk_nvmf_auth_failure_reason reason)
310 {
311 	struct spdk_nvmf_auth_failure *msg = qpair->poll_status->dma_data;
312 	struct nvme_auth *auth = &qpair->auth;
313 
314 	memset(qpair->poll_status->dma_data, 0, NVME_AUTH_DATA_SIZE);
315 	msg->auth_type = SPDK_NVMF_AUTH_TYPE_COMMON_MESSAGE;
316 	msg->auth_id = SPDK_NVMF_AUTH_ID_FAILURE2;
317 	msg->t_id = auth->tid;
318 	msg->rc = SPDK_NVMF_AUTH_FAILURE;
319 	msg->rce = reason;
320 
321 	return nvme_auth_submit_request(qpair, SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND,
322 					sizeof(*msg)) == 0;
323 }
324 
325 static int
326 nvme_auth_check_message(struct spdk_nvme_qpair *qpair, enum spdk_nvmf_auth_id auth_id)
327 {
328 	struct spdk_nvmf_auth_failure *msg = qpair->poll_status->dma_data;
329 	const char *reason = NULL;
330 	const char *reasons[] = {
331 		[SPDK_NVMF_AUTH_FAILED] = "authentication failed",
332 		[SPDK_NVMF_AUTH_PROTOCOL_UNUSABLE] = "protocol not usable",
333 		[SPDK_NVMF_AUTH_SCC_MISMATCH] = "secure channel concatenation mismatch",
334 		[SPDK_NVMF_AUTH_HASH_UNUSABLE] = "hash not usable",
335 		[SPDK_NVMF_AUTH_DHGROUP_UNUSABLE] = "dhgroup not usable",
336 		[SPDK_NVMF_AUTH_INCORRECT_PAYLOAD] = "incorrect payload",
337 		[SPDK_NVMF_AUTH_INCORRECT_PROTOCOL_MESSAGE] = "incorrect protocol message",
338 	};
339 
340 	switch (msg->auth_type) {
341 	case SPDK_NVMF_AUTH_TYPE_DHCHAP:
342 		if (msg->auth_id == auth_id) {
343 			return 0;
344 		}
345 		AUTH_ERRLOG(qpair, "received unexpected DH-HMAC-CHAP message id: %u (expected: %u)\n",
346 			    msg->auth_id, auth_id);
347 		break;
348 	case SPDK_NVMF_AUTH_TYPE_COMMON_MESSAGE:
349 		/* The only common message that we can expect to receive is AUTH_failure1 */
350 		if (msg->auth_id != SPDK_NVMF_AUTH_ID_FAILURE1) {
351 			AUTH_ERRLOG(qpair, "received unexpected common message id: %u\n",
352 				    msg->auth_id);
353 			break;
354 		}
355 		if (msg->rc == SPDK_NVMF_AUTH_FAILURE && msg->rce < SPDK_COUNTOF(reasons)) {
356 			reason = reasons[msg->rce];
357 		}
358 		AUTH_ERRLOG(qpair, "received AUTH_failure1: rc=%d, rce=%d (%s)\n",
359 			    msg->rc, msg->rce, reason);
360 		nvme_auth_set_failure(qpair, -EACCES, false);
361 		return -EACCES;
362 	default:
363 		AUTH_ERRLOG(qpair, "received unknown message type: %u\n", msg->auth_type);
364 		break;
365 	}
366 
367 	nvme_auth_set_failure(qpair, -EACCES,
368 			      nvme_auth_send_failure2(qpair,
369 					      SPDK_NVMF_AUTH_INCORRECT_PROTOCOL_MESSAGE));
370 	return -EACCES;
371 }
372 
373 static int
374 nvme_auth_send_negotiate(struct spdk_nvme_qpair *qpair)
375 {
376 	struct nvme_auth *auth = &qpair->auth;
377 	struct spdk_nvmf_auth_negotiate *msg = qpair->poll_status->dma_data;
378 	struct spdk_nvmf_auth_descriptor *desc = msg->descriptors;
379 	uint8_t hashids[] = {
380 		SPDK_NVMF_DHCHAP_HASH_SHA256,
381 		SPDK_NVMF_DHCHAP_HASH_SHA384,
382 		SPDK_NVMF_DHCHAP_HASH_SHA512,
383 	};
384 	uint8_t dhgids[] = {
385 		SPDK_NVMF_DHCHAP_DHGROUP_NULL,
386 	};
387 
388 	memset(qpair->poll_status->dma_data, 0, NVME_AUTH_DATA_SIZE);
389 	desc->auth_id = SPDK_NVMF_AUTH_TYPE_DHCHAP;
390 	desc->halen = SPDK_COUNTOF(hashids);
391 	desc->dhlen = SPDK_COUNTOF(dhgids);
392 
393 	assert(desc->halen <= sizeof(desc->hash_id_list));
394 	assert(desc->dhlen <= sizeof(desc->dhg_id_list));
395 	memcpy(desc->hash_id_list, hashids, desc->halen);
396 	memcpy(desc->dhg_id_list, dhgids, desc->dhlen);
397 
398 	msg->auth_type = SPDK_NVMF_AUTH_TYPE_COMMON_MESSAGE;
399 	msg->auth_id = SPDK_NVMF_AUTH_ID_NEGOTIATE;
400 	msg->t_id = auth->tid;
401 	msg->sc_c = SPDK_NVMF_AUTH_SCC_DISABLED;
402 	msg->napd = 1;
403 
404 	return nvme_auth_submit_request(qpair, SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND,
405 					sizeof(*msg) + msg->napd * sizeof(*desc));
406 }
407 
408 static int
409 nvme_auth_check_challenge(struct spdk_nvme_qpair *qpair)
410 {
411 	struct spdk_nvmf_dhchap_challenge *challenge = qpair->poll_status->dma_data;
412 	struct nvme_auth *auth = &qpair->auth;
413 	uint8_t hl;
414 	int rc;
415 
416 	rc = nvme_auth_check_message(qpair, SPDK_NVMF_AUTH_ID_DHCHAP_CHALLENGE);
417 	if (rc != 0) {
418 		return rc;
419 	}
420 
421 	if (challenge->t_id != auth->tid) {
422 		AUTH_ERRLOG(qpair, "unexpected tid: received=%u, expected=%u\n",
423 			    challenge->t_id, auth->tid);
424 		goto error;
425 	}
426 
427 	if (challenge->seqnum == 0) {
428 		AUTH_ERRLOG(qpair, "received challenge with seqnum=0\n");
429 		goto error;
430 	}
431 
432 	hl = nvme_auth_get_digest_len(challenge->hash_id);
433 	if (hl == 0) {
434 		AUTH_ERRLOG(qpair, "unsupported hash function: 0x%x\n", challenge->hash_id);
435 		goto error;
436 	}
437 
438 	if (challenge->hl != hl) {
439 		AUTH_ERRLOG(qpair, "unexpected hash length: received=%u, expected=%u\n",
440 			    challenge->hl, hl);
441 		goto error;
442 	}
443 
444 	if (challenge->dhg_id != SPDK_NVMF_DHCHAP_DHGROUP_NULL) {
445 		AUTH_ERRLOG(qpair, "unsupported dhgroup: 0x%x\n", challenge->dhg_id);
446 		goto error;
447 	}
448 
449 	return 0;
450 error:
451 	nvme_auth_set_failure(qpair, -EACCES,
452 			      nvme_auth_send_failure2(qpair, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD));
453 	return -EACCES;
454 }
455 
456 static int
457 nvme_auth_send_reply(struct spdk_nvme_qpair *qpair)
458 {
459 	struct nvme_completion_poll_status *status = qpair->poll_status;
460 	struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
461 	struct spdk_nvmf_dhchap_challenge *challenge = status->dma_data;
462 	struct spdk_nvmf_dhchap_reply *reply = status->dma_data;
463 	struct nvme_auth *auth = &qpair->auth;
464 	uint8_t hl, response[NVME_AUTH_DATA_SIZE];
465 	int rc;
466 
467 	hl = nvme_auth_get_digest_len(challenge->hash_id);
468 	AUTH_DEBUGLOG(qpair, "key=%s, hash=%u, dhgroup=%u, seq=%u, tid=%u, subnqn=%s, hostnqn=%s, "
469 		      "len=%u\n", spdk_key_get_name(ctrlr->opts.dhchap_key),
470 		      challenge->hash_id, challenge->dhg_id, challenge->seqnum, auth->tid,
471 		      ctrlr->trid.subnqn, ctrlr->opts.hostnqn, hl);
472 	rc = nvme_auth_calc_response(ctrlr->opts.dhchap_key,
473 				     (enum spdk_nvmf_dhchap_hash)challenge->hash_id,
474 				     "HostHost", challenge->seqnum, auth->tid, 0,
475 				     ctrlr->opts.hostnqn, ctrlr->trid.subnqn, NULL, 0,
476 				     challenge->cval, response);
477 	if (rc != 0) {
478 		AUTH_ERRLOG(qpair, "failed to calculate response: %s\n", spdk_strerror(-rc));
479 		return rc;
480 	}
481 
482 	/* Now that the response has been calculated, send the reply */
483 	memset(qpair->poll_status->dma_data, 0, NVME_AUTH_DATA_SIZE);
484 	memcpy(reply->rval, response, hl);
485 
486 	reply->auth_type = SPDK_NVMF_AUTH_TYPE_DHCHAP;
487 	reply->auth_id = SPDK_NVMF_AUTH_ID_DHCHAP_REPLY;
488 	reply->t_id = auth->tid;
489 	reply->hl = hl;
490 	reply->cvalid = 0;
491 	reply->dhvlen = 0;
492 	reply->seqnum = 0;
493 
494 	/* The 2 * reply->hl below is because the spec says that both rval[hl] and cval[hl] must
495 	 * always be part of the reply message, even cvalid is zero.
496 	 */
497 	return nvme_auth_submit_request(qpair, SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND,
498 					sizeof(*reply) + 2 * reply->hl);
499 }
500 
501 static int
502 nvme_auth_check_success1(struct spdk_nvme_qpair *qpair)
503 {
504 	struct spdk_nvmf_dhchap_success1 *msg = qpair->poll_status->dma_data;
505 	struct nvme_auth *auth = &qpair->auth;
506 	int rc;
507 
508 	rc = nvme_auth_check_message(qpair, SPDK_NVMF_AUTH_ID_DHCHAP_SUCCESS1);
509 	if (rc != 0) {
510 		return rc;
511 	}
512 
513 	if (msg->t_id != auth->tid) {
514 		AUTH_ERRLOG(qpair, "unexpected tid: received=%u, expected=%u\n",
515 			    msg->t_id, auth->tid);
516 		goto error;
517 	}
518 
519 	return 0;
520 error:
521 	nvme_auth_set_failure(qpair, -EACCES,
522 			      nvme_auth_send_failure2(qpair, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD));
523 	return -EACCES;
524 }
525 
526 int
527 nvme_fabric_qpair_authenticate_poll(struct spdk_nvme_qpair *qpair)
528 {
529 	struct nvme_auth *auth = &qpair->auth;
530 	struct nvme_completion_poll_status *status = qpair->poll_status;
531 	enum nvme_qpair_auth_state prev_state;
532 	int rc;
533 
534 	do {
535 		prev_state = auth->state;
536 
537 		switch (auth->state) {
538 		case NVME_QPAIR_AUTH_STATE_NEGOTIATE:
539 			rc = nvme_auth_send_negotiate(qpair);
540 			if (rc != 0) {
541 				nvme_auth_set_failure(qpair, rc, false);
542 				AUTH_ERRLOG(qpair, "failed to send AUTH_negotiate: %s\n",
543 					    spdk_strerror(-rc));
544 				break;
545 			}
546 			nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_AWAIT_NEGOTIATE);
547 			break;
548 		case NVME_QPAIR_AUTH_STATE_AWAIT_NEGOTIATE:
549 			rc = nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL);
550 			if (rc != 0) {
551 				if (rc != -EAGAIN) {
552 					nvme_auth_print_cpl(qpair, "AUTH_negotiate");
553 					nvme_auth_set_failure(qpair, rc, false);
554 				}
555 				break;
556 			}
557 			/* Negotiate has been sent, try to receive the challenge */
558 			rc = nvme_auth_recv_message(qpair);
559 			if (rc != 0) {
560 				nvme_auth_set_failure(qpair, rc, false);
561 				AUTH_ERRLOG(qpair, "failed to recv DH-HMAC-CHAP_challenge: %s\n",
562 					    spdk_strerror(-rc));
563 				break;
564 			}
565 			nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_AWAIT_CHALLENGE);
566 			break;
567 		case NVME_QPAIR_AUTH_STATE_AWAIT_CHALLENGE:
568 			rc = nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL);
569 			if (rc != 0) {
570 				if (rc != -EAGAIN) {
571 					nvme_auth_print_cpl(qpair, "DH-HMAC-CHAP_challenge");
572 					nvme_auth_set_failure(qpair, rc, false);
573 				}
574 				break;
575 			}
576 			rc = nvme_auth_check_challenge(qpair);
577 			if (rc != 0) {
578 				break;
579 			}
580 			rc = nvme_auth_send_reply(qpair);
581 			if (rc != 0) {
582 				nvme_auth_set_failure(qpair, rc, false);
583 				AUTH_ERRLOG(qpair, "failed to send DH-HMAC-CHAP_reply: %s\n",
584 					    spdk_strerror(-rc));
585 				break;
586 			}
587 			nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_AWAIT_REPLY);
588 			break;
589 		case NVME_QPAIR_AUTH_STATE_AWAIT_REPLY:
590 			rc = nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL);
591 			if (rc != 0) {
592 				if (rc != -EAGAIN) {
593 					nvme_auth_print_cpl(qpair, "DH-HMAC-CHAP_reply");
594 					nvme_auth_set_failure(qpair, rc, false);
595 				}
596 				break;
597 			}
598 			/* Reply has been sent, try to receive response */
599 			rc = nvme_auth_recv_message(qpair);
600 			if (rc != 0) {
601 				nvme_auth_set_failure(qpair, rc, false);
602 				AUTH_ERRLOG(qpair, "failed to recv DH-HMAC-CHAP_success1: %s\n",
603 					    spdk_strerror(-rc));
604 				break;
605 			}
606 			nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS1);
607 			break;
608 		case NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS1:
609 			rc = nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL);
610 			if (rc != 0) {
611 				if (rc != -EAGAIN) {
612 					nvme_auth_print_cpl(qpair, "DH-HMAC-CHAP_success1");
613 					nvme_auth_set_failure(qpair, rc, false);
614 				}
615 				break;
616 			}
617 			rc = nvme_auth_check_success1(qpair);
618 			if (rc != 0) {
619 				break;
620 			}
621 			AUTH_DEBUGLOG(qpair, "authentication completed successfully\n");
622 			nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_DONE);
623 			break;
624 		case NVME_QPAIR_AUTH_STATE_AWAIT_FAILURE2:
625 			rc = nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL);
626 			if (rc == -EAGAIN) {
627 				break;
628 			}
629 			nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_DONE);
630 			break;
631 		case NVME_QPAIR_AUTH_STATE_DONE:
632 			if (qpair->poll_status != NULL && !status->timed_out) {
633 				qpair->poll_status = NULL;
634 				spdk_free(status->dma_data);
635 				free(status);
636 			}
637 			return auth->status;
638 		default:
639 			assert(0 && "invalid state");
640 			return -EINVAL;
641 		}
642 	} while (auth->state != prev_state);
643 
644 	return -EAGAIN;
645 }
646 
647 int
648 nvme_fabric_qpair_authenticate_async(struct spdk_nvme_qpair *qpair)
649 {
650 	struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
651 	struct nvme_completion_poll_status *status;
652 	struct nvme_auth *auth = &qpair->auth;
653 	int rc;
654 
655 	if (ctrlr->opts.dhchap_key == NULL) {
656 		AUTH_ERRLOG(qpair, "missing DH-HMAC-CHAP key\n");
657 		return -ENOKEY;
658 	}
659 
660 	if (qpair->auth.flags & NVME_QPAIR_AUTH_FLAG_ASCR) {
661 		AUTH_ERRLOG(qpair, "secure channel concatentation is not supported\n");
662 		return -EINVAL;
663 	}
664 
665 	status = calloc(1, sizeof(*qpair->poll_status));
666 	if (!status) {
667 		AUTH_ERRLOG(qpair, "failed to allocate poll status\n");
668 		return -ENOMEM;
669 	}
670 
671 	status->dma_data = spdk_zmalloc(NVME_AUTH_DATA_SIZE, 0, NULL, SPDK_ENV_LCORE_ID_ANY,
672 					SPDK_MALLOC_DMA);
673 	if (!status->dma_data) {
674 		AUTH_ERRLOG(qpair, "failed to allocate poll status\n");
675 		free(status);
676 		return -ENOMEM;
677 	}
678 
679 	assert(qpair->poll_status == NULL);
680 	qpair->poll_status = status;
681 
682 	nvme_robust_mutex_lock(&ctrlr->ctrlr_lock);
683 	auth->tid = ctrlr->auth_tid++;
684 	nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock);
685 
686 	nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_NEGOTIATE);
687 
688 	/* Do the initial poll to kick-start the state machine */
689 	rc = nvme_fabric_qpair_authenticate_poll(qpair);
690 	return rc != -EAGAIN ? rc : 0;
691 }
692 SPDK_LOG_REGISTER_COMPONENT(nvme_auth)
693