2 This file is for Challenge-Handshake Authentication Protocol (CHAP)
5 Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
10 #include "IScsiImpl.h"
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
17 STATIC CONST CHAP_HASH mChapHash
[] = {
19 ISCSI_CHAP_ALGORITHM_SHA256
,
26 #ifdef ENABLE_MD5_DEPRECATED_INTERFACES
28 // Keep the deprecated MD5 entry at the end of the array (making MD5 the
29 // least preferred choice of the initiator).
32 ISCSI_CHAP_ALGORITHM_MD5
,
39 #endif // ENABLE_MD5_DEPRECATED_INTERFACES
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.
47 STATIC CHAR8 mChapHashListString
[
48 3 + // UINT8 identifier in
50 (1 + 3) * (ARRAY_SIZE (mChapHash
) - 1) + // comma prepended for
53 1 + // extra character for
60 Initiator calculates its own expected hash value.
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.
73 @retval EFI_SUCCESS The expected hash value was calculatedly
75 @retval EFI_PROTOCOL_ERROR The length of the secret should be at least
76 the length of the hash value for the hashing
78 @retval EFI_PROTOCOL_ERROR Hash operation fails.
79 @retval EFI_OUT_OF_RESOURCES Failure to allocate resource to complete
84 IScsiCHAPCalculateResponse (
85 IN UINT32 ChapIdentifier
,
87 IN UINT32 SecretLength
,
88 IN UINT8
*ChapChallenge
,
89 IN UINT32 ChallengeLength
,
90 IN CONST CHAP_HASH
*Hash
,
91 OUT UINT8
*ChapResponse
99 if (SecretLength
< ISCSI_CHAP_SECRET_MIN_LEN
) {
100 return EFI_PROTOCOL_ERROR
;
103 ASSERT (Hash
!= NULL
);
105 ContextSize
= Hash
->GetContextSize ();
106 Ctx
= AllocatePool (ContextSize
);
108 return EFI_OUT_OF_RESOURCES
;
111 Status
= EFI_PROTOCOL_ERROR
;
113 if (!Hash
->Init (Ctx
)) {
118 // Hash Identifier - Only calculate 1 byte data (RFC1994)
120 IdByte
[0] = (CHAR8
)ChapIdentifier
;
121 if (!Hash
->Update (Ctx
, IdByte
, 1)) {
128 if (!Hash
->Update (Ctx
, ChapSecret
, SecretLength
)) {
133 // Hash Challenge received from Target
135 if (!Hash
->Update (Ctx
, ChapChallenge
, ChallengeLength
)) {
139 if (Hash
->Final (Ctx
, ChapResponse
)) {
140 Status
= EFI_SUCCESS
;
149 The initiator checks the CHAP response replied by target against its own
150 calculation of the expected hash value.
152 @param[in] AuthData iSCSI CHAP authentication data.
153 @param[in] TargetResponse The response from target.
155 @retval EFI_SUCCESS The response from target passed
157 @retval EFI_SECURITY_VIOLATION The response from target was not expected
159 @retval Others Other errors as indicated.
163 IScsiCHAPAuthTarget (
164 IN ISCSI_CHAP_AUTH_DATA
*AuthData
,
165 IN UINT8
*TargetResponse
170 UINT8 VerifyRsp
[ISCSI_CHAP_MAX_DIGEST_SIZE
];
173 Status
= EFI_SUCCESS
;
175 SecretSize
= (UINT32
)AsciiStrLen (AuthData
->AuthConfig
->ReverseCHAPSecret
);
177 ASSERT (AuthData
->Hash
!= NULL
);
179 Status
= IScsiCHAPCalculateResponse (
180 AuthData
->OutIdentifier
,
181 AuthData
->AuthConfig
->ReverseCHAPSecret
,
183 AuthData
->OutChallenge
,
184 AuthData
->Hash
->DigestSize
, // ChallengeLength
189 Mismatch
= CompareMem (
192 AuthData
->Hash
->DigestSize
195 Status
= EFI_SECURITY_VIOLATION
;
202 This function checks the received iSCSI Login Response during the security
205 @param[in] Conn The iSCSI connection.
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.
214 IScsiCHAPOnRspReceived (
215 IN ISCSI_CONNECTION
*Conn
219 ISCSI_SESSION
*Session
;
220 ISCSI_CHAP_AUTH_DATA
*AuthData
;
224 LIST_ENTRY
*KeyValueList
;
230 UINT8 TargetRsp
[ISCSI_CHAP_MAX_DIGEST_SIZE
];
235 ASSERT (Conn
->CurrentStage
== ISCSI_SECURITY_NEGOTIATION
);
236 ASSERT (Conn
->RspQue
.BufNum
!= 0);
238 Session
= Conn
->Session
;
239 AuthData
= &Session
->AuthData
.CHAP
;
240 Len
= Conn
->RspQue
.BufSize
;
241 Data
= AllocateZeroPool (Len
);
243 return EFI_OUT_OF_RESOURCES
;
247 // Copy the data in case the data spans over multiple PDUs.
249 NetbufQueCopy (&Conn
->RspQue
, 0, Len
, Data
);
252 // Build the key-value list from the data segment of the Login Response.
254 KeyValueList
= IScsiBuildKeyValueList ((CHAR8
*)Data
, Len
);
255 if (KeyValueList
== NULL
) {
256 Status
= EFI_OUT_OF_RESOURCES
;
260 Status
= EFI_PROTOCOL_ERROR
;
262 switch (Conn
->AuthStep
) {
263 case ISCSI_AUTH_INITIAL
:
265 // The first Login Response.
267 Value
= IScsiGetValueByKeyFromList (
269 ISCSI_KEY_TARGET_PORTAL_GROUP_TAG
275 Result
= IScsiNetNtoi (Value
);
276 if (Result
> 0xFFFF) {
280 Session
->TargetPortalGroupTag
= (UINT16
)Result
;
282 Value
= IScsiGetValueByKeyFromList (
284 ISCSI_KEY_AUTH_METHOD
291 // Initiator mandates CHAP authentication but target replies without
292 // "CHAP", or initiator suggets "None" but target replies with some kind of
295 if (Session
->AuthType
== ISCSI_AUTH_TYPE_NONE
) {
296 if (AsciiStrCmp (Value
, ISCSI_KEY_VALUE_NONE
) != 0) {
299 } else if (Session
->AuthType
== ISCSI_AUTH_TYPE_CHAP
) {
300 if (AsciiStrCmp (Value
, ISCSI_AUTH_METHOD_CHAP
) != 0) {
308 // Transit to CHAP step one.
310 Conn
->AuthStep
= ISCSI_CHAP_STEP_ONE
;
311 Status
= EFI_SUCCESS
;
314 case ISCSI_CHAP_STEP_TWO
:
316 // The Target replies with CHAP_A=<A> CHAP_I=<I> CHAP_C=<C>
318 Value
= IScsiGetValueByKeyFromList (
320 ISCSI_KEY_CHAP_ALGORITHM
326 Algorithm
= IScsiNetNtoi (Value
);
327 for (HashIndex
= 0; HashIndex
< ARRAY_SIZE (mChapHash
); HashIndex
++) {
328 if (Algorithm
== mChapHash
[HashIndex
].Algorithm
) {
333 if (HashIndex
== ARRAY_SIZE (mChapHash
)) {
335 // Unsupported algorithm is chosen by target.
341 // Remember the target's chosen hash algorithm.
343 ASSERT (AuthData
->Hash
== NULL
);
344 AuthData
->Hash
= &mChapHash
[HashIndex
];
346 Identifier
= IScsiGetValueByKeyFromList (
348 ISCSI_KEY_CHAP_IDENTIFIER
350 if (Identifier
== NULL
) {
354 Challenge
= IScsiGetValueByKeyFromList (
356 ISCSI_KEY_CHAP_CHALLENGE
358 if (Challenge
== NULL
) {
363 // Process the CHAP identifier and CHAP Challenge from Target.
364 // Calculate Response value.
366 Result
= IScsiNetNtoi (Identifier
);
371 AuthData
->InIdentifier
= (UINT32
)Result
;
372 AuthData
->InChallengeLength
= (UINT32
)sizeof (AuthData
->InChallenge
);
373 Status
= IScsiHexToBin (
374 (UINT8
*)AuthData
->InChallenge
,
375 &AuthData
->InChallengeLength
,
378 if (EFI_ERROR (Status
)) {
379 Status
= EFI_PROTOCOL_ERROR
;
383 Status
= IScsiCHAPCalculateResponse (
384 AuthData
->InIdentifier
,
385 AuthData
->AuthConfig
->CHAPSecret
,
386 (UINT32
)AsciiStrLen (AuthData
->AuthConfig
->CHAPSecret
),
387 AuthData
->InChallenge
,
388 AuthData
->InChallengeLength
,
390 AuthData
->CHAPResponse
394 // Transit to next step.
396 Conn
->AuthStep
= ISCSI_CHAP_STEP_THREE
;
399 case ISCSI_CHAP_STEP_THREE
:
401 // One way CHAP authentication and the target would like to
404 Status
= EFI_SUCCESS
;
407 case ISCSI_CHAP_STEP_FOUR
:
408 ASSERT (AuthData
->AuthConfig
->CHAPType
== ISCSI_CHAP_MUTUAL
);
410 // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.
412 Name
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_CHAP_NAME
);
417 Response
= IScsiGetValueByKeyFromList (
419 ISCSI_KEY_CHAP_RESPONSE
421 if (Response
== NULL
) {
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
;
434 // Check the CHAP Name and Response replied by Target.
436 Status
= IScsiCHAPAuthTarget (AuthData
, TargetRsp
);
445 if (KeyValueList
!= NULL
) {
446 IScsiFreeKeyValueList (KeyValueList
);
455 This function fills the CHAP authentication information into the login PDU
456 during the security negotiation stage in the iSCSI connection login.
458 @param[in] Conn The iSCSI connection.
459 @param[in, out] Pdu The PDU to send out.
461 @retval EFI_SUCCESS All check passed and the phase-related CHAP
462 authentication info is filled into the iSCSI
464 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
465 @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
470 IN ISCSI_CONNECTION
*Conn
,
475 ISCSI_SESSION
*Session
;
476 ISCSI_LOGIN_REQUEST
*LoginReq
;
477 ISCSI_CHAP_AUTH_DATA
*AuthData
;
484 EFI_STATUS BinToHexStatus
;
486 ASSERT (Conn
->CurrentStage
== ISCSI_SECURITY_NEGOTIATION
);
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
;
495 Status
= EFI_SUCCESS
;
497 RspLen
= 2 * ISCSI_CHAP_MAX_DIGEST_SIZE
+ 3;
498 Response
= AllocateZeroPool (RspLen
);
499 if (Response
== NULL
) {
500 return EFI_OUT_OF_RESOURCES
;
503 ChallengeLen
= 2 * ISCSI_CHAP_MAX_DIGEST_SIZE
+ 3;
504 Challenge
= AllocateZeroPool (ChallengeLen
);
505 if (Challenge
== NULL
) {
507 return EFI_OUT_OF_RESOURCES
;
510 switch (Conn
->AuthStep
) {
511 case ISCSI_AUTH_INITIAL
:
513 // It's the initial Login Request. Fill in the key=value pairs mandatory
514 // for the initial Login Request.
516 IScsiAddKeyValuePair (
518 ISCSI_KEY_INITIATOR_NAME
,
519 mPrivate
->InitiatorName
521 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_SESSION_TYPE
, "Normal");
522 IScsiAddKeyValuePair (
524 ISCSI_KEY_TARGET_NAME
,
525 Session
->ConfigData
->SessionConfigData
.TargetName
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
);
532 Value
= ISCSI_AUTH_METHOD_CHAP
;
535 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_AUTH_METHOD
, Value
);
539 case ISCSI_CHAP_STEP_ONE
:
541 // First step, send the Login Request with CHAP_A=<A1,A2...> key-value
544 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_ALGORITHM
, mChapHashListString
);
546 Conn
->AuthStep
= ISCSI_CHAP_STEP_TWO
;
549 case ISCSI_CHAP_STEP_THREE
:
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
557 IScsiAddKeyValuePair (
560 (CHAR8
*)&AuthData
->AuthConfig
->CHAPName
565 ASSERT (AuthData
->Hash
!= NULL
);
566 BinToHexStatus
= IScsiBinToHex (
567 (UINT8
*)AuthData
->CHAPResponse
,
568 AuthData
->Hash
->DigestSize
,
572 ASSERT_EFI_ERROR (BinToHexStatus
);
573 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_RESPONSE
, Response
);
575 if (AuthData
->AuthConfig
->CHAPType
== ISCSI_CHAP_MUTUAL
) {
579 IScsiGenRandom ((UINT8
*)&AuthData
->OutIdentifier
, 1);
580 AsciiSPrint (ValueStr
, sizeof (ValueStr
), "%d", AuthData
->OutIdentifier
);
581 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_IDENTIFIER
, ValueStr
);
586 (UINT8
*)AuthData
->OutChallenge
,
587 AuthData
->Hash
->DigestSize
589 BinToHexStatus
= IScsiBinToHex (
590 (UINT8
*)AuthData
->OutChallenge
,
591 AuthData
->Hash
->DigestSize
,
595 ASSERT_EFI_ERROR (BinToHexStatus
);
596 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_CHALLENGE
, Challenge
);
598 Conn
->AuthStep
= ISCSI_CHAP_STEP_FOUR
;
602 // Set the stage transition flag.
604 ISCSI_SET_FLAG (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
608 Status
= EFI_PROTOCOL_ERROR
;
613 FreePool (Challenge
);
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.
622 This function sanity-checks the internal table of supported CHAP hashing
626 IScsiCHAPInitHashList (
633 CONST CHAP_HASH
*Hash
;
636 Position
= mChapHashListString
;
637 Left
= sizeof (mChapHashListString
);
638 for (HashIndex
= 0; HashIndex
< ARRAY_SIZE (mChapHash
); HashIndex
++) {
639 Hash
= &mChapHash
[HashIndex
];
642 // Format the next hash identifier.
644 // Assert that we can format at least one non-NUL character, i.e. that we
645 // can progress. Truncation is checked after printing.
648 Printed
= AsciiSPrint (
652 (HashIndex
== 0) ? "" : ",",
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.
663 ASSERT (Printed
+ 1 < Left
);
669 // Sanity-check the digest size for Hash.
671 ASSERT (Hash
->DigestSize
<= ISCSI_CHAP_MAX_DIGEST_SIZE
);