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