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
);
294 (UINT8
*) AuthData
->InChallenge
,
295 &AuthData
->InChallengeLength
,
298 Status
= IScsiCHAPCalculateResponse (
299 AuthData
->InIdentifier
,
300 AuthData
->AuthConfig
->CHAPSecret
,
301 (UINT32
) AsciiStrLen (AuthData
->AuthConfig
->CHAPSecret
),
302 AuthData
->InChallenge
,
303 AuthData
->InChallengeLength
,
304 AuthData
->CHAPResponse
308 // Transit to next step.
310 Conn
->AuthStep
= ISCSI_CHAP_STEP_THREE
;
313 case ISCSI_CHAP_STEP_THREE
:
315 // One way CHAP authentication and the target would like to
318 Status
= EFI_SUCCESS
;
321 case ISCSI_CHAP_STEP_FOUR
:
322 ASSERT (AuthData
->AuthConfig
->CHAPType
== ISCSI_CHAP_MUTUAL
);
324 // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.
326 Name
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_CHAP_NAME
);
331 Response
= IScsiGetValueByKeyFromList (
333 ISCSI_KEY_CHAP_RESPONSE
335 if (Response
== NULL
) {
339 RspLen
= ISCSI_CHAP_RSP_LEN
;
340 IScsiHexToBin (TargetRsp
, &RspLen
, Response
);
343 // Check the CHAP Name and Response replied by Target.
345 Status
= IScsiCHAPAuthTarget (AuthData
, TargetRsp
);
354 if (KeyValueList
!= NULL
) {
355 IScsiFreeKeyValueList (KeyValueList
);
365 This function fills the CHAP authentication information into the login PDU
366 during the security negotiation stage in the iSCSI connection login.
368 @param[in] Conn The iSCSI connection.
369 @param[in, out] Pdu The PDU to send out.
371 @retval EFI_SUCCESS All check passed and the phase-related CHAP
372 authentication info is filled into the iSCSI
374 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
375 @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
380 IN ISCSI_CONNECTION
*Conn
,
385 ISCSI_SESSION
*Session
;
386 ISCSI_LOGIN_REQUEST
*LoginReq
;
387 ISCSI_CHAP_AUTH_DATA
*AuthData
;
395 ASSERT (Conn
->CurrentStage
== ISCSI_SECURITY_NEGOTIATION
);
397 Session
= Conn
->Session
;
398 AuthData
= &Session
->AuthData
.CHAP
;
399 LoginReq
= (ISCSI_LOGIN_REQUEST
*) NetbufGetByte (Pdu
, 0, 0);
400 if (LoginReq
== NULL
) {
401 return EFI_PROTOCOL_ERROR
;
403 Status
= EFI_SUCCESS
;
405 RspLen
= 2 * ISCSI_CHAP_RSP_LEN
+ 3;
406 Response
= AllocateZeroPool (RspLen
);
407 if (Response
== NULL
) {
408 return EFI_OUT_OF_RESOURCES
;
411 ChallengeLen
= 2 * ISCSI_CHAP_RSP_LEN
+ 3;
412 Challenge
= AllocateZeroPool (ChallengeLen
);
413 if (Challenge
== NULL
) {
415 return EFI_OUT_OF_RESOURCES
;
418 switch (Conn
->AuthStep
) {
419 case ISCSI_AUTH_INITIAL
:
421 // It's the initial Login Request. Fill in the key=value pairs mandatory
422 // for the initial Login Request.
424 IScsiAddKeyValuePair (
426 ISCSI_KEY_INITIATOR_NAME
,
427 mPrivate
->InitiatorName
429 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_SESSION_TYPE
, "Normal");
430 IScsiAddKeyValuePair (
432 ISCSI_KEY_TARGET_NAME
,
433 Session
->ConfigData
->SessionConfigData
.TargetName
436 if (Session
->AuthType
== ISCSI_AUTH_TYPE_NONE
) {
437 Value
= ISCSI_KEY_VALUE_NONE
;
438 ISCSI_SET_FLAG (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
440 Value
= ISCSI_AUTH_METHOD_CHAP
;
443 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_AUTH_METHOD
, Value
);
447 case ISCSI_CHAP_STEP_ONE
:
449 // First step, send the Login Request with CHAP_A=<A1,A2...> key-value
452 AsciiSPrint (ValueStr
, sizeof (ValueStr
), "%d", ISCSI_CHAP_ALGORITHM_MD5
);
453 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_ALGORITHM
, ValueStr
);
455 Conn
->AuthStep
= ISCSI_CHAP_STEP_TWO
;
458 case ISCSI_CHAP_STEP_THREE
:
460 // Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or
461 // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target authentication is
466 IScsiAddKeyValuePair (
469 (CHAR8
*) &AuthData
->AuthConfig
->CHAPName
475 (UINT8
*) AuthData
->CHAPResponse
,
480 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_RESPONSE
, Response
);
482 if (AuthData
->AuthConfig
->CHAPType
== ISCSI_CHAP_MUTUAL
) {
486 IScsiGenRandom ((UINT8
*) &AuthData
->OutIdentifier
, 1);
487 AsciiSPrint (ValueStr
, sizeof (ValueStr
), "%d", AuthData
->OutIdentifier
);
488 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_IDENTIFIER
, ValueStr
);
492 IScsiGenRandom ((UINT8
*) AuthData
->OutChallenge
, ISCSI_CHAP_RSP_LEN
);
494 (UINT8
*) AuthData
->OutChallenge
,
499 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_CHAP_CHALLENGE
, Challenge
);
501 Conn
->AuthStep
= ISCSI_CHAP_STEP_FOUR
;
504 // Set the stage transition flag.
506 ISCSI_SET_FLAG (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
510 Status
= EFI_PROTOCOL_ERROR
;
515 FreePool (Challenge
);