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