]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/IScsiDxe/IScsiCHAP.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / NetworkPkg / IScsiDxe / IScsiCHAP.c
1 /** @file
2 This file is for Challenge-Handshake Authentication Protocol (CHAP)
3 Configuration.
4
5 Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include "IScsiImpl.h"
11
12 //
13 // Supported CHAP hash algorithms, mapped to sets of BaseCryptLib APIs and
14 // macros. CHAP_HASH structures at lower subscripts in the array are preferred
15 // by the initiator.
16 //
17 STATIC CONST CHAP_HASH mChapHash[] = {
18 {
19 ISCSI_CHAP_ALGORITHM_SHA256,
20 SHA256_DIGEST_SIZE,
21 Sha256GetContextSize,
22 Sha256Init,
23 Sha256Update,
24 Sha256Final
25 },
26 #ifdef ENABLE_MD5_DEPRECATED_INTERFACES
27 //
28 // Keep the deprecated MD5 entry at the end of the array (making MD5 the
29 // least preferred choice of the initiator).
30 //
31 {
32 ISCSI_CHAP_ALGORITHM_MD5,
33 MD5_DIGEST_SIZE,
34 Md5GetContextSize,
35 Md5Init,
36 Md5Update,
37 Md5Final
38 },
39 #endif // ENABLE_MD5_DEPRECATED_INTERFACES
40 };
41
42 //
43 // Ordered list of mChapHash[*].Algorithm values. It is formatted for the
44 // CHAP_A=<A1,A2...> value string, by the IScsiCHAPInitHashList() function. It
45 // is sent by the initiator in ISCSI_CHAP_STEP_ONE.
46 //
47 STATIC CHAR8 mChapHashListString[
48 3 + // UINT8 identifier in
49 // decimal
50 (1 + 3) * (ARRAY_SIZE (mChapHash) - 1) + // comma prepended for
51 // entries after the
52 // first
53 1 + // extra character for
54 // AsciiSPrint()
55 // truncation check
56 1 // terminating NUL
57 ];
58
59 /**
60 Initiator calculates its own expected hash value.
61
62 @param[in] ChapIdentifier iSCSI CHAP identifier sent by authenticator.
63 @param[in] ChapSecret iSCSI CHAP secret of the authenticator.
64 @param[in] SecretLength The length of iSCSI CHAP secret.
65 @param[in] ChapChallenge The challenge message sent by authenticator.
66 @param[in] ChallengeLength The length of iSCSI CHAP challenge message.
67 @param[in] Hash Pointer to the CHAP_HASH structure that
68 determines the hashing algorithm to use. The
69 caller is responsible for making Hash point
70 to an "mChapHash" element.
71 @param[out] ChapResponse The calculation of the expected hash value.
72
73 @retval EFI_SUCCESS The expected hash value was calculatedly
74 successfully.
75 @retval EFI_PROTOCOL_ERROR The length of the secret should be at least
76 the length of the hash value for the hashing
77 algorithm chosen.
78 @retval EFI_PROTOCOL_ERROR Hash operation fails.
79 @retval EFI_OUT_OF_RESOURCES Failure to allocate resource to complete
80 hashing.
81
82 **/
83 EFI_STATUS
84 IScsiCHAPCalculateResponse (
85 IN UINT32 ChapIdentifier,
86 IN CHAR8 *ChapSecret,
87 IN UINT32 SecretLength,
88 IN UINT8 *ChapChallenge,
89 IN UINT32 ChallengeLength,
90 IN CONST CHAP_HASH *Hash,
91 OUT UINT8 *ChapResponse
92 )
93 {
94 UINTN ContextSize;
95 VOID *Ctx;
96 CHAR8 IdByte[1];
97 EFI_STATUS Status;
98
99 if (SecretLength < ISCSI_CHAP_SECRET_MIN_LEN) {
100 return EFI_PROTOCOL_ERROR;
101 }
102
103 ASSERT (Hash != NULL);
104
105 ContextSize = Hash->GetContextSize ();
106 Ctx = AllocatePool (ContextSize);
107 if (Ctx == NULL) {
108 return EFI_OUT_OF_RESOURCES;
109 }
110
111 Status = EFI_PROTOCOL_ERROR;
112
113 if (!Hash->Init (Ctx)) {
114 goto Exit;
115 }
116
117 //
118 // Hash Identifier - Only calculate 1 byte data (RFC1994)
119 //
120 IdByte[0] = (CHAR8)ChapIdentifier;
121 if (!Hash->Update (Ctx, IdByte, 1)) {
122 goto Exit;
123 }
124
125 //
126 // Hash Secret
127 //
128 if (!Hash->Update (Ctx, ChapSecret, SecretLength)) {
129 goto Exit;
130 }
131
132 //
133 // Hash Challenge received from Target
134 //
135 if (!Hash->Update (Ctx, ChapChallenge, ChallengeLength)) {
136 goto Exit;
137 }
138
139 if (Hash->Final (Ctx, ChapResponse)) {
140 Status = EFI_SUCCESS;
141 }
142
143 Exit:
144 FreePool (Ctx);
145 return Status;
146 }
147
148 /**
149 The initiator checks the CHAP response replied by target against its own
150 calculation of the expected hash value.
151
152 @param[in] AuthData iSCSI CHAP authentication data.
153 @param[in] TargetResponse The response from target.
154
155 @retval EFI_SUCCESS The response from target passed
156 authentication.
157 @retval EFI_SECURITY_VIOLATION The response from target was not expected
158 value.
159 @retval Others Other errors as indicated.
160
161 **/
162 EFI_STATUS
163 IScsiCHAPAuthTarget (
164 IN ISCSI_CHAP_AUTH_DATA *AuthData,
165 IN UINT8 *TargetResponse
166 )
167 {
168 EFI_STATUS Status;
169 UINT32 SecretSize;
170 UINT8 VerifyRsp[ISCSI_CHAP_MAX_DIGEST_SIZE];
171 INTN Mismatch;
172
173 Status = EFI_SUCCESS;
174
175 SecretSize = (UINT32)AsciiStrLen (AuthData->AuthConfig->ReverseCHAPSecret);
176
177 ASSERT (AuthData->Hash != NULL);
178
179 Status = IScsiCHAPCalculateResponse (
180 AuthData->OutIdentifier,
181 AuthData->AuthConfig->ReverseCHAPSecret,
182 SecretSize,
183 AuthData->OutChallenge,
184 AuthData->Hash->DigestSize, // ChallengeLength
185 AuthData->Hash,
186 VerifyRsp
187 );
188
189 Mismatch = CompareMem (
190 VerifyRsp,
191 TargetResponse,
192 AuthData->Hash->DigestSize
193 );
194 if (Mismatch != 0) {
195 Status = EFI_SECURITY_VIOLATION;
196 }
197
198 return Status;
199 }
200
201 /**
202 This function checks the received iSCSI Login Response during the security
203 negotiation stage.
204
205 @param[in] Conn The iSCSI connection.
206
207 @retval EFI_SUCCESS The Login Response passed the CHAP validation.
208 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
209 @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
210 @retval Others Other errors as indicated.
211
212 **/
213 EFI_STATUS
214 IScsiCHAPOnRspReceived (
215 IN ISCSI_CONNECTION *Conn
216 )
217 {
218 EFI_STATUS Status;
219 ISCSI_SESSION *Session;
220 ISCSI_CHAP_AUTH_DATA *AuthData;
221 CHAR8 *Value;
222 UINT8 *Data;
223 UINT32 Len;
224 LIST_ENTRY *KeyValueList;
225 UINTN Algorithm;
226 CHAR8 *Identifier;
227 CHAR8 *Challenge;
228 CHAR8 *Name;
229 CHAR8 *Response;
230 UINT8 TargetRsp[ISCSI_CHAP_MAX_DIGEST_SIZE];
231 UINT32 RspLen;
232 UINTN Result;
233 UINTN HashIndex;
234
235 ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);
236 ASSERT (Conn->RspQue.BufNum != 0);
237
238 Session = Conn->Session;
239 AuthData = &Session->AuthData.CHAP;
240 Len = Conn->RspQue.BufSize;
241 Data = AllocateZeroPool (Len);
242 if (Data == NULL) {
243 return EFI_OUT_OF_RESOURCES;
244 }
245
246 //
247 // Copy the data in case the data spans over multiple PDUs.
248 //
249 NetbufQueCopy (&Conn->RspQue, 0, Len, Data);
250
251 //
252 // Build the key-value list from the data segment of the Login Response.
253 //
254 KeyValueList = IScsiBuildKeyValueList ((CHAR8 *)Data, Len);
255 if (KeyValueList == NULL) {
256 Status = EFI_OUT_OF_RESOURCES;
257 goto ON_EXIT;
258 }
259
260 Status = EFI_PROTOCOL_ERROR;
261
262 switch (Conn->AuthStep) {
263 case ISCSI_AUTH_INITIAL:
264 //
265 // The first Login Response.
266 //
267 Value = IScsiGetValueByKeyFromList (
268 KeyValueList,
269 ISCSI_KEY_TARGET_PORTAL_GROUP_TAG
270 );
271 if (Value == NULL) {
272 goto ON_EXIT;
273 }
274
275 Result = IScsiNetNtoi (Value);
276 if (Result > 0xFFFF) {
277 goto ON_EXIT;
278 }
279
280 Session->TargetPortalGroupTag = (UINT16)Result;
281
282 Value = IScsiGetValueByKeyFromList (
283 KeyValueList,
284 ISCSI_KEY_AUTH_METHOD
285 );
286 if (Value == NULL) {
287 goto ON_EXIT;
288 }
289
290 //
291 // Initiator mandates CHAP authentication but target replies without
292 // "CHAP", or initiator suggets "None" but target replies with some kind of
293 // auth method.
294 //
295 if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {
296 if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) != 0) {
297 goto ON_EXIT;
298 }
299 } else if (Session->AuthType == ISCSI_AUTH_TYPE_CHAP) {
300 if (AsciiStrCmp (Value, ISCSI_AUTH_METHOD_CHAP) != 0) {
301 goto ON_EXIT;
302 }
303 } else {
304 goto ON_EXIT;
305 }
306
307 //
308 // Transit to CHAP step one.
309 //
310 Conn->AuthStep = ISCSI_CHAP_STEP_ONE;
311 Status = EFI_SUCCESS;
312 break;
313
314 case ISCSI_CHAP_STEP_TWO:
315 //
316 // The Target replies with CHAP_A=<A> CHAP_I=<I> CHAP_C=<C>
317 //
318 Value = IScsiGetValueByKeyFromList (
319 KeyValueList,
320 ISCSI_KEY_CHAP_ALGORITHM
321 );
322 if (Value == NULL) {
323 goto ON_EXIT;
324 }
325
326 Algorithm = IScsiNetNtoi (Value);
327 for (HashIndex = 0; HashIndex < ARRAY_SIZE (mChapHash); HashIndex++) {
328 if (Algorithm == mChapHash[HashIndex].Algorithm) {
329 break;
330 }
331 }
332
333 if (HashIndex == ARRAY_SIZE (mChapHash)) {
334 //
335 // Unsupported algorithm is chosen by target.
336 //
337 goto ON_EXIT;
338 }
339
340 //
341 // Remember the target's chosen hash algorithm.
342 //
343 ASSERT (AuthData->Hash == NULL);
344 AuthData->Hash = &mChapHash[HashIndex];
345
346 Identifier = IScsiGetValueByKeyFromList (
347 KeyValueList,
348 ISCSI_KEY_CHAP_IDENTIFIER
349 );
350 if (Identifier == NULL) {
351 goto ON_EXIT;
352 }
353
354 Challenge = IScsiGetValueByKeyFromList (
355 KeyValueList,
356 ISCSI_KEY_CHAP_CHALLENGE
357 );
358 if (Challenge == NULL) {
359 goto ON_EXIT;
360 }
361
362 //
363 // Process the CHAP identifier and CHAP Challenge from Target.
364 // Calculate Response value.
365 //
366 Result = IScsiNetNtoi (Identifier);
367 if (Result > 0xFF) {
368 goto ON_EXIT;
369 }
370
371 AuthData->InIdentifier = (UINT32)Result;
372 AuthData->InChallengeLength = (UINT32)sizeof (AuthData->InChallenge);
373 Status = IScsiHexToBin (
374 (UINT8 *)AuthData->InChallenge,
375 &AuthData->InChallengeLength,
376 Challenge
377 );
378 if (EFI_ERROR (Status)) {
379 Status = EFI_PROTOCOL_ERROR;
380 goto ON_EXIT;
381 }
382
383 Status = IScsiCHAPCalculateResponse (
384 AuthData->InIdentifier,
385 AuthData->AuthConfig->CHAPSecret,
386 (UINT32)AsciiStrLen (AuthData->AuthConfig->CHAPSecret),
387 AuthData->InChallenge,
388 AuthData->InChallengeLength,
389 AuthData->Hash,
390 AuthData->CHAPResponse
391 );
392
393 //
394 // Transit to next step.
395 //
396 Conn->AuthStep = ISCSI_CHAP_STEP_THREE;
397 break;
398
399 case ISCSI_CHAP_STEP_THREE:
400 //
401 // One way CHAP authentication and the target would like to
402 // authenticate us.
403 //
404 Status = EFI_SUCCESS;
405 break;
406
407 case ISCSI_CHAP_STEP_FOUR:
408 ASSERT (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL);
409 //
410 // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.
411 //
412 Name = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_NAME);
413 if (Name == NULL) {
414 goto ON_EXIT;
415 }
416
417 Response = IScsiGetValueByKeyFromList (
418 KeyValueList,
419 ISCSI_KEY_CHAP_RESPONSE
420 );
421 if (Response == NULL) {
422 goto ON_EXIT;
423 }
424
425 ASSERT (AuthData->Hash != NULL);
426 RspLen = AuthData->Hash->DigestSize;
427 Status = IScsiHexToBin (TargetRsp, &RspLen, Response);
428 if (EFI_ERROR (Status) || (RspLen != AuthData->Hash->DigestSize)) {
429 Status = EFI_PROTOCOL_ERROR;
430 goto ON_EXIT;
431 }
432
433 //
434 // Check the CHAP Name and Response replied by Target.
435 //
436 Status = IScsiCHAPAuthTarget (AuthData, TargetRsp);
437 break;
438
439 default:
440 break;
441 }
442
443 ON_EXIT:
444
445 if (KeyValueList != NULL) {
446 IScsiFreeKeyValueList (KeyValueList);
447 }
448
449 FreePool (Data);
450
451 return Status;
452 }
453
454 /**
455 This function fills the CHAP authentication information into the login PDU
456 during the security negotiation stage in the iSCSI connection login.
457
458 @param[in] Conn The iSCSI connection.
459 @param[in, out] Pdu The PDU to send out.
460
461 @retval EFI_SUCCESS All check passed and the phase-related CHAP
462 authentication info is filled into the iSCSI
463 PDU.
464 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
465 @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
466
467 **/
468 EFI_STATUS
469 IScsiCHAPToSendReq (
470 IN ISCSI_CONNECTION *Conn,
471 IN OUT NET_BUF *Pdu
472 )
473 {
474 EFI_STATUS Status;
475 ISCSI_SESSION *Session;
476 ISCSI_LOGIN_REQUEST *LoginReq;
477 ISCSI_CHAP_AUTH_DATA *AuthData;
478 CHAR8 *Value;
479 CHAR8 ValueStr[256];
480 CHAR8 *Response;
481 UINT32 RspLen;
482 CHAR8 *Challenge;
483 UINT32 ChallengeLen;
484 EFI_STATUS BinToHexStatus;
485
486 ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);
487
488 Session = Conn->Session;
489 AuthData = &Session->AuthData.CHAP;
490 LoginReq = (ISCSI_LOGIN_REQUEST *)NetbufGetByte (Pdu, 0, 0);
491 if (LoginReq == NULL) {
492 return EFI_PROTOCOL_ERROR;
493 }
494
495 Status = EFI_SUCCESS;
496
497 RspLen = 2 * ISCSI_CHAP_MAX_DIGEST_SIZE + 3;
498 Response = AllocateZeroPool (RspLen);
499 if (Response == NULL) {
500 return EFI_OUT_OF_RESOURCES;
501 }
502
503 ChallengeLen = 2 * ISCSI_CHAP_MAX_DIGEST_SIZE + 3;
504 Challenge = AllocateZeroPool (ChallengeLen);
505 if (Challenge == NULL) {
506 FreePool (Response);
507 return EFI_OUT_OF_RESOURCES;
508 }
509
510 switch (Conn->AuthStep) {
511 case ISCSI_AUTH_INITIAL:
512 //
513 // It's the initial Login Request. Fill in the key=value pairs mandatory
514 // for the initial Login Request.
515 //
516 IScsiAddKeyValuePair (
517 Pdu,
518 ISCSI_KEY_INITIATOR_NAME,
519 mPrivate->InitiatorName
520 );
521 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_SESSION_TYPE, "Normal");
522 IScsiAddKeyValuePair (
523 Pdu,
524 ISCSI_KEY_TARGET_NAME,
525 Session->ConfigData->SessionConfigData.TargetName
526 );
527
528 if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {
529 Value = ISCSI_KEY_VALUE_NONE;
530 ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
531 } else {
532 Value = ISCSI_AUTH_METHOD_CHAP;
533 }
534
535 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_AUTH_METHOD, Value);
536
537 break;
538
539 case ISCSI_CHAP_STEP_ONE:
540 //
541 // First step, send the Login Request with CHAP_A=<A1,A2...> key-value
542 // pair.
543 //
544 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, mChapHashListString);
545
546 Conn->AuthStep = ISCSI_CHAP_STEP_TWO;
547 break;
548
549 case ISCSI_CHAP_STEP_THREE:
550 //
551 // Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or
552 // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target authentication is
553 // required too.
554 //
555 // CHAP_N=<N>
556 //
557 IScsiAddKeyValuePair (
558 Pdu,
559 ISCSI_KEY_CHAP_NAME,
560 (CHAR8 *)&AuthData->AuthConfig->CHAPName
561 );
562 //
563 // CHAP_R=<R>
564 //
565 ASSERT (AuthData->Hash != NULL);
566 BinToHexStatus = IScsiBinToHex (
567 (UINT8 *)AuthData->CHAPResponse,
568 AuthData->Hash->DigestSize,
569 Response,
570 &RspLen
571 );
572 ASSERT_EFI_ERROR (BinToHexStatus);
573 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_RESPONSE, Response);
574
575 if (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL) {
576 //
577 // CHAP_I=<I>
578 //
579 IScsiGenRandom ((UINT8 *)&AuthData->OutIdentifier, 1);
580 AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", AuthData->OutIdentifier);
581 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_IDENTIFIER, ValueStr);
582 //
583 // CHAP_C=<C>
584 //
585 IScsiGenRandom (
586 (UINT8 *)AuthData->OutChallenge,
587 AuthData->Hash->DigestSize
588 );
589 BinToHexStatus = IScsiBinToHex (
590 (UINT8 *)AuthData->OutChallenge,
591 AuthData->Hash->DigestSize,
592 Challenge,
593 &ChallengeLen
594 );
595 ASSERT_EFI_ERROR (BinToHexStatus);
596 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_CHALLENGE, Challenge);
597
598 Conn->AuthStep = ISCSI_CHAP_STEP_FOUR;
599 }
600
601 //
602 // Set the stage transition flag.
603 //
604 ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
605 break;
606
607 default:
608 Status = EFI_PROTOCOL_ERROR;
609 break;
610 }
611
612 FreePool (Response);
613 FreePool (Challenge);
614
615 return Status;
616 }
617
618 /**
619 Initialize the CHAP_A=<A1,A2...> *value* string for the entire driver, to be
620 sent by the initiator in ISCSI_CHAP_STEP_ONE.
621
622 This function sanity-checks the internal table of supported CHAP hashing
623 algorithms, as well.
624 **/
625 VOID
626 IScsiCHAPInitHashList (
627 VOID
628 )
629 {
630 CHAR8 *Position;
631 UINTN Left;
632 UINTN HashIndex;
633 CONST CHAP_HASH *Hash;
634 UINTN Printed;
635
636 Position = mChapHashListString;
637 Left = sizeof (mChapHashListString);
638 for (HashIndex = 0; HashIndex < ARRAY_SIZE (mChapHash); HashIndex++) {
639 Hash = &mChapHash[HashIndex];
640
641 //
642 // Format the next hash identifier.
643 //
644 // Assert that we can format at least one non-NUL character, i.e. that we
645 // can progress. Truncation is checked after printing.
646 //
647 ASSERT (Left >= 2);
648 Printed = AsciiSPrint (
649 Position,
650 Left,
651 "%a%d",
652 (HashIndex == 0) ? "" : ",",
653 Hash->Algorithm
654 );
655 //
656 // There's no way to differentiate between the "buffer filled to the brim,
657 // but not truncated" result and the "truncated" result of AsciiSPrint().
658 // This is why "mChapHashListString" has an extra byte allocated, and the
659 // reason why we use the less-than (rather than the less-than-or-equal-to)
660 // relational operator in the assertion below -- we enforce "no truncation"
661 // by excluding the "completely used up" case too.
662 //
663 ASSERT (Printed + 1 < Left);
664
665 Position += Printed;
666 Left -= Printed;
667
668 //
669 // Sanity-check the digest size for Hash.
670 //
671 ASSERT (Hash->DigestSize <= ISCSI_CHAP_MAX_DIGEST_SIZE);
672 }
673 }