\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
Initiator calculates its own expected hash value.\r
\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 calculatedly\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 MD5 hash operation fail.\r
- @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete MD5.\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
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
EFI_STATUS Status;\r
UINT32 SecretSize;\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
- MD5_DIGEST_SIZE, // ChallengeLength\r
+ AuthData->Hash->DigestSize, // ChallengeLength\r
+ AuthData->Hash,\r
VerifyRsp\r
);\r
\r
- if (CompareMem (VerifyRsp, TargetResponse, MD5_DIGEST_SIZE) != 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
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
\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 (\r
KeyValueList,\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
- RspLen = MD5_DIGEST_SIZE;\r
+ ASSERT (AuthData->Hash != NULL);\r
+ RspLen = AuthData->Hash->DigestSize;\r
Status = IScsiHexToBin (TargetRsp, &RspLen, Response);\r
- if (EFI_ERROR (Status) || RspLen != MD5_DIGEST_SIZE) {\r
+ if (EFI_ERROR (Status) || RspLen != AuthData->Hash->DigestSize) {\r
Status = EFI_PROTOCOL_ERROR;\r
goto ON_EXIT;\r
}\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_R=<R>\r
//\r
+ ASSERT (AuthData->Hash != NULL);\r
BinToHexStatus = IScsiBinToHex (\r
(UINT8 *) AuthData->CHAPResponse,\r
- MD5_DIGEST_SIZE,\r
+ AuthData->Hash->DigestSize,\r
Response,\r
&RspLen\r
);\r
//\r
// CHAP_C=<C>\r
//\r
- IScsiGenRandom ((UINT8 *) AuthData->OutChallenge, MD5_DIGEST_SIZE);\r
+ IScsiGenRandom (\r
+ (UINT8 *) AuthData->OutChallenge,\r
+ AuthData->Hash->DigestSize\r
+ );\r
BinToHexStatus = IScsiBinToHex (\r
(UINT8 *) AuthData->OutChallenge,\r
- MD5_DIGEST_SIZE,\r
+ AuthData->Hash->DigestSize,\r
Challenge,\r
&ChallengeLen\r
);\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