xref: /spdk/lib/nvmf/auth.c (revision 7b8c7efe8fe5cbfb09d5ebff2fbad7ce49c7504d)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2024 Intel Corporation
3  */
4 
5 #include "spdk/nvme.h"
6 #include "spdk/json.h"
7 #include "spdk/log.h"
8 #include "spdk/stdinc.h"
9 #include "spdk/string.h"
10 #include "spdk/thread.h"
11 #include "spdk/util.h"
12 #include "spdk_internal/nvme.h"
13 
14 #include <openssl/rand.h>
15 
16 #include "nvmf_internal.h"
17 
18 #define NVMF_AUTH_DEFAULT_KATO_US (120ull * 1000 * 1000)
19 #define NVMF_AUTH_DIGEST_MAX_SIZE 64
20 #define NVMF_AUTH_DH_KEY_MAX_SIZE 1024
21 
22 #define AUTH_ERRLOG(q, fmt, ...) \
23 	SPDK_ERRLOG("[%s:%s:%u] " fmt, (q)->ctrlr->subsys->subnqn, (q)->ctrlr->hostnqn, \
24 		    (q)->qid, ## __VA_ARGS__)
25 #define AUTH_DEBUGLOG(q, fmt, ...) \
26 	SPDK_DEBUGLOG(nvmf_auth, "[%s:%s:%u] " fmt, \
27 		      (q)->ctrlr->subsys->subnqn, (q)->ctrlr->hostnqn, (q)->qid, ## __VA_ARGS__)
28 #define AUTH_LOGDUMP(msg, buf, len) \
29 	SPDK_LOGDUMP(nvmf_auth, msg, buf, len)
30 
31 enum nvmf_qpair_auth_state {
32 	NVMF_QPAIR_AUTH_NEGOTIATE,
33 	NVMF_QPAIR_AUTH_CHALLENGE,
34 	NVMF_QPAIR_AUTH_REPLY,
35 	NVMF_QPAIR_AUTH_SUCCESS1,
36 	NVMF_QPAIR_AUTH_FAILURE1,
37 	NVMF_QPAIR_AUTH_COMPLETED,
38 	NVMF_QPAIR_AUTH_ERROR,
39 };
40 
41 struct spdk_nvmf_qpair_auth {
42 	enum nvmf_qpair_auth_state	state;
43 	struct spdk_poller		*poller;
44 	int				fail_reason;
45 	uint16_t			tid;
46 	int				digest;
47 	int				dhgroup;
48 	uint8_t				cval[NVMF_AUTH_DIGEST_MAX_SIZE];
49 	uint32_t			seqnum;
50 	struct spdk_nvme_dhchap_dhkey	*dhkey;
51 };
52 
53 struct nvmf_auth_common_header {
54 	uint8_t		auth_type;
55 	uint8_t		auth_id;
56 	uint8_t		reserved0[2];
57 	uint16_t	t_id;
58 };
59 
60 static void
61 nvmf_auth_request_complete(struct spdk_nvmf_request *req, int sct, int sc, int dnr)
62 {
63 	struct spdk_nvme_cpl *response = &req->rsp->nvme_cpl;
64 
65 	response->status.sct = sct;
66 	response->status.sc = sc;
67 	response->status.dnr = dnr;
68 
69 	spdk_nvmf_request_complete(req);
70 }
71 
72 static const char *
73 nvmf_auth_get_state_name(enum nvmf_qpair_auth_state state)
74 {
75 	static const char *state_names[] = {
76 		[NVMF_QPAIR_AUTH_NEGOTIATE] = "negotiate",
77 		[NVMF_QPAIR_AUTH_CHALLENGE] = "challenge",
78 		[NVMF_QPAIR_AUTH_REPLY] = "reply",
79 		[NVMF_QPAIR_AUTH_SUCCESS1] = "success1",
80 		[NVMF_QPAIR_AUTH_FAILURE1] = "failure1",
81 		[NVMF_QPAIR_AUTH_COMPLETED] = "completed",
82 		[NVMF_QPAIR_AUTH_ERROR] = "error",
83 	};
84 
85 	return state_names[state];
86 }
87 
88 static void
89 nvmf_auth_set_state(struct spdk_nvmf_qpair *qpair, enum nvmf_qpair_auth_state state)
90 {
91 	struct spdk_nvmf_qpair_auth *auth = qpair->auth;
92 
93 	if (auth->state == state) {
94 		return;
95 	}
96 
97 	AUTH_DEBUGLOG(qpair, "auth state: %s\n", nvmf_auth_get_state_name(state));
98 	auth->state = state;
99 }
100 
101 static void
102 nvmf_auth_disconnect_qpair(struct spdk_nvmf_qpair *qpair)
103 {
104 	nvmf_auth_set_state(qpair, NVMF_QPAIR_AUTH_ERROR);
105 	spdk_nvmf_qpair_disconnect(qpair);
106 }
107 
108 static void
109 nvmf_auth_request_fail1(struct spdk_nvmf_request *req, int reason)
110 {
111 	struct spdk_nvmf_qpair *qpair = req->qpair;
112 	struct spdk_nvmf_qpair_auth *auth = qpair->auth;
113 
114 	nvmf_auth_set_state(qpair, NVMF_QPAIR_AUTH_FAILURE1);
115 	auth->fail_reason = reason;
116 
117 	/* The command itself is completed successfully, but a subsequent AUTHENTICATION_RECV
118 	 * command will be completed with an AUTH_failure1 message
119 	 */
120 	nvmf_auth_request_complete(req, SPDK_NVME_SCT_GENERIC, SPDK_NVME_SC_SUCCESS, 0);
121 }
122 
123 static int
124 nvmf_auth_timeout_poller(void *ctx)
125 {
126 	struct spdk_nvmf_qpair *qpair = ctx;
127 	struct spdk_nvmf_qpair_auth *auth = qpair->auth;
128 
129 	AUTH_ERRLOG(qpair, "authentication timed out\n");
130 
131 	spdk_poller_unregister(&auth->poller);
132 	nvmf_auth_disconnect_qpair(qpair);
133 
134 	return SPDK_POLLER_BUSY;
135 }
136 
137 static int
138 nvmf_auth_rearm_poller(struct spdk_nvmf_qpair *qpair)
139 {
140 	struct spdk_nvmf_ctrlr *ctrlr = qpair->ctrlr;
141 	struct spdk_nvmf_qpair_auth *auth = qpair->auth;
142 	uint64_t timeout;
143 
144 	timeout = ctrlr->feat.keep_alive_timer.bits.kato > 0 ?
145 		  ctrlr->feat.keep_alive_timer.bits.kato * 1000 :
146 		  NVMF_AUTH_DEFAULT_KATO_US;
147 
148 	spdk_poller_unregister(&auth->poller);
149 	auth->poller = SPDK_POLLER_REGISTER(nvmf_auth_timeout_poller, qpair, timeout);
150 	if (auth->poller == NULL) {
151 		return -ENOMEM;
152 	}
153 
154 	return 0;
155 }
156 
157 static void
158 nvmf_auth_qpair_cleanup(struct spdk_nvmf_qpair_auth *auth)
159 {
160 	spdk_poller_unregister(&auth->poller);
161 	spdk_nvme_dhchap_dhkey_free(&auth->dhkey);
162 }
163 
164 static int
165 nvmf_auth_check_command(struct spdk_nvmf_request *req, uint8_t secp,
166 			uint8_t spsp0, uint8_t spsp1, uint32_t len)
167 {
168 	struct spdk_nvmf_qpair *qpair = req->qpair;
169 
170 	if (secp != SPDK_NVMF_AUTH_SECP_NVME) {
171 		AUTH_ERRLOG(qpair, "invalid secp=%u\n", secp);
172 		return -EINVAL;
173 	}
174 	if (spsp0 != 1 || spsp1 != 1) {
175 		AUTH_ERRLOG(qpair, "invalid spsp0=%u, spsp1=%u\n", spsp0, spsp1);
176 		return -EINVAL;
177 	}
178 	if (len != req->length) {
179 		AUTH_ERRLOG(qpair, "invalid length: %"PRIu32" != %"PRIu32"\n", len, req->length);
180 		return -EINVAL;
181 	}
182 
183 	return 0;
184 }
185 
186 static void *
187 nvmf_auth_get_message(struct spdk_nvmf_request *req, size_t size)
188 {
189 	if (req->length > 0 && req->iovcnt == 1 && req->iov[0].iov_len >= size) {
190 		return req->iov[0].iov_base;
191 	}
192 
193 	return NULL;
194 }
195 
196 static void
197 nvmf_auth_negotiate_exec(struct spdk_nvmf_request *req, struct spdk_nvmf_auth_negotiate *msg)
198 {
199 	struct spdk_nvmf_qpair *qpair = req->qpair;
200 	struct spdk_nvmf_qpair_auth *auth = qpair->auth;
201 	struct spdk_nvmf_auth_descriptor *desc = NULL;
202 	/* These arrays are sorted from the strongest hash/dhgroup to the weakest, so the strongest
203 	 * hash/dhgroup pair supported by the host is always selected
204 	 */
205 	enum spdk_nvmf_dhchap_hash digests[] = {
206 		SPDK_NVMF_DHCHAP_HASH_SHA512,
207 		SPDK_NVMF_DHCHAP_HASH_SHA384,
208 		SPDK_NVMF_DHCHAP_HASH_SHA256
209 	};
210 	enum spdk_nvmf_dhchap_dhgroup dhgroups[] = {
211 		SPDK_NVMF_DHCHAP_DHGROUP_8192,
212 		SPDK_NVMF_DHCHAP_DHGROUP_6144,
213 		SPDK_NVMF_DHCHAP_DHGROUP_4096,
214 		SPDK_NVMF_DHCHAP_DHGROUP_3072,
215 		SPDK_NVMF_DHCHAP_DHGROUP_2048,
216 		SPDK_NVMF_DHCHAP_DHGROUP_NULL,
217 	};
218 	int digest = -1, dhgroup = -1;
219 	size_t i, j;
220 
221 	if (auth->state != NVMF_QPAIR_AUTH_NEGOTIATE) {
222 		AUTH_ERRLOG(qpair, "invalid state: %s\n", nvmf_auth_get_state_name(auth->state));
223 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PROTOCOL_MESSAGE);
224 		return;
225 	}
226 
227 	auth->tid = msg->t_id;
228 	if (req->length < sizeof(*msg) || req->length != sizeof(*msg) + msg->napd * sizeof(*desc)) {
229 		AUTH_ERRLOG(qpair, "invalid message length: %"PRIu32"\n", req->length);
230 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
231 		return;
232 	}
233 
234 	if (msg->sc_c != SPDK_NVMF_AUTH_SCC_DISABLED) {
235 		AUTH_ERRLOG(qpair, "scc mismatch\n");
236 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_SCC_MISMATCH);
237 		return;
238 	}
239 
240 	for (i = 0; i < msg->napd; ++i) {
241 		if (msg->descriptors[i].auth_id == SPDK_NVMF_AUTH_TYPE_DHCHAP) {
242 			desc = &msg->descriptors[i];
243 			break;
244 		}
245 	}
246 	if (desc == NULL) {
247 		AUTH_ERRLOG(qpair, "no usable protocol found\n");
248 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_PROTOCOL_UNUSABLE);
249 		return;
250 	}
251 	if (desc->halen > SPDK_COUNTOF(desc->hash_id_list) ||
252 	    desc->dhlen > SPDK_COUNTOF(desc->dhg_id_list)) {
253 		AUTH_ERRLOG(qpair, "invalid halen=%u, dhlen=%u\n", desc->halen, desc->dhlen);
254 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
255 		return;
256 	}
257 
258 	for (i = 0; i < SPDK_COUNTOF(digests); ++i) {
259 		for (j = 0; j < desc->halen; ++j) {
260 			if (digests[i] == desc->hash_id_list[j]) {
261 				AUTH_DEBUGLOG(qpair, "selected digest: %s\n",
262 					      spdk_nvme_dhchap_get_digest_name(digests[i]));
263 				digest = digests[i];
264 				break;
265 			}
266 		}
267 		if (digest >= 0) {
268 			break;
269 		}
270 	}
271 	if (digest < 0) {
272 		AUTH_ERRLOG(qpair, "no usable digests found\n");
273 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_HASH_UNUSABLE);
274 		return;
275 	}
276 
277 	for (i = 0; i < SPDK_COUNTOF(dhgroups); ++i) {
278 		for (j = 0; j < desc->dhlen; ++j) {
279 			if (dhgroups[i] == desc->dhg_id_list[j]) {
280 				AUTH_DEBUGLOG(qpair, "selected dhgroup: %s\n",
281 					      spdk_nvme_dhchap_get_dhgroup_name(dhgroups[i]));
282 				dhgroup = dhgroups[i];
283 				break;
284 			}
285 		}
286 		if (dhgroup >= 0) {
287 			break;
288 		}
289 	}
290 	if (dhgroup < 0) {
291 		AUTH_ERRLOG(qpair, "no usable dhgroups found\n");
292 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_DHGROUP_UNUSABLE);
293 		return;
294 	}
295 
296 	if (nvmf_auth_rearm_poller(qpair)) {
297 		nvmf_auth_request_complete(req, SPDK_NVME_SCT_GENERIC,
298 					   SPDK_NVME_SC_INTERNAL_DEVICE_ERROR, 1);
299 		nvmf_auth_disconnect_qpair(qpair);
300 		return;
301 	}
302 
303 	auth->digest = digest;
304 	auth->dhgroup = dhgroup;
305 	nvmf_auth_set_state(qpair, NVMF_QPAIR_AUTH_CHALLENGE);
306 	nvmf_auth_request_complete(req, SPDK_NVME_SCT_GENERIC, SPDK_NVME_SC_SUCCESS, 0);
307 }
308 
309 static void
310 nvmf_auth_reply_exec(struct spdk_nvmf_request *req, struct spdk_nvmf_dhchap_reply *msg)
311 {
312 	struct spdk_nvmf_qpair *qpair = req->qpair;
313 	struct spdk_nvmf_ctrlr *ctrlr = qpair->ctrlr;
314 	struct spdk_nvmf_qpair_auth *auth = qpair->auth;
315 	uint8_t response[NVMF_AUTH_DIGEST_MAX_SIZE];
316 	uint8_t dhsec[NVMF_AUTH_DH_KEY_MAX_SIZE];
317 	struct spdk_key *key = NULL;
318 	size_t dhseclen = 0;
319 	uint8_t hl;
320 	int rc;
321 
322 	if (auth->state != NVMF_QPAIR_AUTH_REPLY) {
323 		AUTH_ERRLOG(qpair, "invalid state=%s\n", nvmf_auth_get_state_name(auth->state));
324 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PROTOCOL_MESSAGE);
325 		goto out;
326 	}
327 	if (req->length < sizeof(*msg)) {
328 		AUTH_ERRLOG(qpair, "invalid message length=%"PRIu32"\n", req->length);
329 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
330 		goto out;
331 	}
332 
333 	hl = spdk_nvme_dhchap_get_digest_length(auth->digest);
334 	if (hl == 0 || msg->hl != hl) {
335 		AUTH_ERRLOG(qpair, "hash length mismatch: %u != %u\n", msg->hl, hl);
336 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
337 		goto out;
338 	}
339 	if (req->length != sizeof(*msg) + 2 * hl + msg->dhvlen) {
340 		AUTH_ERRLOG(qpair, "invalid message length: %"PRIu32" != %zu\n",
341 			    req->length, sizeof(*msg) + 2 * hl);
342 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
343 		goto out;
344 	}
345 	if (msg->t_id != auth->tid) {
346 		AUTH_ERRLOG(qpair, "transaction id mismatch: %u != %u\n", msg->t_id, auth->tid);
347 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
348 		goto out;
349 	}
350 	if (msg->cvalid) {
351 		AUTH_ERRLOG(qpair, "bidirection authentication isn't supported yet\n");
352 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
353 		goto out;
354 	}
355 
356 	key = nvmf_subsystem_get_dhchap_key(ctrlr->subsys, ctrlr->hostnqn, NVMF_AUTH_KEY_HOST);
357 	if (key == NULL) {
358 		AUTH_ERRLOG(qpair, "couldn't get DH-HMAC-CHAP key\n");
359 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_FAILED);
360 		goto out;
361 	}
362 
363 	if (auth->dhgroup != SPDK_NVMF_DHCHAP_DHGROUP_NULL) {
364 		AUTH_LOGDUMP("host pubkey:", &msg->rval[2 * hl], msg->dhvlen);
365 		dhseclen = sizeof(dhsec);
366 		rc = spdk_nvme_dhchap_dhkey_derive_secret(auth->dhkey, &msg->rval[2 * hl],
367 				msg->dhvlen, dhsec, &dhseclen);
368 		if (rc != 0) {
369 			AUTH_ERRLOG(qpair, "couldn't derive DH secret\n");
370 			nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_FAILED);
371 			goto out;
372 		}
373 
374 		AUTH_LOGDUMP("dh secret:", dhsec, dhseclen);
375 	}
376 
377 	assert(hl <= sizeof(response) && hl <= sizeof(auth->cval));
378 	rc = spdk_nvme_dhchap_calculate(key, (enum spdk_nvmf_dhchap_hash)auth->digest,
379 					"HostHost", auth->seqnum, auth->tid, 0,
380 					ctrlr->hostnqn, ctrlr->subsys->subnqn,
381 					dhseclen > 0 ? dhsec : NULL, dhseclen,
382 					auth->cval, response);
383 	if (rc != 0) {
384 		AUTH_ERRLOG(qpair, "failed to calculate challenge response: %s\n",
385 			    spdk_strerror(-rc));
386 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_FAILED);
387 		goto out;
388 	}
389 
390 	if (memcmp(msg->rval, response, hl) != 0) {
391 		AUTH_ERRLOG(qpair, "challenge response mismatch\n");
392 		AUTH_LOGDUMP("response:", msg->rval, hl);
393 		AUTH_LOGDUMP("expected:", response, hl);
394 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_FAILED);
395 		goto out;
396 	}
397 
398 	if (nvmf_auth_rearm_poller(qpair)) {
399 		nvmf_auth_request_complete(req, SPDK_NVME_SCT_GENERIC,
400 					   SPDK_NVME_SC_INTERNAL_DEVICE_ERROR, 1);
401 		nvmf_auth_disconnect_qpair(qpair);
402 		goto out;
403 	}
404 
405 	nvmf_auth_set_state(qpair, NVMF_QPAIR_AUTH_SUCCESS1);
406 	nvmf_auth_request_complete(req, SPDK_NVME_SCT_GENERIC, SPDK_NVME_SC_SUCCESS, 0);
407 out:
408 	spdk_keyring_put_key(key);
409 }
410 
411 static void
412 nvmf_auth_send_exec(struct spdk_nvmf_request *req)
413 {
414 	struct spdk_nvmf_qpair *qpair = req->qpair;
415 	struct spdk_nvmf_fabric_auth_send_cmd *cmd = &req->cmd->auth_send_cmd;
416 	struct nvmf_auth_common_header *header;
417 	int rc;
418 
419 	rc = nvmf_auth_check_command(req, cmd->secp, cmd->spsp0, cmd->spsp1, cmd->tl);
420 	if (rc != 0) {
421 		nvmf_auth_request_complete(req, SPDK_NVME_SCT_GENERIC,
422 					   SPDK_NVME_SC_INVALID_FIELD, 1);
423 		return;
424 	}
425 
426 	header = nvmf_auth_get_message(req, sizeof(*header));
427 	if (header == NULL) {
428 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
429 		return;
430 	}
431 
432 	switch (header->auth_type) {
433 	case SPDK_NVMF_AUTH_TYPE_COMMON_MESSAGE:
434 		switch (header->auth_id) {
435 		case SPDK_NVMF_AUTH_ID_NEGOTIATE:
436 			nvmf_auth_negotiate_exec(req, (void *)header);
437 			break;
438 		default:
439 			AUTH_ERRLOG(qpair, "unexpected auth_id=%u\n", header->auth_id);
440 			nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PROTOCOL_MESSAGE);
441 			break;
442 		}
443 		break;
444 	case SPDK_NVMF_AUTH_TYPE_DHCHAP:
445 		switch (header->auth_id) {
446 		case SPDK_NVMF_AUTH_ID_DHCHAP_REPLY:
447 			nvmf_auth_reply_exec(req, (void *)header);
448 			break;
449 		default:
450 			AUTH_ERRLOG(qpair, "unexpected auth_id=%u\n", header->auth_id);
451 			nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PROTOCOL_MESSAGE);
452 			break;
453 		}
454 		break;
455 	default:
456 		AUTH_ERRLOG(qpair, "unexpected auth_type=%u\n", header->auth_type);
457 		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PROTOCOL_MESSAGE);
458 		break;
459 	}
460 }
461 
462 static void
463 nvmf_auth_recv_complete(struct spdk_nvmf_request *req, uint32_t length)
464 {
465 	assert(req->cmd->nvmf_cmd.fctype == SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_RECV);
466 	req->length = length;
467 	nvmf_auth_request_complete(req, SPDK_NVME_SCT_GENERIC, SPDK_NVME_SC_SUCCESS, 0);
468 }
469 
470 static void
471 nvmf_auth_recv_failure1(struct spdk_nvmf_request *req, int fail_reason)
472 {
473 	struct spdk_nvmf_qpair *qpair = req->qpair;
474 	struct spdk_nvmf_qpair_auth *auth = qpair->auth;
475 	struct spdk_nvmf_auth_failure *failure;
476 
477 	failure = nvmf_auth_get_message(req, sizeof(*failure));
478 	if (failure == NULL) {
479 		nvmf_auth_request_complete(req, SPDK_NVME_SCT_GENERIC,
480 					   SPDK_NVME_SC_INVALID_FIELD, 1);
481 		nvmf_auth_disconnect_qpair(qpair);
482 		return;
483 	}
484 
485 	failure->auth_type = SPDK_NVMF_AUTH_TYPE_COMMON_MESSAGE;
486 	failure->auth_id = SPDK_NVMF_AUTH_ID_FAILURE1;
487 	failure->t_id = auth->tid;
488 	failure->rc = SPDK_NVMF_AUTH_FAILURE;
489 	failure->rce = fail_reason;
490 
491 	nvmf_auth_set_state(qpair, NVMF_QPAIR_AUTH_FAILURE1);
492 	nvmf_auth_recv_complete(req, sizeof(*failure));
493 	nvmf_auth_disconnect_qpair(qpair);
494 }
495 
496 static int
497 nvmf_auth_get_seqnum(struct spdk_nvmf_qpair *qpair)
498 {
499 	struct spdk_nvmf_subsystem *subsys = qpair->ctrlr->subsys;
500 	struct spdk_nvmf_qpair_auth *auth = qpair->auth;
501 	int rc;
502 
503 	pthread_mutex_lock(&subsys->mutex);
504 	if (subsys->auth_seqnum == 0) {
505 		rc = RAND_bytes((void *)&subsys->auth_seqnum, sizeof(subsys->auth_seqnum));
506 		if (rc != 1) {
507 			pthread_mutex_unlock(&subsys->mutex);
508 			return -EIO;
509 		}
510 	}
511 	if (++subsys->auth_seqnum == 0) {
512 		subsys->auth_seqnum = 1;
513 
514 	}
515 	auth->seqnum = subsys->auth_seqnum;
516 	pthread_mutex_unlock(&subsys->mutex);
517 
518 	return 0;
519 }
520 
521 static int
522 nvmf_auth_recv_challenge(struct spdk_nvmf_request *req)
523 {
524 	struct spdk_nvmf_qpair *qpair = req->qpair;
525 	struct spdk_nvmf_qpair_auth *auth = qpair->auth;
526 	struct spdk_nvmf_dhchap_challenge *challenge;
527 	uint8_t hl, dhv[NVMF_AUTH_DH_KEY_MAX_SIZE];
528 	size_t dhvlen = 0;
529 	int rc;
530 
531 	hl = spdk_nvme_dhchap_get_digest_length(auth->digest);
532 	assert(hl > 0 && hl <= sizeof(auth->cval));
533 
534 	if (auth->dhgroup != SPDK_NVMF_DHCHAP_DHGROUP_NULL) {
535 		auth->dhkey = spdk_nvme_dhchap_generate_dhkey(auth->dhgroup);
536 		if (auth->dhkey == NULL) {
537 			AUTH_ERRLOG(qpair, "failed to generate DH key\n");
538 			return SPDK_NVMF_AUTH_FAILED;
539 		}
540 
541 		dhvlen = sizeof(dhv);
542 		rc = spdk_nvme_dhchap_dhkey_get_pubkey(auth->dhkey, dhv, &dhvlen);
543 		if (rc != 0) {
544 			AUTH_ERRLOG(qpair, "failed to get DH public key\n");
545 			return SPDK_NVMF_AUTH_FAILED;
546 		}
547 
548 		AUTH_LOGDUMP("ctrlr pubkey:", dhv, dhvlen);
549 	}
550 
551 	challenge = nvmf_auth_get_message(req, sizeof(*challenge) + hl + dhvlen);
552 	if (challenge == NULL) {
553 		AUTH_ERRLOG(qpair, "invalid message length: %"PRIu32"\n", req->length);
554 		return SPDK_NVMF_AUTH_INCORRECT_PAYLOAD;
555 	}
556 	rc = nvmf_auth_get_seqnum(qpair);
557 	if (rc != 0) {
558 		return SPDK_NVMF_AUTH_FAILED;
559 	}
560 	rc = RAND_bytes(auth->cval, hl);
561 	if (rc != 1) {
562 		return SPDK_NVMF_AUTH_FAILED;
563 	}
564 	if (nvmf_auth_rearm_poller(qpair)) {
565 		nvmf_auth_request_complete(req, SPDK_NVME_SCT_GENERIC,
566 					   SPDK_NVME_SC_INTERNAL_DEVICE_ERROR, 1);
567 		nvmf_auth_disconnect_qpair(qpair);
568 		return 0;
569 	}
570 
571 	memcpy(challenge->cval, auth->cval, hl);
572 	memcpy(&challenge->cval[hl], dhv, dhvlen);
573 	challenge->auth_type = SPDK_NVMF_AUTH_TYPE_DHCHAP;
574 	challenge->auth_id = SPDK_NVMF_AUTH_ID_DHCHAP_CHALLENGE;
575 	challenge->t_id = auth->tid;
576 	challenge->hl = hl;
577 	challenge->hash_id = (uint8_t)auth->digest;
578 	challenge->dhg_id = (uint8_t)auth->dhgroup;
579 	challenge->dhvlen = dhvlen;
580 	challenge->seqnum = auth->seqnum;
581 
582 	nvmf_auth_set_state(qpair, NVMF_QPAIR_AUTH_REPLY);
583 	nvmf_auth_recv_complete(req, sizeof(*challenge) + hl + dhvlen);
584 
585 	return 0;
586 }
587 
588 static int
589 nvmf_auth_recv_success1(struct spdk_nvmf_request *req)
590 {
591 	struct spdk_nvmf_qpair *qpair = req->qpair;
592 	struct spdk_nvmf_qpair_auth *auth = qpair->auth;
593 	struct spdk_nvmf_dhchap_success1 *success;
594 
595 	success = nvmf_auth_get_message(req, sizeof(*success));
596 	if (success == NULL) {
597 		AUTH_ERRLOG(qpair, "invalid message length: %"PRIu32"\n", req->length);
598 		return SPDK_NVMF_AUTH_INCORRECT_PAYLOAD;
599 	}
600 
601 	success->auth_type = SPDK_NVMF_AUTH_TYPE_DHCHAP;
602 	success->auth_id = SPDK_NVMF_AUTH_ID_DHCHAP_SUCCESS1;
603 	success->t_id = auth->tid;
604 	/* Kernel initiator always expects hl to be set, regardless of rvalid */
605 	success->hl = spdk_nvme_dhchap_get_digest_length(auth->digest);
606 	success->rvalid = 0;
607 
608 	AUTH_DEBUGLOG(qpair, "host authentication successful\n");
609 	nvmf_auth_recv_complete(req, sizeof(*success));
610 	nvmf_qpair_set_state(qpair, SPDK_NVMF_QPAIR_ENABLED);
611 	nvmf_auth_set_state(qpair, NVMF_QPAIR_AUTH_COMPLETED);
612 	nvmf_auth_qpair_cleanup(auth);
613 
614 	return 0;
615 }
616 
617 static void
618 nvmf_auth_recv_exec(struct spdk_nvmf_request *req)
619 {
620 	struct spdk_nvmf_qpair *qpair = req->qpair;
621 	struct spdk_nvmf_qpair_auth *auth = qpair->auth;
622 	struct spdk_nvmf_fabric_auth_recv_cmd *cmd = &req->cmd->auth_recv_cmd;
623 	int rc;
624 
625 	rc = nvmf_auth_check_command(req, cmd->secp, cmd->spsp0, cmd->spsp1, cmd->al);
626 	if (rc != 0) {
627 		nvmf_auth_request_complete(req, SPDK_NVME_SCT_GENERIC,
628 					   SPDK_NVME_SC_INVALID_FIELD, 1);
629 		return;
630 	}
631 
632 	spdk_iov_memset(req->iov, req->iovcnt, 0);
633 	switch (auth->state) {
634 	case NVMF_QPAIR_AUTH_CHALLENGE:
635 		rc = nvmf_auth_recv_challenge(req);
636 		if (rc != 0) {
637 			nvmf_auth_recv_failure1(req, rc);
638 		}
639 		break;
640 	case NVMF_QPAIR_AUTH_SUCCESS1:
641 		rc = nvmf_auth_recv_success1(req);
642 		if (rc != 0) {
643 			nvmf_auth_recv_failure1(req, rc);
644 		}
645 		break;
646 	case NVMF_QPAIR_AUTH_FAILURE1:
647 		nvmf_auth_recv_failure1(req, auth->fail_reason);
648 		break;
649 	default:
650 		nvmf_auth_recv_failure1(req, SPDK_NVMF_AUTH_INCORRECT_PROTOCOL_MESSAGE);
651 		break;
652 	}
653 }
654 
655 int
656 nvmf_auth_request_exec(struct spdk_nvmf_request *req)
657 {
658 	struct spdk_nvmf_qpair *qpair = req->qpair;
659 	union nvmf_h2c_msg *cmd = req->cmd;
660 
661 	/* We don't support reauthentication */
662 	if (qpair->state != SPDK_NVMF_QPAIR_AUTHENTICATING) {
663 		nvmf_auth_request_complete(req, SPDK_NVME_SCT_GENERIC,
664 					   SPDK_NVME_SC_COMMAND_SEQUENCE_ERROR, 0);
665 		return SPDK_NVMF_REQUEST_EXEC_STATUS_ASYNCHRONOUS;
666 	}
667 
668 	assert(cmd->nvmf_cmd.opcode == SPDK_NVME_OPC_FABRIC);
669 	switch (cmd->nvmf_cmd.fctype) {
670 	case SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND:
671 		nvmf_auth_send_exec(req);
672 		break;
673 	case SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_RECV:
674 		nvmf_auth_recv_exec(req);
675 		break;
676 	default:
677 		assert(0 && "invalid fctype");
678 		nvmf_auth_request_complete(req, SPDK_NVME_SCT_GENERIC,
679 					   SPDK_NVME_SC_INTERNAL_DEVICE_ERROR, 0);
680 		break;
681 	}
682 
683 	return SPDK_NVMF_REQUEST_EXEC_STATUS_ASYNCHRONOUS;
684 }
685 
686 int
687 nvmf_qpair_auth_init(struct spdk_nvmf_qpair *qpair)
688 {
689 	struct spdk_nvmf_qpair_auth *auth;
690 	int rc;
691 
692 	assert(qpair->auth == NULL);
693 	auth = calloc(1, sizeof(*qpair->auth));
694 	if (auth == NULL) {
695 		return -ENOMEM;
696 	}
697 
698 	auth->digest = -1;
699 	qpair->auth = auth;
700 	nvmf_auth_set_state(qpair, NVMF_QPAIR_AUTH_NEGOTIATE);
701 
702 	rc = nvmf_auth_rearm_poller(qpair);
703 	if (rc != 0) {
704 		AUTH_ERRLOG(qpair, "failed to arm timeout poller: %s\n", spdk_strerror(-rc));
705 		nvmf_qpair_auth_destroy(qpair);
706 		return rc;
707 	}
708 
709 	return 0;
710 }
711 
712 void
713 nvmf_qpair_auth_destroy(struct spdk_nvmf_qpair *qpair)
714 {
715 	struct spdk_nvmf_qpair_auth *auth = qpair->auth;
716 
717 	if (auth != NULL) {
718 		nvmf_auth_qpair_cleanup(auth);
719 		free(qpair->auth);
720 		qpair->auth = NULL;
721 	}
722 }
723 
724 void
725 nvmf_qpair_auth_dump(struct spdk_nvmf_qpair *qpair, struct spdk_json_write_ctx *w)
726 {
727 	struct spdk_nvmf_qpair_auth *auth = qpair->auth;
728 	const char *digest, *dhgroup;
729 
730 	if (auth == NULL) {
731 		return;
732 	}
733 
734 	spdk_json_write_named_object_begin(w, "auth");
735 	spdk_json_write_named_string(w, "state", nvmf_auth_get_state_name(auth->state));
736 	digest = spdk_nvme_dhchap_get_digest_name(auth->digest);
737 	spdk_json_write_named_string(w, "digest", digest ? digest : "unknown");
738 	dhgroup = spdk_nvme_dhchap_get_dhgroup_name(auth->dhgroup);
739 	spdk_json_write_named_string(w, "dhgroup", dhgroup ? dhgroup : "unknown");
740 	spdk_json_write_object_end(w);
741 }
742 
743 bool
744 nvmf_auth_is_supported(void)
745 {
746 	return true;
747 }
748 SPDK_LOG_REGISTER_COMPONENT(nvmf_auth)
749