--- /dev/null
+/** @file\r
+ This file is for Challenge-Handshake Authentication Protocol (CHAP) Configuration.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IScsiImpl.h"\r
+\r
+/**\r
+ Initator caculates its own expected hash value.\r
+ \r
+ @param[in] ChapIdentifier iSCSI CHAP identifier sent by authenticator.\r
+ @param[in] ChapSecret iSCSI CHAP secret of the authenticator.\r
+ @param[in] SecretLength The length of iSCSI CHAP secret.\r
+ @param[in] ChapChallenge The challenge message sent by authenticator.\r
+ @param[in] ChallengeLength The length of iSCSI CHAP challenge message.\r
+ @param[out] ChapResponse The calculation of the expected hash value.\r
+ \r
+ @retval EFI_SUCCESS The expected hash value was caculatedly successfully.\r
+ @retval EFI_PROTOCOL_ERROR The length of the secret should be at least the\r
+ length of the hash value for the hashing algorithm chosen.\r
+ @retval EFI_PROTOCOL_ERROR MD5 hash operation fail.\r
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete MD5.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiCHAPCalculateResponse (\r
+ IN UINT32 ChapIdentifier,\r
+ IN CHAR8 *ChapSecret,\r
+ IN UINT32 SecretLength,\r
+ IN UINT8 *ChapChallenge,\r
+ IN UINT32 ChallengeLength,\r
+ OUT UINT8 *ChapResponse\r
+ )\r
+{\r
+ UINTN Md5ContextSize;\r
+ VOID *Md5Ctx;\r
+ CHAR8 IdByte[1];\r
+ EFI_STATUS Status;\r
+\r
+ if (SecretLength < ISCSI_CHAP_SECRET_MIN_LEN) {\r
+ return EFI_PROTOCOL_ERROR;\r
+ }\r
+\r
+ Md5ContextSize = Md5GetContextSize ();\r
+ Md5Ctx = AllocatePool (Md5ContextSize);\r
+ if (Md5Ctx == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Status = EFI_PROTOCOL_ERROR;\r
+\r
+ if (!Md5Init (Md5Ctx)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Hash Identifier - Only calculate 1 byte data (RFC1994)\r
+ //\r
+ IdByte[0] = (CHAR8) ChapIdentifier;\r
+ if (!Md5Update (Md5Ctx, IdByte, 1)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Hash Secret\r
+ //\r
+ if (!Md5Update (Md5Ctx, ChapSecret, SecretLength)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Hash Challenge received from Target\r
+ //\r
+ if (!Md5Update (Md5Ctx, ChapChallenge, ChallengeLength)) {\r
+ goto Exit;\r
+ }\r
+\r
+ if (Md5Final (Md5Ctx, ChapResponse)) {\r
+ Status = EFI_SUCCESS;\r
+ }\r
+\r
+Exit:\r
+ FreePool (Md5Ctx);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The initator checks the CHAP response replied by target against its own\r
+ calculation of the expected hash value. \r
+ \r
+ @param[in] AuthData iSCSI CHAP authentication data. \r
+ @param[in] TargetResponse The response from target. \r
+\r
+ @retval EFI_SUCCESS The response from target passed authentication.\r
+ @retval EFI_SECURITY_VIOLATION The response from target was not expected value.\r
+ @retval Others Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiCHAPAuthTarget (\r
+ IN ISCSI_CHAP_AUTH_DATA *AuthData,\r
+ IN UINT8 *TargetResponse\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT32 SecretSize;\r
+ UINT8 VerifyRsp[ISCSI_CHAP_RSP_LEN];\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ SecretSize = (UINT32) AsciiStrLen (AuthData->AuthConfig->ReverseCHAPSecret);\r
+ Status = IScsiCHAPCalculateResponse (\r
+ AuthData->OutIdentifier,\r
+ AuthData->AuthConfig->ReverseCHAPSecret,\r
+ SecretSize,\r
+ AuthData->OutChallenge,\r
+ AuthData->OutChallengeLength,\r
+ VerifyRsp\r
+ );\r
+\r
+ if (CompareMem (VerifyRsp, TargetResponse, ISCSI_CHAP_RSP_LEN) != 0) {\r
+ Status = EFI_SECURITY_VIOLATION;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function checks the received iSCSI Login Response during the security\r
+ negotiation stage.\r
+\r
+ @param[in] Conn The iSCSI connection.\r
+\r
+ @retval EFI_SUCCESS The Login Response passed the CHAP validation.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+ @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.\r
+ @retval Others Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiCHAPOnRspReceived (\r
+ IN ISCSI_CONNECTION *Conn\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ ISCSI_SESSION *Session;\r
+ ISCSI_CHAP_AUTH_DATA *AuthData;\r
+ CHAR8 *Value;\r
+ UINT8 *Data;\r
+ UINT32 Len;\r
+ LIST_ENTRY *KeyValueList;\r
+ UINTN Algorithm;\r
+ CHAR8 *Identifier;\r
+ CHAR8 *Challenge;\r
+ CHAR8 *Name;\r
+ CHAR8 *Response;\r
+ UINT8 TargetRsp[ISCSI_CHAP_RSP_LEN];\r
+ UINT32 RspLen;\r
+ UINTN Result;\r
+\r
+ ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);\r
+ ASSERT (Conn->RspQue.BufNum != 0);\r
+\r
+ Session = Conn->Session;\r
+ AuthData = &Session->AuthData.CHAP;\r
+ Len = Conn->RspQue.BufSize;\r
+ Data = AllocateZeroPool (Len);\r
+ if (Data == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ //\r
+ // Copy the data in case the data spans over multiple PDUs.\r
+ //\r
+ NetbufQueCopy (&Conn->RspQue, 0, Len, Data);\r
+\r
+ //\r
+ // Build the key-value list from the data segment of the Login Response.\r
+ //\r
+ KeyValueList = IScsiBuildKeyValueList ((CHAR8 *) Data, Len);\r
+ if (KeyValueList == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = EFI_PROTOCOL_ERROR;\r
+\r
+ switch (Conn->AuthStep) {\r
+ case ISCSI_AUTH_INITIAL:\r
+ //\r
+ // The first Login Response.\r
+ //\r
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG);\r
+ if (Value == NULL) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Result = IScsiNetNtoi (Value);\r
+ if (Result > 0xFFFF) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Session->TargetPortalGroupTag = (UINT16) Result;\r
+\r
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_AUTH_METHOD);\r
+ if (Value == NULL) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Initiator mandates CHAP authentication but target replies without "CHAP", or\r
+ // initiator suggets "None" but target replies with some kind of auth method.\r
+ //\r
+ if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {\r
+ if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) != 0) {\r
+ goto ON_EXIT;\r
+ }\r
+ } else if (Session->AuthType == ISCSI_AUTH_TYPE_CHAP) {\r
+ if (AsciiStrCmp (Value, ISCSI_AUTH_METHOD_CHAP) != 0) {\r
+ goto ON_EXIT;\r
+ }\r
+ } else {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Transit to CHAP step one.\r
+ //\r
+ Conn->AuthStep = ISCSI_CHAP_STEP_ONE;\r
+ Status = EFI_SUCCESS;\r
+ break;\r
+\r
+ case ISCSI_CHAP_STEP_TWO:\r
+ //\r
+ // The Target replies with CHAP_A=<A> CHAP_I=<I> CHAP_C=<C>\r
+ //\r
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_ALGORITHM);\r
+ if (Value == NULL) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Algorithm = IScsiNetNtoi (Value);\r
+ if (Algorithm != ISCSI_CHAP_ALGORITHM_MD5) {\r
+ //\r
+ // Unsupported algorithm is chosen by target.\r
+ //\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Identifier = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_IDENTIFIER);\r
+ if (Identifier == NULL) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Challenge = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_CHALLENGE);\r
+ if (Challenge == NULL) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Process the CHAP identifier and CHAP Challenge from Target.\r
+ // Calculate Response value.\r
+ // \r
+ Result = IScsiNetNtoi (Identifier);\r
+ if (Result > 0xFF) {\r
+ goto ON_EXIT;\r
+ } \r
+ \r
+ AuthData->InIdentifier = (UINT32) Result;\r
+ AuthData->InChallengeLength = ISCSI_CHAP_AUTH_MAX_LEN;\r
+ IScsiHexToBin ((UINT8 *) AuthData->InChallenge, &AuthData->InChallengeLength, Challenge);\r
+ Status = IScsiCHAPCalculateResponse (\r
+ AuthData->InIdentifier,\r
+ AuthData->AuthConfig->CHAPSecret,\r
+ (UINT32) AsciiStrLen (AuthData->AuthConfig->CHAPSecret),\r
+ AuthData->InChallenge,\r
+ AuthData->InChallengeLength,\r
+ AuthData->CHAPResponse\r
+ );\r
+\r
+ //\r
+ // Transit to next step.\r
+ //\r
+ Conn->AuthStep = ISCSI_CHAP_STEP_THREE;\r
+ break;\r
+\r
+ case ISCSI_CHAP_STEP_THREE:\r
+ //\r
+ // One way CHAP authentication and the target would like to\r
+ // authenticate us.\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ break;\r
+\r
+ case ISCSI_CHAP_STEP_FOUR:\r
+ ASSERT (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL);\r
+ //\r
+ // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.\r
+ //\r
+ Name = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_NAME);\r
+ if (Name == NULL) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Response = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_RESPONSE);\r
+ if (Response == NULL) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ RspLen = ISCSI_CHAP_RSP_LEN;\r
+ IScsiHexToBin (TargetRsp, &RspLen, Response);\r
+\r
+ //\r
+ // Check the CHAP Name and Response replied by Target.\r
+ //\r
+ Status = IScsiCHAPAuthTarget (AuthData, TargetRsp);\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ON_EXIT:\r
+\r
+ if (KeyValueList != NULL) {\r
+ IScsiFreeKeyValueList (KeyValueList);\r
+ } \r
+\r
+ FreePool (Data);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function fills the CHAP authentication information into the login PDU\r
+ during the security negotiation stage in the iSCSI connection login.\r
+\r
+ @param[in] Conn The iSCSI connection.\r
+ @param[in, out] Pdu The PDU to send out.\r
+\r
+ @retval EFI_SUCCESS All check passed and the phase-related CHAP\r
+ authentication info is filled into the iSCSI PDU.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+ @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiCHAPToSendReq (\r
+ IN ISCSI_CONNECTION *Conn,\r
+ IN OUT NET_BUF *Pdu\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ ISCSI_SESSION *Session;\r
+ ISCSI_LOGIN_REQUEST *LoginReq;\r
+ ISCSI_CHAP_AUTH_DATA *AuthData;\r
+ CHAR8 *Value;\r
+ CHAR8 ValueStr[256];\r
+ CHAR8 *Response;\r
+ UINT32 RspLen;\r
+ CHAR8 *Challenge;\r
+ UINT32 ChallengeLen;\r
+\r
+ ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);\r
+\r
+ Session = Conn->Session;\r
+ AuthData = &Session->AuthData.CHAP;\r
+ LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufGetByte (Pdu, 0, 0);\r
+ Status = EFI_SUCCESS;\r
+\r
+ RspLen = 2 * ISCSI_CHAP_RSP_LEN + 3;\r
+ Response = AllocateZeroPool (RspLen);\r
+ if (Response == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ ChallengeLen = 2 * ISCSI_CHAP_RSP_LEN + 3;\r
+ Challenge = AllocateZeroPool (ChallengeLen);\r
+ if (Challenge == NULL) {\r
+ FreePool (Response);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ switch (Conn->AuthStep) {\r
+ case ISCSI_AUTH_INITIAL:\r
+ //\r
+ // It's the initial Login Request. Fill in the key=value pairs mandatory\r
+ // for the initial Login Request.\r
+ //\r
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIATOR_NAME, mPrivate->InitiatorName);\r
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_SESSION_TYPE, "Normal");\r
+ IScsiAddKeyValuePair (\r
+ Pdu,\r
+ ISCSI_KEY_TARGET_NAME,\r
+ Session->ConfigData->SessionConfigData.TargetName\r
+ );\r
+\r
+ if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {\r
+ Value = ISCSI_KEY_VALUE_NONE;\r
+ ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);\r
+ } else {\r
+ Value = ISCSI_AUTH_METHOD_CHAP;\r
+ }\r
+\r
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_AUTH_METHOD, Value);\r
+\r
+ break;\r
+\r
+ case ISCSI_CHAP_STEP_ONE:\r
+ //\r
+ // First step, send the Login Request with CHAP_A=<A1,A2...> key-value pair.\r
+ //\r
+ AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", ISCSI_CHAP_ALGORITHM_MD5);\r
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, ValueStr);\r
+\r
+ Conn->AuthStep = ISCSI_CHAP_STEP_TWO;\r
+ break;\r
+\r
+ case ISCSI_CHAP_STEP_THREE:\r
+ //\r
+ // Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or\r
+ // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target authentication is\r
+ // required too.\r
+ //\r
+ // CHAP_N=<N>\r
+ //\r
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_NAME, (CHAR8 *) &AuthData->AuthConfig->CHAPName);\r
+ //\r
+ // CHAP_R=<R>\r
+ //\r
+ IScsiBinToHex ((UINT8 *) AuthData->CHAPResponse, ISCSI_CHAP_RSP_LEN, Response, &RspLen);\r
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_RESPONSE, Response);\r
+\r
+ if (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL) {\r
+ //\r
+ // CHAP_I=<I>\r
+ //\r
+ IScsiGenRandom ((UINT8 *) &AuthData->OutIdentifier, 1);\r
+ AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", AuthData->OutIdentifier);\r
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_IDENTIFIER, ValueStr);\r
+ //\r
+ // CHAP_C=<C>\r
+ //\r
+ IScsiGenRandom ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN);\r
+ AuthData->OutChallengeLength = ISCSI_CHAP_RSP_LEN;\r
+ IScsiBinToHex ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN, Challenge, &ChallengeLen);\r
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_CHALLENGE, Challenge);\r
+\r
+ Conn->AuthStep = ISCSI_CHAP_STEP_FOUR;\r
+ }\r
+ //\r
+ // Set the stage transition flag.\r
+ //\r
+ ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);\r
+ break;\r
+\r
+ default:\r
+ Status = EFI_PROTOCOL_ERROR;\r
+ break;\r
+ }\r
+\r
+ FreePool (Response);\r
+ FreePool (Challenge);\r
+\r
+ return Status;\r
+}\r