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
;
203 This function checks the received iSCSI Login Response during the security
206 @param[in] Conn The iSCSI connection.
208 @retval EFI_SUCCESS The Login Response passed the CHAP validation.
209 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
210 @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
211 @retval Others Other errors as indicated.
215 IScsiCHAPOnRspReceived (
216 IN ISCSI_CONNECTION
*Conn
220 ISCSI_SESSION
*Session
;
221 ISCSI_CHAP_AUTH_DATA
*AuthData
;
225 LIST_ENTRY
*KeyValueList
;
231 UINT8 TargetRsp
[ISCSI_CHAP_MAX_DIGEST_SIZE
];
236 ASSERT (Conn
->CurrentStage
== ISCSI_SECURITY_NEGOTIATION
);
237 ASSERT (Conn
->RspQue
.BufNum
!= 0);
239 Session
= Conn
->Session
;
240 AuthData
= &Session
->AuthData
.CHAP
;
241 Len
= Conn
->RspQue
.BufSize
;
242 Data
= AllocateZeroPool (Len
);
244 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
290 // Initiator mandates CHAP authentication but target replies without
291 // "CHAP", or initiator suggets "None" but target replies with some kind of
294 if (Session
->AuthType
== ISCSI_AUTH_TYPE_NONE
) {
295 if (AsciiStrCmp (Value
, ISCSI_KEY_VALUE_NONE
) != 0) {
298 } else if (Session
->AuthType
== ISCSI_AUTH_TYPE_CHAP
) {
299 if (AsciiStrCmp (Value
, ISCSI_AUTH_METHOD_CHAP
) != 0) {
307 // Transit to CHAP step one.
309 Conn
->AuthStep
= ISCSI_CHAP_STEP_ONE
;
310 Status
= EFI_SUCCESS
;
313 case ISCSI_CHAP_STEP_TWO
:
315 // The Target replies with CHAP_A=<A> CHAP_I=<I> CHAP_C=<C>
317 Value
= IScsiGetValueByKeyFromList (
319 ISCSI_KEY_CHAP_ALGORITHM
325 Algorithm
= IScsiNetNtoi (Value
);
326 for (HashIndex
= 0; HashIndex
< ARRAY_SIZE (mChapHash
); HashIndex
++) {
327 if (Algorithm
== mChapHash
[HashIndex
].Algorithm
) {
331 if (HashIndex
== ARRAY_SIZE (mChapHash
)) {
333 // Unsupported algorithm is chosen by target.
338 // Remember the target's chosen hash algorithm.
340 ASSERT (AuthData
->Hash
== NULL
);
341 AuthData
->Hash
= &mChapHash
[HashIndex
];
343 Identifier
= IScsiGetValueByKeyFromList (
345 ISCSI_KEY_CHAP_IDENTIFIER
347 if (Identifier
== NULL
) {
351 Challenge
= IScsiGetValueByKeyFromList (
353 ISCSI_KEY_CHAP_CHALLENGE
355 if (Challenge
== NULL
) {
359 // Process the CHAP identifier and CHAP Challenge from Target.
360 // Calculate Response value.
362 Result
= IScsiNetNtoi (Identifier
);
367 AuthData
->InIdentifier
= (UINT32
) Result
;
368 AuthData
->InChallengeLength
= (UINT32
) sizeof (AuthData
->InChallenge
);
369 Status
= IScsiHexToBin (
370 (UINT8
*) AuthData
->InChallenge
,
371 &AuthData
->InChallengeLength
,
374 if (EFI_ERROR (Status
)) {
375 Status
= EFI_PROTOCOL_ERROR
;
378 Status
= IScsiCHAPCalculateResponse (
379 AuthData
->InIdentifier
,
380 AuthData
->AuthConfig
->CHAPSecret
,
381 (UINT32
) AsciiStrLen (AuthData
->AuthConfig
->CHAPSecret
),
382 AuthData
->InChallenge
,
383 AuthData
->InChallengeLength
,
385 AuthData
->CHAPResponse
389 // Transit to next step.
391 Conn
->AuthStep
= ISCSI_CHAP_STEP_THREE
;
394 case ISCSI_CHAP_STEP_THREE
:
396 // One way CHAP authentication and the target would like to
399 Status
= EFI_SUCCESS
;
402 case ISCSI_CHAP_STEP_FOUR
:
403 ASSERT (AuthData
->AuthConfig
->CHAPType
== ISCSI_CHAP_MUTUAL
);
405 // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.
407 Name
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_CHAP_NAME
);
412 Response
= IScsiGetValueByKeyFromList (
414 ISCSI_KEY_CHAP_RESPONSE
416 if (Response
== NULL
) {
420 ASSERT (AuthData
->Hash
!= NULL
);
421 RspLen
= AuthData
->Hash
->DigestSize
;
422 Status
= IScsiHexToBin (TargetRsp
, &RspLen
, Response
);
423 if (EFI_ERROR (Status
) || RspLen
!= AuthData
->Hash
->DigestSize
) {
424 Status
= EFI_PROTOCOL_ERROR
;
429 // Check the CHAP Name and Response replied by Target.
431 Status
= IScsiCHAPAuthTarget (AuthData
, TargetRsp
);
440 if (KeyValueList
!= NULL
) {
441 IScsiFreeKeyValueList (KeyValueList
);
451 This function fills the CHAP authentication information into the login PDU
452 during the security negotiation stage in the iSCSI connection login.
454 @param[in] Conn The iSCSI connection.
455 @param[in, out] Pdu The PDU to send out.
457 @retval EFI_SUCCESS All check passed and the phase-related CHAP
458 authentication info is filled into the iSCSI
460 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
461 @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
466 IN ISCSI_CONNECTION
*Conn
,
471 ISCSI_SESSION
*Session
;
472 ISCSI_LOGIN_REQUEST
*LoginReq
;
473 ISCSI_CHAP_AUTH_DATA
*AuthData
;
480 EFI_STATUS BinToHexStatus
;
482 ASSERT (Conn
->CurrentStage
== ISCSI_SECURITY_NEGOTIATION
);
484 Session
= Conn
->Session
;
485 AuthData
= &Session
->AuthData
.CHAP
;
486 LoginReq
= (ISCSI_LOGIN_REQUEST
*) NetbufGetByte (Pdu
, 0, 0);
487 if (LoginReq
== NULL
) {
488 return EFI_PROTOCOL_ERROR
;
490 Status
= EFI_SUCCESS
;
492 RspLen
= 2 * ISCSI_CHAP_MAX_DIGEST_SIZE
+ 3;
493 Response
= AllocateZeroPool (RspLen
);
494 if (Response
== NULL
) {
495 return EFI_OUT_OF_RESOURCES
;
498 ChallengeLen
= 2 * ISCSI_CHAP_MAX_DIGEST_SIZE
+ 3;
499 Challenge
= AllocateZeroPool (ChallengeLen
);
500 if (Challenge
== NULL
) {
502 return EFI_OUT_OF_RESOURCES
;
505 switch (Conn
->AuthStep
) {
506 case ISCSI_AUTH_INITIAL
:
508 // It's the initial Login Request. Fill in the key=value pairs mandatory
509 // for the initial Login Request.
511 IScsiAddKeyValuePair (
513 ISCSI_KEY_INITIATOR_NAME
,
514 mPrivate
->InitiatorName
516 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_SESSION_TYPE
, "Normal");
517 IScsiAddKeyValuePair (
519 ISCSI_KEY_TARGET_NAME
,
520 Session
->ConfigData
->SessionConfigData
.TargetName
523 if (Session
->AuthType
== ISCSI_AUTH_TYPE_NONE
) {
524 Value
= ISCSI_KEY_VALUE_NONE
;
525 ISCSI_SET_FLAG (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
527 Value
= ISCSI_AUTH_METHOD_CHAP
;
530 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_AUTH_METHOD
, Value
);
534 case ISCSI_CHAP_STEP_ONE
:
536 // First step, send the Login Request with CHAP_A=<A1,A2...> key-value
539 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_ALGORITHM
, mChapHashListString
);
541 Conn
->AuthStep
= ISCSI_CHAP_STEP_TWO
;
544 case ISCSI_CHAP_STEP_THREE
:
546 // Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or
547 // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target authentication is
552 IScsiAddKeyValuePair (
555 (CHAR8
*) &AuthData
->AuthConfig
->CHAPName
560 ASSERT (AuthData
->Hash
!= NULL
);
561 BinToHexStatus
= IScsiBinToHex (
562 (UINT8
*) AuthData
->CHAPResponse
,
563 AuthData
->Hash
->DigestSize
,
567 ASSERT_EFI_ERROR (BinToHexStatus
);
568 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_RESPONSE
, Response
);
570 if (AuthData
->AuthConfig
->CHAPType
== ISCSI_CHAP_MUTUAL
) {
574 IScsiGenRandom ((UINT8
*) &AuthData
->OutIdentifier
, 1);
575 AsciiSPrint (ValueStr
, sizeof (ValueStr
), "%d", AuthData
->OutIdentifier
);
576 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_IDENTIFIER
, ValueStr
);
581 (UINT8
*) AuthData
->OutChallenge
,
582 AuthData
->Hash
->DigestSize
584 BinToHexStatus
= IScsiBinToHex (
585 (UINT8
*) AuthData
->OutChallenge
,
586 AuthData
->Hash
->DigestSize
,
590 ASSERT_EFI_ERROR (BinToHexStatus
);
591 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_CHALLENGE
, Challenge
);
593 Conn
->AuthStep
= ISCSI_CHAP_STEP_FOUR
;
596 // Set the stage transition flag.
598 ISCSI_SET_FLAG (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
602 Status
= EFI_PROTOCOL_ERROR
;
607 FreePool (Challenge
);
613 Initialize the CHAP_A=<A1,A2...> *value* string for the entire driver, to be
614 sent by the initiator in ISCSI_CHAP_STEP_ONE.
616 This function sanity-checks the internal table of supported CHAP hashing
620 IScsiCHAPInitHashList (
627 CONST CHAP_HASH
*Hash
;
630 Position
= mChapHashListString
;
631 Left
= sizeof (mChapHashListString
);
632 for (HashIndex
= 0; HashIndex
< ARRAY_SIZE (mChapHash
); HashIndex
++) {
633 Hash
= &mChapHash
[HashIndex
];
636 // Format the next hash identifier.
638 // Assert that we can format at least one non-NUL character, i.e. that we
639 // can progress. Truncation is checked after printing.
642 Printed
= AsciiSPrint (
646 (HashIndex
== 0) ? "" : ",",
650 // There's no way to differentiate between the "buffer filled to the brim,
651 // but not truncated" result and the "truncated" result of AsciiSPrint().
652 // This is why "mChapHashListString" has an extra byte allocated, and the
653 // reason why we use the less-than (rather than the less-than-or-equal-to)
654 // relational operator in the assertion below -- we enforce "no truncation"
655 // by excluding the "completely used up" case too.
657 ASSERT (Printed
+ 1 < Left
);
663 // Sanity-check the digest size for Hash.
665 ASSERT (Hash
->DigestSize
<= ISCSI_CHAP_MAX_DIGEST_SIZE
);