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 Initiator calculates its own expected hash value.
15 @param[in] ChapIdentifier iSCSI CHAP identifier sent by authenticator.
16 @param[in] ChapSecret iSCSI CHAP secret of the authenticator.
17 @param[in] SecretLength The length of iSCSI CHAP secret.
18 @param[in] ChapChallenge The challenge message sent by authenticator.
19 @param[in] ChallengeLength The length of iSCSI CHAP challenge message.
20 @param[out] ChapResponse The calculation of the expected hash value.
22 @retval EFI_SUCCESS The expected hash value was calculatedly
24 @retval EFI_PROTOCOL_ERROR The length of the secret should be at least
25 the length of the hash value for the hashing
27 @retval EFI_PROTOCOL_ERROR MD5 hash operation fail.
28 @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete MD5.
32 IScsiCHAPCalculateResponse (
33 IN UINT32 ChapIdentifier
,
35 IN UINT32 SecretLength
,
36 IN UINT8
*ChapChallenge
,
37 IN UINT32 ChallengeLength
,
38 OUT UINT8
*ChapResponse
46 if (SecretLength
< ISCSI_CHAP_SECRET_MIN_LEN
) {
47 return EFI_PROTOCOL_ERROR
;
50 Md5ContextSize
= Md5GetContextSize ();
51 Md5Ctx
= AllocatePool (Md5ContextSize
);
53 return EFI_OUT_OF_RESOURCES
;
56 Status
= EFI_PROTOCOL_ERROR
;
58 if (!Md5Init (Md5Ctx
)) {
63 // Hash Identifier - Only calculate 1 byte data (RFC1994)
65 IdByte
[0] = (CHAR8
) ChapIdentifier
;
66 if (!Md5Update (Md5Ctx
, IdByte
, 1)) {
73 if (!Md5Update (Md5Ctx
, ChapSecret
, SecretLength
)) {
78 // Hash Challenge received from Target
80 if (!Md5Update (Md5Ctx
, ChapChallenge
, ChallengeLength
)) {
84 if (Md5Final (Md5Ctx
, ChapResponse
)) {
94 The initiator checks the CHAP response replied by target against its own
95 calculation of the expected hash value.
97 @param[in] AuthData iSCSI CHAP authentication data.
98 @param[in] TargetResponse The response from target.
100 @retval EFI_SUCCESS The response from target passed
102 @retval EFI_SECURITY_VIOLATION The response from target was not expected
104 @retval Others Other errors as indicated.
108 IScsiCHAPAuthTarget (
109 IN ISCSI_CHAP_AUTH_DATA
*AuthData
,
110 IN UINT8
*TargetResponse
115 UINT8 VerifyRsp
[ISCSI_CHAP_RSP_LEN
];
117 Status
= EFI_SUCCESS
;
119 SecretSize
= (UINT32
) AsciiStrLen (AuthData
->AuthConfig
->ReverseCHAPSecret
);
120 Status
= IScsiCHAPCalculateResponse (
121 AuthData
->OutIdentifier
,
122 AuthData
->AuthConfig
->ReverseCHAPSecret
,
124 AuthData
->OutChallenge
,
125 ISCSI_CHAP_RSP_LEN
, // ChallengeLength
129 if (CompareMem (VerifyRsp
, TargetResponse
, ISCSI_CHAP_RSP_LEN
) != 0) {
130 Status
= EFI_SECURITY_VIOLATION
;
138 This function checks the received iSCSI Login Response during the security
141 @param[in] Conn The iSCSI connection.
143 @retval EFI_SUCCESS The Login Response passed the CHAP validation.
144 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
145 @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
146 @retval Others Other errors as indicated.
150 IScsiCHAPOnRspReceived (
151 IN ISCSI_CONNECTION
*Conn
155 ISCSI_SESSION
*Session
;
156 ISCSI_CHAP_AUTH_DATA
*AuthData
;
160 LIST_ENTRY
*KeyValueList
;
166 UINT8 TargetRsp
[ISCSI_CHAP_RSP_LEN
];
170 ASSERT (Conn
->CurrentStage
== ISCSI_SECURITY_NEGOTIATION
);
171 ASSERT (Conn
->RspQue
.BufNum
!= 0);
173 Session
= Conn
->Session
;
174 AuthData
= &Session
->AuthData
.CHAP
;
175 Len
= Conn
->RspQue
.BufSize
;
176 Data
= AllocateZeroPool (Len
);
178 return EFI_OUT_OF_RESOURCES
;
181 // Copy the data in case the data spans over multiple PDUs.
183 NetbufQueCopy (&Conn
->RspQue
, 0, Len
, Data
);
186 // Build the key-value list from the data segment of the Login Response.
188 KeyValueList
= IScsiBuildKeyValueList ((CHAR8
*) Data
, Len
);
189 if (KeyValueList
== NULL
) {
190 Status
= EFI_OUT_OF_RESOURCES
;
194 Status
= EFI_PROTOCOL_ERROR
;
196 switch (Conn
->AuthStep
) {
197 case ISCSI_AUTH_INITIAL
:
199 // The first Login Response.
201 Value
= IScsiGetValueByKeyFromList (
203 ISCSI_KEY_TARGET_PORTAL_GROUP_TAG
209 Result
= IScsiNetNtoi (Value
);
210 if (Result
> 0xFFFF) {
214 Session
->TargetPortalGroupTag
= (UINT16
) Result
;
216 Value
= IScsiGetValueByKeyFromList (
218 ISCSI_KEY_AUTH_METHOD
224 // Initiator mandates CHAP authentication but target replies without
225 // "CHAP", or initiator suggets "None" but target replies with some kind of
228 if (Session
->AuthType
== ISCSI_AUTH_TYPE_NONE
) {
229 if (AsciiStrCmp (Value
, ISCSI_KEY_VALUE_NONE
) != 0) {
232 } else if (Session
->AuthType
== ISCSI_AUTH_TYPE_CHAP
) {
233 if (AsciiStrCmp (Value
, ISCSI_AUTH_METHOD_CHAP
) != 0) {
241 // Transit to CHAP step one.
243 Conn
->AuthStep
= ISCSI_CHAP_STEP_ONE
;
244 Status
= EFI_SUCCESS
;
247 case ISCSI_CHAP_STEP_TWO
:
249 // The Target replies with CHAP_A=<A> CHAP_I=<I> CHAP_C=<C>
251 Value
= IScsiGetValueByKeyFromList (
253 ISCSI_KEY_CHAP_ALGORITHM
259 Algorithm
= IScsiNetNtoi (Value
);
260 if (Algorithm
!= ISCSI_CHAP_ALGORITHM_MD5
) {
262 // Unsupported algorithm is chosen by target.
267 Identifier
= IScsiGetValueByKeyFromList (
269 ISCSI_KEY_CHAP_IDENTIFIER
271 if (Identifier
== NULL
) {
275 Challenge
= IScsiGetValueByKeyFromList (
277 ISCSI_KEY_CHAP_CHALLENGE
279 if (Challenge
== NULL
) {
283 // Process the CHAP identifier and CHAP Challenge from Target.
284 // Calculate Response value.
286 Result
= IScsiNetNtoi (Identifier
);
291 AuthData
->InIdentifier
= (UINT32
) Result
;
292 AuthData
->InChallengeLength
= (UINT32
) sizeof (AuthData
->InChallenge
);
293 Status
= IScsiHexToBin (
294 (UINT8
*) AuthData
->InChallenge
,
295 &AuthData
->InChallengeLength
,
298 if (EFI_ERROR (Status
)) {
299 Status
= EFI_PROTOCOL_ERROR
;
302 Status
= IScsiCHAPCalculateResponse (
303 AuthData
->InIdentifier
,
304 AuthData
->AuthConfig
->CHAPSecret
,
305 (UINT32
) AsciiStrLen (AuthData
->AuthConfig
->CHAPSecret
),
306 AuthData
->InChallenge
,
307 AuthData
->InChallengeLength
,
308 AuthData
->CHAPResponse
312 // Transit to next step.
314 Conn
->AuthStep
= ISCSI_CHAP_STEP_THREE
;
317 case ISCSI_CHAP_STEP_THREE
:
319 // One way CHAP authentication and the target would like to
322 Status
= EFI_SUCCESS
;
325 case ISCSI_CHAP_STEP_FOUR
:
326 ASSERT (AuthData
->AuthConfig
->CHAPType
== ISCSI_CHAP_MUTUAL
);
328 // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.
330 Name
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_CHAP_NAME
);
335 Response
= IScsiGetValueByKeyFromList (
337 ISCSI_KEY_CHAP_RESPONSE
339 if (Response
== NULL
) {
343 RspLen
= ISCSI_CHAP_RSP_LEN
;
344 Status
= IScsiHexToBin (TargetRsp
, &RspLen
, Response
);
345 if (EFI_ERROR (Status
) || RspLen
!= ISCSI_CHAP_RSP_LEN
) {
346 Status
= EFI_PROTOCOL_ERROR
;
351 // Check the CHAP Name and Response replied by Target.
353 Status
= IScsiCHAPAuthTarget (AuthData
, TargetRsp
);
362 if (KeyValueList
!= NULL
) {
363 IScsiFreeKeyValueList (KeyValueList
);
373 This function fills the CHAP authentication information into the login PDU
374 during the security negotiation stage in the iSCSI connection login.
376 @param[in] Conn The iSCSI connection.
377 @param[in, out] Pdu The PDU to send out.
379 @retval EFI_SUCCESS All check passed and the phase-related CHAP
380 authentication info is filled into the iSCSI
382 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
383 @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
388 IN ISCSI_CONNECTION
*Conn
,
393 ISCSI_SESSION
*Session
;
394 ISCSI_LOGIN_REQUEST
*LoginReq
;
395 ISCSI_CHAP_AUTH_DATA
*AuthData
;
402 EFI_STATUS BinToHexStatus
;
404 ASSERT (Conn
->CurrentStage
== ISCSI_SECURITY_NEGOTIATION
);
406 Session
= Conn
->Session
;
407 AuthData
= &Session
->AuthData
.CHAP
;
408 LoginReq
= (ISCSI_LOGIN_REQUEST
*) NetbufGetByte (Pdu
, 0, 0);
409 if (LoginReq
== NULL
) {
410 return EFI_PROTOCOL_ERROR
;
412 Status
= EFI_SUCCESS
;
414 RspLen
= 2 * ISCSI_CHAP_RSP_LEN
+ 3;
415 Response
= AllocateZeroPool (RspLen
);
416 if (Response
== NULL
) {
417 return EFI_OUT_OF_RESOURCES
;
420 ChallengeLen
= 2 * ISCSI_CHAP_RSP_LEN
+ 3;
421 Challenge
= AllocateZeroPool (ChallengeLen
);
422 if (Challenge
== NULL
) {
424 return EFI_OUT_OF_RESOURCES
;
427 switch (Conn
->AuthStep
) {
428 case ISCSI_AUTH_INITIAL
:
430 // It's the initial Login Request. Fill in the key=value pairs mandatory
431 // for the initial Login Request.
433 IScsiAddKeyValuePair (
435 ISCSI_KEY_INITIATOR_NAME
,
436 mPrivate
->InitiatorName
438 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_SESSION_TYPE
, "Normal");
439 IScsiAddKeyValuePair (
441 ISCSI_KEY_TARGET_NAME
,
442 Session
->ConfigData
->SessionConfigData
.TargetName
445 if (Session
->AuthType
== ISCSI_AUTH_TYPE_NONE
) {
446 Value
= ISCSI_KEY_VALUE_NONE
;
447 ISCSI_SET_FLAG (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
449 Value
= ISCSI_AUTH_METHOD_CHAP
;
452 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_AUTH_METHOD
, Value
);
456 case ISCSI_CHAP_STEP_ONE
:
458 // First step, send the Login Request with CHAP_A=<A1,A2...> key-value
461 AsciiSPrint (ValueStr
, sizeof (ValueStr
), "%d", ISCSI_CHAP_ALGORITHM_MD5
);
462 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_ALGORITHM
, ValueStr
);
464 Conn
->AuthStep
= ISCSI_CHAP_STEP_TWO
;
467 case ISCSI_CHAP_STEP_THREE
:
469 // Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or
470 // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target authentication is
475 IScsiAddKeyValuePair (
478 (CHAR8
*) &AuthData
->AuthConfig
->CHAPName
483 BinToHexStatus
= IScsiBinToHex (
484 (UINT8
*) AuthData
->CHAPResponse
,
489 ASSERT_EFI_ERROR (BinToHexStatus
);
490 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_RESPONSE
, Response
);
492 if (AuthData
->AuthConfig
->CHAPType
== ISCSI_CHAP_MUTUAL
) {
496 IScsiGenRandom ((UINT8
*) &AuthData
->OutIdentifier
, 1);
497 AsciiSPrint (ValueStr
, sizeof (ValueStr
), "%d", AuthData
->OutIdentifier
);
498 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_IDENTIFIER
, ValueStr
);
502 IScsiGenRandom ((UINT8
*) AuthData
->OutChallenge
, ISCSI_CHAP_RSP_LEN
);
503 BinToHexStatus
= IScsiBinToHex (
504 (UINT8
*) AuthData
->OutChallenge
,
509 ASSERT_EFI_ERROR (BinToHexStatus
);
510 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_CHALLENGE
, Challenge
);
512 Conn
->AuthStep
= ISCSI_CHAP_STEP_FOUR
;
515 // Set the stage transition flag.
517 ISCSI_SET_FLAG (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
521 Status
= EFI_PROTOCOL_ERROR
;
526 FreePool (Challenge
);