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
,
27 // Keep the deprecated MD5 entry at the end of the array (making MD5 the
28 // least preferred choice of the initiator).
31 ISCSI_CHAP_ALGORITHM_MD5
,
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.
45 STATIC CHAR8 mChapHashListString
[
46 3 + // UINT8 identifier in
48 (1 + 3) * (ARRAY_SIZE (mChapHash
) - 1) + // comma prepended for
51 1 + // extra character for
58 Initiator calculates its own expected hash value.
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.
71 @retval EFI_SUCCESS The expected hash value was calculatedly
73 @retval EFI_PROTOCOL_ERROR The length of the secret should be at least
74 the length of the hash value for the hashing
76 @retval EFI_PROTOCOL_ERROR Hash operation fails.
77 @retval EFI_OUT_OF_RESOURCES Failure to allocate resource to complete
82 IScsiCHAPCalculateResponse (
83 IN UINT32 ChapIdentifier
,
85 IN UINT32 SecretLength
,
86 IN UINT8
*ChapChallenge
,
87 IN UINT32 ChallengeLength
,
88 IN CONST CHAP_HASH
*Hash
,
89 OUT UINT8
*ChapResponse
97 if (SecretLength
< ISCSI_CHAP_SECRET_MIN_LEN
) {
98 return EFI_PROTOCOL_ERROR
;
101 ASSERT (Hash
!= NULL
);
103 ContextSize
= Hash
->GetContextSize ();
104 Ctx
= AllocatePool (ContextSize
);
106 return EFI_OUT_OF_RESOURCES
;
109 Status
= EFI_PROTOCOL_ERROR
;
111 if (!Hash
->Init (Ctx
)) {
116 // Hash Identifier - Only calculate 1 byte data (RFC1994)
118 IdByte
[0] = (CHAR8
) ChapIdentifier
;
119 if (!Hash
->Update (Ctx
, IdByte
, 1)) {
126 if (!Hash
->Update (Ctx
, ChapSecret
, SecretLength
)) {
131 // Hash Challenge received from Target
133 if (!Hash
->Update (Ctx
, ChapChallenge
, ChallengeLength
)) {
137 if (Hash
->Final (Ctx
, ChapResponse
)) {
138 Status
= EFI_SUCCESS
;
147 The initiator checks the CHAP response replied by target against its own
148 calculation of the expected hash value.
150 @param[in] AuthData iSCSI CHAP authentication data.
151 @param[in] TargetResponse The response from target.
153 @retval EFI_SUCCESS The response from target passed
155 @retval EFI_SECURITY_VIOLATION The response from target was not expected
157 @retval Others Other errors as indicated.
161 IScsiCHAPAuthTarget (
162 IN ISCSI_CHAP_AUTH_DATA
*AuthData
,
163 IN UINT8
*TargetResponse
168 UINT8 VerifyRsp
[ISCSI_CHAP_MAX_DIGEST_SIZE
];
171 Status
= EFI_SUCCESS
;
173 SecretSize
= (UINT32
) AsciiStrLen (AuthData
->AuthConfig
->ReverseCHAPSecret
);
175 ASSERT (AuthData
->Hash
!= NULL
);
177 Status
= IScsiCHAPCalculateResponse (
178 AuthData
->OutIdentifier
,
179 AuthData
->AuthConfig
->ReverseCHAPSecret
,
181 AuthData
->OutChallenge
,
182 AuthData
->Hash
->DigestSize
, // ChallengeLength
187 Mismatch
= CompareMem (
190 AuthData
->Hash
->DigestSize
193 Status
= EFI_SECURITY_VIOLATION
;
201 This function checks the received iSCSI Login Response during the security
204 @param[in] Conn The iSCSI connection.
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.
213 IScsiCHAPOnRspReceived (
214 IN ISCSI_CONNECTION
*Conn
218 ISCSI_SESSION
*Session
;
219 ISCSI_CHAP_AUTH_DATA
*AuthData
;
223 LIST_ENTRY
*KeyValueList
;
229 UINT8 TargetRsp
[ISCSI_CHAP_MAX_DIGEST_SIZE
];
234 ASSERT (Conn
->CurrentStage
== ISCSI_SECURITY_NEGOTIATION
);
235 ASSERT (Conn
->RspQue
.BufNum
!= 0);
237 Session
= Conn
->Session
;
238 AuthData
= &Session
->AuthData
.CHAP
;
239 Len
= Conn
->RspQue
.BufSize
;
240 Data
= AllocateZeroPool (Len
);
242 return EFI_OUT_OF_RESOURCES
;
245 // Copy the data in case the data spans over multiple PDUs.
247 NetbufQueCopy (&Conn
->RspQue
, 0, Len
, Data
);
250 // Build the key-value list from the data segment of the Login Response.
252 KeyValueList
= IScsiBuildKeyValueList ((CHAR8
*) Data
, Len
);
253 if (KeyValueList
== NULL
) {
254 Status
= EFI_OUT_OF_RESOURCES
;
258 Status
= EFI_PROTOCOL_ERROR
;
260 switch (Conn
->AuthStep
) {
261 case ISCSI_AUTH_INITIAL
:
263 // The first Login Response.
265 Value
= IScsiGetValueByKeyFromList (
267 ISCSI_KEY_TARGET_PORTAL_GROUP_TAG
273 Result
= IScsiNetNtoi (Value
);
274 if (Result
> 0xFFFF) {
278 Session
->TargetPortalGroupTag
= (UINT16
) Result
;
280 Value
= IScsiGetValueByKeyFromList (
282 ISCSI_KEY_AUTH_METHOD
288 // Initiator mandates CHAP authentication but target replies without
289 // "CHAP", or initiator suggets "None" but target replies with some kind of
292 if (Session
->AuthType
== ISCSI_AUTH_TYPE_NONE
) {
293 if (AsciiStrCmp (Value
, ISCSI_KEY_VALUE_NONE
) != 0) {
296 } else if (Session
->AuthType
== ISCSI_AUTH_TYPE_CHAP
) {
297 if (AsciiStrCmp (Value
, ISCSI_AUTH_METHOD_CHAP
) != 0) {
305 // Transit to CHAP step one.
307 Conn
->AuthStep
= ISCSI_CHAP_STEP_ONE
;
308 Status
= EFI_SUCCESS
;
311 case ISCSI_CHAP_STEP_TWO
:
313 // The Target replies with CHAP_A=<A> CHAP_I=<I> CHAP_C=<C>
315 Value
= IScsiGetValueByKeyFromList (
317 ISCSI_KEY_CHAP_ALGORITHM
323 Algorithm
= IScsiNetNtoi (Value
);
324 for (HashIndex
= 0; HashIndex
< ARRAY_SIZE (mChapHash
); HashIndex
++) {
325 if (Algorithm
== mChapHash
[HashIndex
].Algorithm
) {
329 if (HashIndex
== ARRAY_SIZE (mChapHash
)) {
331 // Unsupported algorithm is chosen by target.
336 // Remember the target's chosen hash algorithm.
338 ASSERT (AuthData
->Hash
== NULL
);
339 AuthData
->Hash
= &mChapHash
[HashIndex
];
341 Identifier
= IScsiGetValueByKeyFromList (
343 ISCSI_KEY_CHAP_IDENTIFIER
345 if (Identifier
== NULL
) {
349 Challenge
= IScsiGetValueByKeyFromList (
351 ISCSI_KEY_CHAP_CHALLENGE
353 if (Challenge
== NULL
) {
357 // Process the CHAP identifier and CHAP Challenge from Target.
358 // Calculate Response value.
360 Result
= IScsiNetNtoi (Identifier
);
365 AuthData
->InIdentifier
= (UINT32
) Result
;
366 AuthData
->InChallengeLength
= (UINT32
) sizeof (AuthData
->InChallenge
);
367 Status
= IScsiHexToBin (
368 (UINT8
*) AuthData
->InChallenge
,
369 &AuthData
->InChallengeLength
,
372 if (EFI_ERROR (Status
)) {
373 Status
= EFI_PROTOCOL_ERROR
;
376 Status
= IScsiCHAPCalculateResponse (
377 AuthData
->InIdentifier
,
378 AuthData
->AuthConfig
->CHAPSecret
,
379 (UINT32
) AsciiStrLen (AuthData
->AuthConfig
->CHAPSecret
),
380 AuthData
->InChallenge
,
381 AuthData
->InChallengeLength
,
383 AuthData
->CHAPResponse
387 // Transit to next step.
389 Conn
->AuthStep
= ISCSI_CHAP_STEP_THREE
;
392 case ISCSI_CHAP_STEP_THREE
:
394 // One way CHAP authentication and the target would like to
397 Status
= EFI_SUCCESS
;
400 case ISCSI_CHAP_STEP_FOUR
:
401 ASSERT (AuthData
->AuthConfig
->CHAPType
== ISCSI_CHAP_MUTUAL
);
403 // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.
405 Name
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_CHAP_NAME
);
410 Response
= IScsiGetValueByKeyFromList (
412 ISCSI_KEY_CHAP_RESPONSE
414 if (Response
== NULL
) {
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
;
427 // Check the CHAP Name and Response replied by Target.
429 Status
= IScsiCHAPAuthTarget (AuthData
, TargetRsp
);
438 if (KeyValueList
!= NULL
) {
439 IScsiFreeKeyValueList (KeyValueList
);
449 This function fills the CHAP authentication information into the login PDU
450 during the security negotiation stage in the iSCSI connection login.
452 @param[in] Conn The iSCSI connection.
453 @param[in, out] Pdu The PDU to send out.
455 @retval EFI_SUCCESS All check passed and the phase-related CHAP
456 authentication info is filled into the iSCSI
458 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
459 @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
464 IN ISCSI_CONNECTION
*Conn
,
469 ISCSI_SESSION
*Session
;
470 ISCSI_LOGIN_REQUEST
*LoginReq
;
471 ISCSI_CHAP_AUTH_DATA
*AuthData
;
478 EFI_STATUS BinToHexStatus
;
480 ASSERT (Conn
->CurrentStage
== ISCSI_SECURITY_NEGOTIATION
);
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
;
488 Status
= EFI_SUCCESS
;
490 RspLen
= 2 * ISCSI_CHAP_MAX_DIGEST_SIZE
+ 3;
491 Response
= AllocateZeroPool (RspLen
);
492 if (Response
== NULL
) {
493 return EFI_OUT_OF_RESOURCES
;
496 ChallengeLen
= 2 * ISCSI_CHAP_MAX_DIGEST_SIZE
+ 3;
497 Challenge
= AllocateZeroPool (ChallengeLen
);
498 if (Challenge
== NULL
) {
500 return EFI_OUT_OF_RESOURCES
;
503 switch (Conn
->AuthStep
) {
504 case ISCSI_AUTH_INITIAL
:
506 // It's the initial Login Request. Fill in the key=value pairs mandatory
507 // for the initial Login Request.
509 IScsiAddKeyValuePair (
511 ISCSI_KEY_INITIATOR_NAME
,
512 mPrivate
->InitiatorName
514 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_SESSION_TYPE
, "Normal");
515 IScsiAddKeyValuePair (
517 ISCSI_KEY_TARGET_NAME
,
518 Session
->ConfigData
->SessionConfigData
.TargetName
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
);
525 Value
= ISCSI_AUTH_METHOD_CHAP
;
528 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_AUTH_METHOD
, Value
);
532 case ISCSI_CHAP_STEP_ONE
:
534 // First step, send the Login Request with CHAP_A=<A1,A2...> key-value
537 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_ALGORITHM
, mChapHashListString
);
539 Conn
->AuthStep
= ISCSI_CHAP_STEP_TWO
;
542 case ISCSI_CHAP_STEP_THREE
:
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
550 IScsiAddKeyValuePair (
553 (CHAR8
*) &AuthData
->AuthConfig
->CHAPName
558 ASSERT (AuthData
->Hash
!= NULL
);
559 BinToHexStatus
= IScsiBinToHex (
560 (UINT8
*) AuthData
->CHAPResponse
,
561 AuthData
->Hash
->DigestSize
,
565 ASSERT_EFI_ERROR (BinToHexStatus
);
566 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_RESPONSE
, Response
);
568 if (AuthData
->AuthConfig
->CHAPType
== ISCSI_CHAP_MUTUAL
) {
572 IScsiGenRandom ((UINT8
*) &AuthData
->OutIdentifier
, 1);
573 AsciiSPrint (ValueStr
, sizeof (ValueStr
), "%d", AuthData
->OutIdentifier
);
574 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_IDENTIFIER
, ValueStr
);
579 (UINT8
*) AuthData
->OutChallenge
,
580 AuthData
->Hash
->DigestSize
582 BinToHexStatus
= IScsiBinToHex (
583 (UINT8
*) AuthData
->OutChallenge
,
584 AuthData
->Hash
->DigestSize
,
588 ASSERT_EFI_ERROR (BinToHexStatus
);
589 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_CHALLENGE
, Challenge
);
591 Conn
->AuthStep
= ISCSI_CHAP_STEP_FOUR
;
594 // Set the stage transition flag.
596 ISCSI_SET_FLAG (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
600 Status
= EFI_PROTOCOL_ERROR
;
605 FreePool (Challenge
);
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.
614 This function sanity-checks the internal table of supported CHAP hashing
618 IScsiCHAPInitHashList (
625 CONST CHAP_HASH
*Hash
;
628 Position
= mChapHashListString
;
629 Left
= sizeof (mChapHashListString
);
630 for (HashIndex
= 0; HashIndex
< ARRAY_SIZE (mChapHash
); HashIndex
++) {
631 Hash
= &mChapHash
[HashIndex
];
634 // Format the next hash identifier.
636 // Assert that we can format at least one non-NUL character, i.e. that we
637 // can progress. Truncation is checked after printing.
640 Printed
= AsciiSPrint (
644 (HashIndex
== 0) ? "" : ",",
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.
655 ASSERT (Printed
+ 1 < Left
);
661 // Sanity-check the digest size for Hash.
663 ASSERT (Hash
->DigestSize
<= ISCSI_CHAP_MAX_DIGEST_SIZE
);