/** @file\r
- This file is for Challenge-Handshake Authentication Protocol (CHAP) Configuration.\r
+ This file is for Challenge-Handshake Authentication Protocol (CHAP)\r
+ 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
+Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
#include "IScsiImpl.h"\r
\r
+//\r
+// Supported CHAP hash algorithms, mapped to sets of BaseCryptLib APIs and\r
+// macros. CHAP_HASH structures at lower subscripts in the array are preferred\r
+// by the initiator.\r
+//\r
+STATIC CONST CHAP_HASH mChapHash[] = {\r
+ {\r
+ ISCSI_CHAP_ALGORITHM_MD5,\r
+ MD5_DIGEST_SIZE,\r
+ Md5GetContextSize,\r
+ Md5Init,\r
+ Md5Update,\r
+ Md5Final\r
+ },\r
+};\r
+\r
+//\r
+// Ordered list of mChapHash[*].Algorithm values. It is formatted for the\r
+// CHAP_A=<A1,A2...> value string, by the IScsiCHAPInitHashList() function. It\r
+// is sent by the initiator in ISCSI_CHAP_STEP_ONE.\r
+//\r
+STATIC CHAR8 mChapHashListString[\r
+ 3 + // UINT8 identifier in\r
+ // decimal\r
+ (1 + 3) * (ARRAY_SIZE (mChapHash) - 1) + // comma prepended for\r
+ // entries after the\r
+ // first\r
+ 1 + // extra character for\r
+ // AsciiSPrint()\r
+ // truncation check\r
+ 1 // terminating NUL\r
+ ];\r
+\r
/**\r
- Initator caculates its own expected hash value.\r
- \r
+ Initiator calculates 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[in] Hash Pointer to the CHAP_HASH structure that\r
+ determines the hashing algorithm to use. The\r
+ caller is responsible for making Hash point\r
+ to an "mChapHash" element.\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
+ @retval EFI_SUCCESS The expected hash value was calculatedly\r
+ successfully.\r
+ @retval EFI_PROTOCOL_ERROR The length of the secret should be at least\r
+ the length of the hash value for the hashing\r
+ algorithm chosen.\r
+ @retval EFI_PROTOCOL_ERROR Hash operation fails.\r
+ @retval EFI_OUT_OF_RESOURCES Failure to allocate resource to complete\r
+ hashing.\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
+ IN UINT32 ChapIdentifier,\r
+ IN CHAR8 *ChapSecret,\r
+ IN UINT32 SecretLength,\r
+ IN UINT8 *ChapChallenge,\r
+ IN UINT32 ChallengeLength,\r
+ IN CONST CHAP_HASH *Hash,\r
+ OUT UINT8 *ChapResponse\r
)\r
{\r
- UINTN Md5ContextSize;\r
- VOID *Md5Ctx;\r
+ UINTN ContextSize;\r
+ VOID *Ctx;\r
CHAR8 IdByte[1];\r
EFI_STATUS Status;\r
\r
return EFI_PROTOCOL_ERROR;\r
}\r
\r
- Md5ContextSize = Md5GetContextSize ();\r
- Md5Ctx = AllocatePool (Md5ContextSize);\r
- if (Md5Ctx == NULL) {\r
+ ASSERT (Hash != NULL);\r
+\r
+ ContextSize = Hash->GetContextSize ();\r
+ Ctx = AllocatePool (ContextSize);\r
+ if (Ctx == NULL) {\r
return EFI_OUT_OF_RESOURCES;\r
}\r
\r
Status = EFI_PROTOCOL_ERROR;\r
\r
- if (!Md5Init (Md5Ctx)) {\r
+ if (!Hash->Init (Ctx)) {\r
goto Exit;\r
}\r
\r
// Hash Identifier - Only calculate 1 byte data (RFC1994)\r
//\r
IdByte[0] = (CHAR8) ChapIdentifier;\r
- if (!Md5Update (Md5Ctx, IdByte, 1)) {\r
+ if (!Hash->Update (Ctx, IdByte, 1)) {\r
goto Exit;\r
}\r
\r
//\r
// Hash Secret\r
//\r
- if (!Md5Update (Md5Ctx, ChapSecret, SecretLength)) {\r
+ if (!Hash->Update (Ctx, ChapSecret, SecretLength)) {\r
goto Exit;\r
}\r
\r
//\r
// Hash Challenge received from Target\r
//\r
- if (!Md5Update (Md5Ctx, ChapChallenge, ChallengeLength)) {\r
+ if (!Hash->Update (Ctx, ChapChallenge, ChallengeLength)) {\r
goto Exit;\r
}\r
\r
- if (Md5Final (Md5Ctx, ChapResponse)) {\r
+ if (Hash->Final (Ctx, ChapResponse)) {\r
Status = EFI_SUCCESS;\r
}\r
\r
Exit:\r
- FreePool (Md5Ctx);\r
+ FreePool (Ctx);\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
+ The initiator 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\r
+ authentication.\r
+ @retval EFI_SECURITY_VIOLATION The response from target was not expected\r
+ value.\r
@retval Others Other errors as indicated.\r
\r
**/\r
{\r
EFI_STATUS Status;\r
UINT32 SecretSize;\r
- UINT8 VerifyRsp[ISCSI_CHAP_RSP_LEN];\r
+ UINT8 VerifyRsp[ISCSI_CHAP_MAX_DIGEST_SIZE];\r
+ INTN Mismatch;\r
\r
Status = EFI_SUCCESS;\r
\r
SecretSize = (UINT32) AsciiStrLen (AuthData->AuthConfig->ReverseCHAPSecret);\r
+\r
+ ASSERT (AuthData->Hash != NULL);\r
+\r
Status = IScsiCHAPCalculateResponse (\r
AuthData->OutIdentifier,\r
AuthData->AuthConfig->ReverseCHAPSecret,\r
SecretSize,\r
AuthData->OutChallenge,\r
- AuthData->OutChallengeLength,\r
+ AuthData->Hash->DigestSize, // ChallengeLength\r
+ AuthData->Hash,\r
VerifyRsp\r
);\r
\r
- if (CompareMem (VerifyRsp, TargetResponse, ISCSI_CHAP_RSP_LEN) != 0) {\r
+ Mismatch = CompareMem (\r
+ VerifyRsp,\r
+ TargetResponse,\r
+ AuthData->Hash->DigestSize\r
+ );\r
+ if (Mismatch != 0) {\r
Status = EFI_SECURITY_VIOLATION;\r
}\r
\r
CHAR8 *Challenge;\r
CHAR8 *Name;\r
CHAR8 *Response;\r
- UINT8 TargetRsp[ISCSI_CHAP_RSP_LEN];\r
+ UINT8 TargetRsp[ISCSI_CHAP_MAX_DIGEST_SIZE];\r
UINT32 RspLen;\r
UINTN Result;\r
+ UINTN HashIndex;\r
\r
ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);\r
ASSERT (Conn->RspQue.BufNum != 0);\r
//\r
// The first Login Response.\r
//\r
- Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG);\r
+ Value = IScsiGetValueByKeyFromList (\r
+ KeyValueList,\r
+ ISCSI_KEY_TARGET_PORTAL_GROUP_TAG\r
+ );\r
if (Value == NULL) {\r
goto ON_EXIT;\r
}\r
\r
Session->TargetPortalGroupTag = (UINT16) Result;\r
\r
- Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_AUTH_METHOD);\r
+ Value = IScsiGetValueByKeyFromList (\r
+ KeyValueList,\r
+ ISCSI_KEY_AUTH_METHOD\r
+ );\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
+ // Initiator mandates CHAP authentication but target replies without\r
+ // "CHAP", or initiator suggets "None" but target replies with some kind of\r
+ // auth method.\r
//\r
if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {\r
if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) != 0) {\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
+ Value = IScsiGetValueByKeyFromList (\r
+ KeyValueList,\r
+ ISCSI_KEY_CHAP_ALGORITHM\r
+ );\r
if (Value == NULL) {\r
goto ON_EXIT;\r
}\r
\r
Algorithm = IScsiNetNtoi (Value);\r
- if (Algorithm != ISCSI_CHAP_ALGORITHM_MD5) {\r
+ for (HashIndex = 0; HashIndex < ARRAY_SIZE (mChapHash); HashIndex++) {\r
+ if (Algorithm == mChapHash[HashIndex].Algorithm) {\r
+ break;\r
+ }\r
+ }\r
+ if (HashIndex == ARRAY_SIZE (mChapHash)) {\r
//\r
// Unsupported algorithm is chosen by target.\r
//\r
goto ON_EXIT;\r
}\r
+ //\r
+ // Remember the target's chosen hash algorithm.\r
+ //\r
+ ASSERT (AuthData->Hash == NULL);\r
+ AuthData->Hash = &mChapHash[HashIndex];\r
\r
- Identifier = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_IDENTIFIER);\r
+ Identifier = IScsiGetValueByKeyFromList (\r
+ KeyValueList,\r
+ ISCSI_KEY_CHAP_IDENTIFIER\r
+ );\r
if (Identifier == NULL) {\r
goto ON_EXIT;\r
}\r
\r
- Challenge = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_CHALLENGE);\r
+ Challenge = IScsiGetValueByKeyFromList (\r
+ KeyValueList,\r
+ ISCSI_KEY_CHAP_CHALLENGE\r
+ );\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
+ //\r
Result = IScsiNetNtoi (Identifier);\r
if (Result > 0xFF) {\r
goto ON_EXIT;\r
- } \r
- \r
+ }\r
+\r
AuthData->InIdentifier = (UINT32) Result;\r
- AuthData->InChallengeLength = ISCSI_CHAP_AUTH_MAX_LEN;\r
- IScsiHexToBin ((UINT8 *) AuthData->InChallenge, &AuthData->InChallengeLength, Challenge);\r
+ AuthData->InChallengeLength = (UINT32) sizeof (AuthData->InChallenge);\r
+ Status = IScsiHexToBin (\r
+ (UINT8 *) AuthData->InChallenge,\r
+ &AuthData->InChallengeLength,\r
+ Challenge\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_PROTOCOL_ERROR;\r
+ goto ON_EXIT;\r
+ }\r
Status = IScsiCHAPCalculateResponse (\r
AuthData->InIdentifier,\r
AuthData->AuthConfig->CHAPSecret,\r
(UINT32) AsciiStrLen (AuthData->AuthConfig->CHAPSecret),\r
AuthData->InChallenge,\r
AuthData->InChallengeLength,\r
+ AuthData->Hash,\r
AuthData->CHAPResponse\r
);\r
\r
goto ON_EXIT;\r
}\r
\r
- Response = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_RESPONSE);\r
+ Response = IScsiGetValueByKeyFromList (\r
+ KeyValueList,\r
+ ISCSI_KEY_CHAP_RESPONSE\r
+ );\r
if (Response == NULL) {\r
goto ON_EXIT;\r
}\r
\r
- RspLen = ISCSI_CHAP_RSP_LEN;\r
- IScsiHexToBin (TargetRsp, &RspLen, Response);\r
+ ASSERT (AuthData->Hash != NULL);\r
+ RspLen = AuthData->Hash->DigestSize;\r
+ Status = IScsiHexToBin (TargetRsp, &RspLen, Response);\r
+ if (EFI_ERROR (Status) || RspLen != AuthData->Hash->DigestSize) {\r
+ Status = EFI_PROTOCOL_ERROR;\r
+ goto ON_EXIT;\r
+ }\r
\r
//\r
// Check the CHAP Name and Response replied by Target.\r
\r
if (KeyValueList != NULL) {\r
IScsiFreeKeyValueList (KeyValueList);\r
- } \r
+ }\r
\r
FreePool (Data);\r
\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
+ authentication info is filled into the iSCSI\r
+ PDU.\r
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
@retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.\r
\r
UINT32 RspLen;\r
CHAR8 *Challenge;\r
UINT32 ChallengeLen;\r
+ EFI_STATUS BinToHexStatus;\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
+ if (LoginReq == NULL) {\r
+ return EFI_PROTOCOL_ERROR;\r
+ }\r
Status = EFI_SUCCESS;\r
\r
- RspLen = 2 * ISCSI_CHAP_RSP_LEN + 3;\r
+ RspLen = 2 * ISCSI_CHAP_MAX_DIGEST_SIZE + 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
+ ChallengeLen = 2 * ISCSI_CHAP_MAX_DIGEST_SIZE + 3;\r
Challenge = AllocateZeroPool (ChallengeLen);\r
if (Challenge == NULL) {\r
FreePool (Response);\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 (\r
+ Pdu,\r
+ ISCSI_KEY_INITIATOR_NAME,\r
+ mPrivate->InitiatorName\r
+ );\r
IScsiAddKeyValuePair (Pdu, ISCSI_KEY_SESSION_TYPE, "Normal");\r
IScsiAddKeyValuePair (\r
Pdu,\r
\r
case ISCSI_CHAP_STEP_ONE:\r
//\r
- // First step, send the Login Request with CHAP_A=<A1,A2...> key-value pair.\r
+ // First step, send the Login Request with CHAP_A=<A1,A2...> key-value\r
+ // pair.\r
//\r
- AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", ISCSI_CHAP_ALGORITHM_MD5);\r
- IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, ValueStr);\r
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, mChapHashListString);\r
\r
Conn->AuthStep = ISCSI_CHAP_STEP_TWO;\r
break;\r
//\r
// CHAP_N=<N>\r
//\r
- IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_NAME, (CHAR8 *) &AuthData->AuthConfig->CHAPName);\r
+ IScsiAddKeyValuePair (\r
+ Pdu,\r
+ ISCSI_KEY_CHAP_NAME,\r
+ (CHAR8 *) &AuthData->AuthConfig->CHAPName\r
+ );\r
//\r
// CHAP_R=<R>\r
//\r
- IScsiBinToHex ((UINT8 *) AuthData->CHAPResponse, ISCSI_CHAP_RSP_LEN, Response, &RspLen);\r
+ ASSERT (AuthData->Hash != NULL);\r
+ BinToHexStatus = IScsiBinToHex (\r
+ (UINT8 *) AuthData->CHAPResponse,\r
+ AuthData->Hash->DigestSize,\r
+ Response,\r
+ &RspLen\r
+ );\r
+ ASSERT_EFI_ERROR (BinToHexStatus);\r
IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_RESPONSE, Response);\r
\r
if (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL) {\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
+ IScsiGenRandom (\r
+ (UINT8 *) AuthData->OutChallenge,\r
+ AuthData->Hash->DigestSize\r
+ );\r
+ BinToHexStatus = IScsiBinToHex (\r
+ (UINT8 *) AuthData->OutChallenge,\r
+ AuthData->Hash->DigestSize,\r
+ Challenge,\r
+ &ChallengeLen\r
+ );\r
+ ASSERT_EFI_ERROR (BinToHexStatus);\r
IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_CHALLENGE, Challenge);\r
\r
Conn->AuthStep = ISCSI_CHAP_STEP_FOUR;\r
\r
return Status;\r
}\r
+\r
+/**\r
+ Initialize the CHAP_A=<A1,A2...> *value* string for the entire driver, to be\r
+ sent by the initiator in ISCSI_CHAP_STEP_ONE.\r
+\r
+ This function sanity-checks the internal table of supported CHAP hashing\r
+ algorithms, as well.\r
+**/\r
+VOID\r
+IScsiCHAPInitHashList (\r
+ VOID\r
+ )\r
+{\r
+ CHAR8 *Position;\r
+ UINTN Left;\r
+ UINTN HashIndex;\r
+ CONST CHAP_HASH *Hash;\r
+ UINTN Printed;\r
+\r
+ Position = mChapHashListString;\r
+ Left = sizeof (mChapHashListString);\r
+ for (HashIndex = 0; HashIndex < ARRAY_SIZE (mChapHash); HashIndex++) {\r
+ Hash = &mChapHash[HashIndex];\r
+\r
+ //\r
+ // Format the next hash identifier.\r
+ //\r
+ // Assert that we can format at least one non-NUL character, i.e. that we\r
+ // can progress. Truncation is checked after printing.\r
+ //\r
+ ASSERT (Left >= 2);\r
+ Printed = AsciiSPrint (\r
+ Position,\r
+ Left,\r
+ "%a%d",\r
+ (HashIndex == 0) ? "" : ",",\r
+ Hash->Algorithm\r
+ );\r
+ //\r
+ // There's no way to differentiate between the "buffer filled to the brim,\r
+ // but not truncated" result and the "truncated" result of AsciiSPrint().\r
+ // This is why "mChapHashListString" has an extra byte allocated, and the\r
+ // reason why we use the less-than (rather than the less-than-or-equal-to)\r
+ // relational operator in the assertion below -- we enforce "no truncation"\r
+ // by excluding the "completely used up" case too.\r
+ //\r
+ ASSERT (Printed + 1 < Left);\r
+\r
+ Position += Printed;\r
+ Left -= Printed;\r
+\r
+ //\r
+ // Sanity-check the digest size for Hash.\r
+ //\r
+ ASSERT (Hash->DigestSize <= ISCSI_CHAP_MAX_DIGEST_SIZE);\r
+ }\r
+}\r