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