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