diff --git a/NetworkPkg/IScsiDxe/IScsiCHAP.c b/NetworkPkg/IScsiDxe/IScsiCHAP.c index 744824e63d..351bf329b7 100644 --- a/NetworkPkg/IScsiDxe/IScsiCHAP.c +++ b/NetworkPkg/IScsiDxe/IScsiCHAP.c @@ -9,6 +9,39 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "IScsiImpl.h" +// +// Supported CHAP hash algorithms, mapped to sets of BaseCryptLib APIs and +// macros. CHAP_HASH structures at lower subscripts in the array are preferred +// by the initiator. +// +STATIC CONST CHAP_HASH mChapHash[] = { + { + ISCSI_CHAP_ALGORITHM_MD5, + MD5_DIGEST_SIZE, + Md5GetContextSize, + Md5Init, + Md5Update, + Md5Final + }, +}; + +// +// Ordered list of mChapHash[*].Algorithm values. It is formatted for the +// CHAP_A= value string, by the IScsiCHAPInitHashList() function. It +// is sent by the initiator in ISCSI_CHAP_STEP_ONE. +// +STATIC CHAR8 mChapHashListString[ + 3 + // UINT8 identifier in + // decimal + (1 + 3) * (ARRAY_SIZE (mChapHash) - 1) + // comma prepended for + // entries after the + // first + 1 + // extra character for + // AsciiSPrint() + // truncation check + 1 // terminating NUL + ]; + /** Initiator calculates its own expected hash value. @@ -17,6 +50,10 @@ SPDX-License-Identifier: BSD-2-Clause-Patent @param[in] SecretLength The length of iSCSI CHAP secret. @param[in] ChapChallenge The challenge message sent by authenticator. @param[in] ChallengeLength The length of iSCSI CHAP challenge message. + @param[in] Hash Pointer to the CHAP_HASH structure that + determines the hashing algorithm to use. The + caller is responsible for making Hash point + to an "mChapHash" element. @param[out] ChapResponse The calculation of the expected hash value. @retval EFI_SUCCESS The expected hash value was calculatedly @@ -24,8 +61,9 @@ SPDX-License-Identifier: BSD-2-Clause-Patent @retval EFI_PROTOCOL_ERROR The length of the secret should be at least the length of the hash value for the hashing algorithm chosen. - @retval EFI_PROTOCOL_ERROR MD5 hash operation fail. - @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete MD5. + @retval EFI_PROTOCOL_ERROR Hash operation fails. + @retval EFI_OUT_OF_RESOURCES Failure to allocate resource to complete + hashing. **/ EFI_STATUS @@ -35,11 +73,12 @@ IScsiCHAPCalculateResponse ( IN UINT32 SecretLength, IN UINT8 *ChapChallenge, IN UINT32 ChallengeLength, + IN CONST CHAP_HASH *Hash, OUT UINT8 *ChapResponse ) { - UINTN Md5ContextSize; - VOID *Md5Ctx; + UINTN ContextSize; + VOID *Ctx; CHAR8 IdByte[1]; EFI_STATUS Status; @@ -47,15 +86,17 @@ IScsiCHAPCalculateResponse ( return EFI_PROTOCOL_ERROR; } - Md5ContextSize = Md5GetContextSize (); - Md5Ctx = AllocatePool (Md5ContextSize); - if (Md5Ctx == NULL) { + ASSERT (Hash != NULL); + + ContextSize = Hash->GetContextSize (); + Ctx = AllocatePool (ContextSize); + if (Ctx == NULL) { return EFI_OUT_OF_RESOURCES; } Status = EFI_PROTOCOL_ERROR; - if (!Md5Init (Md5Ctx)) { + if (!Hash->Init (Ctx)) { goto Exit; } @@ -63,30 +104,30 @@ IScsiCHAPCalculateResponse ( // Hash Identifier - Only calculate 1 byte data (RFC1994) // IdByte[0] = (CHAR8) ChapIdentifier; - if (!Md5Update (Md5Ctx, IdByte, 1)) { + if (!Hash->Update (Ctx, IdByte, 1)) { goto Exit; } // // Hash Secret // - if (!Md5Update (Md5Ctx, ChapSecret, SecretLength)) { + if (!Hash->Update (Ctx, ChapSecret, SecretLength)) { goto Exit; } // // Hash Challenge received from Target // - if (!Md5Update (Md5Ctx, ChapChallenge, ChallengeLength)) { + if (!Hash->Update (Ctx, ChapChallenge, ChallengeLength)) { goto Exit; } - if (Md5Final (Md5Ctx, ChapResponse)) { + if (Hash->Final (Ctx, ChapResponse)) { Status = EFI_SUCCESS; } Exit: - FreePool (Md5Ctx); + FreePool (Ctx); return Status; } @@ -113,20 +154,30 @@ IScsiCHAPAuthTarget ( EFI_STATUS Status; UINT32 SecretSize; UINT8 VerifyRsp[ISCSI_CHAP_MAX_DIGEST_SIZE]; + INTN Mismatch; Status = EFI_SUCCESS; SecretSize = (UINT32) AsciiStrLen (AuthData->AuthConfig->ReverseCHAPSecret); + + ASSERT (AuthData->Hash != NULL); + Status = IScsiCHAPCalculateResponse ( AuthData->OutIdentifier, AuthData->AuthConfig->ReverseCHAPSecret, SecretSize, AuthData->OutChallenge, - MD5_DIGEST_SIZE, // ChallengeLength + AuthData->Hash->DigestSize, // ChallengeLength + AuthData->Hash, VerifyRsp ); - if (CompareMem (VerifyRsp, TargetResponse, MD5_DIGEST_SIZE) != 0) { + Mismatch = CompareMem ( + VerifyRsp, + TargetResponse, + AuthData->Hash->DigestSize + ); + if (Mismatch != 0) { Status = EFI_SECURITY_VIOLATION; } @@ -166,6 +217,7 @@ IScsiCHAPOnRspReceived ( UINT8 TargetRsp[ISCSI_CHAP_MAX_DIGEST_SIZE]; UINT32 RspLen; UINTN Result; + UINTN HashIndex; ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION); ASSERT (Conn->RspQue.BufNum != 0); @@ -257,12 +309,22 @@ IScsiCHAPOnRspReceived ( } Algorithm = IScsiNetNtoi (Value); - if (Algorithm != ISCSI_CHAP_ALGORITHM_MD5) { + for (HashIndex = 0; HashIndex < ARRAY_SIZE (mChapHash); HashIndex++) { + if (Algorithm == mChapHash[HashIndex].Algorithm) { + break; + } + } + if (HashIndex == ARRAY_SIZE (mChapHash)) { // // Unsupported algorithm is chosen by target. // goto ON_EXIT; } + // + // Remember the target's chosen hash algorithm. + // + ASSERT (AuthData->Hash == NULL); + AuthData->Hash = &mChapHash[HashIndex]; Identifier = IScsiGetValueByKeyFromList ( KeyValueList, @@ -305,6 +367,7 @@ IScsiCHAPOnRspReceived ( (UINT32) AsciiStrLen (AuthData->AuthConfig->CHAPSecret), AuthData->InChallenge, AuthData->InChallengeLength, + AuthData->Hash, AuthData->CHAPResponse ); @@ -340,9 +403,10 @@ IScsiCHAPOnRspReceived ( goto ON_EXIT; } - RspLen = MD5_DIGEST_SIZE; + ASSERT (AuthData->Hash != NULL); + RspLen = AuthData->Hash->DigestSize; Status = IScsiHexToBin (TargetRsp, &RspLen, Response); - if (EFI_ERROR (Status) || RspLen != MD5_DIGEST_SIZE) { + if (EFI_ERROR (Status) || RspLen != AuthData->Hash->DigestSize) { Status = EFI_PROTOCOL_ERROR; goto ON_EXIT; } @@ -458,8 +522,7 @@ IScsiCHAPToSendReq ( // First step, send the Login Request with CHAP_A= key-value // pair. // - AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", ISCSI_CHAP_ALGORITHM_MD5); - IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, ValueStr); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, mChapHashListString); Conn->AuthStep = ISCSI_CHAP_STEP_TWO; break; @@ -480,9 +543,10 @@ IScsiCHAPToSendReq ( // // CHAP_R= // + ASSERT (AuthData->Hash != NULL); BinToHexStatus = IScsiBinToHex ( (UINT8 *) AuthData->CHAPResponse, - MD5_DIGEST_SIZE, + AuthData->Hash->DigestSize, Response, &RspLen ); @@ -499,10 +563,13 @@ IScsiCHAPToSendReq ( // // CHAP_C= // - IScsiGenRandom ((UINT8 *) AuthData->OutChallenge, MD5_DIGEST_SIZE); + IScsiGenRandom ( + (UINT8 *) AuthData->OutChallenge, + AuthData->Hash->DigestSize + ); BinToHexStatus = IScsiBinToHex ( (UINT8 *) AuthData->OutChallenge, - MD5_DIGEST_SIZE, + AuthData->Hash->DigestSize, Challenge, &ChallengeLen ); @@ -527,3 +594,60 @@ IScsiCHAPToSendReq ( return Status; } + +/** + Initialize the CHAP_A= *value* string for the entire driver, to be + sent by the initiator in ISCSI_CHAP_STEP_ONE. + + This function sanity-checks the internal table of supported CHAP hashing + algorithms, as well. +**/ +VOID +IScsiCHAPInitHashList ( + VOID + ) +{ + CHAR8 *Position; + UINTN Left; + UINTN HashIndex; + CONST CHAP_HASH *Hash; + UINTN Printed; + + Position = mChapHashListString; + Left = sizeof (mChapHashListString); + for (HashIndex = 0; HashIndex < ARRAY_SIZE (mChapHash); HashIndex++) { + Hash = &mChapHash[HashIndex]; + + // + // Format the next hash identifier. + // + // Assert that we can format at least one non-NUL character, i.e. that we + // can progress. Truncation is checked after printing. + // + ASSERT (Left >= 2); + Printed = AsciiSPrint ( + Position, + Left, + "%a%d", + (HashIndex == 0) ? "" : ",", + Hash->Algorithm + ); + // + // There's no way to differentiate between the "buffer filled to the brim, + // but not truncated" result and the "truncated" result of AsciiSPrint(). + // This is why "mChapHashListString" has an extra byte allocated, and the + // reason why we use the less-than (rather than the less-than-or-equal-to) + // relational operator in the assertion below -- we enforce "no truncation" + // by excluding the "completely used up" case too. + // + ASSERT (Printed + 1 < Left); + + Position += Printed; + Left -= Printed; + + // + // Sanity-check the digest size for Hash. + // + ASSERT (Hash->DigestSize <= ISCSI_CHAP_MAX_DIGEST_SIZE); + } +} diff --git a/NetworkPkg/IScsiDxe/IScsiCHAP.h b/NetworkPkg/IScsiDxe/IScsiCHAP.h index b8811b7580..1e5cc0b287 100644 --- a/NetworkPkg/IScsiDxe/IScsiCHAP.h +++ b/NetworkPkg/IScsiDxe/IScsiCHAP.h @@ -47,6 +47,45 @@ typedef struct _ISCSI_CHAP_AUTH_CONFIG_NVDATA { #pragma pack() +// +// Typedefs for collecting sets of hash APIs from BaseCryptLib. +// +typedef +UINTN +(EFIAPI *CHAP_HASH_GET_CONTEXT_SIZE) ( + VOID + ); + +typedef +BOOLEAN +(EFIAPI *CHAP_HASH_INIT) ( + OUT VOID *Context + ); + +typedef +BOOLEAN +(EFIAPI *CHAP_HASH_UPDATE) ( + IN OUT VOID *Context, + IN CONST VOID *Data, + IN UINTN DataSize + ); + +typedef +BOOLEAN +(EFIAPI *CHAP_HASH_FINAL) ( + IN OUT VOID *Context, + OUT UINT8 *HashValue + ); + +typedef struct { + UINT8 Algorithm; // ISCSI_CHAP_ALGORITHM_*, CHAP_A + UINT32 DigestSize; + CHAP_HASH_GET_CONTEXT_SIZE GetContextSize; + CHAP_HASH_INIT Init; + CHAP_HASH_UPDATE Update; + CHAP_HASH_FINAL Final; +} CHAP_HASH; + /// /// ISCSI CHAP Authentication Data /// @@ -56,6 +95,11 @@ typedef struct _ISCSI_CHAP_AUTH_DATA { UINT8 InChallenge[1024]; UINT32 InChallengeLength; // + // The hash algorithm (CHAP_A) that the target selects in + // ISCSI_CHAP_STEP_TWO. + // + CONST CHAP_HASH *Hash; + // // Calculated CHAP Response (CHAP_R) value. // UINT8 CHAPResponse[ISCSI_CHAP_MAX_DIGEST_SIZE]; @@ -108,4 +152,15 @@ IScsiCHAPToSendReq ( IN OUT NET_BUF *Pdu ); +/** + Initialize the CHAP_A= *value* string for the entire driver, to be + sent by the initiator in ISCSI_CHAP_STEP_ONE. + + This function sanity-checks the internal table of supported CHAP hashing + algorithms, as well. +**/ +VOID +IScsiCHAPInitHashList ( + VOID + ); #endif diff --git a/NetworkPkg/IScsiDxe/IScsiDriver.c b/NetworkPkg/IScsiDxe/IScsiDriver.c index 98b73308c1..485c929721 100644 --- a/NetworkPkg/IScsiDxe/IScsiDriver.c +++ b/NetworkPkg/IScsiDxe/IScsiDriver.c @@ -1779,6 +1779,8 @@ IScsiDriverEntryPoint ( // // Create the private data structures. // + IScsiCHAPInitHashList (); + mPrivate = AllocateZeroPool (sizeof (ISCSI_PRIVATE_DATA)); if (mPrivate == NULL) { Status = EFI_OUT_OF_RESOURCES; diff --git a/NetworkPkg/IScsiDxe/IScsiProto.c b/NetworkPkg/IScsiDxe/IScsiProto.c index 69d1b39dbb..e62736bc04 100644 --- a/NetworkPkg/IScsiDxe/IScsiProto.c +++ b/NetworkPkg/IScsiDxe/IScsiProto.c @@ -432,6 +432,9 @@ IScsiSessionResetAuthData ( IN OUT ISCSI_SESSION *Session ) { + if (Session->AuthType == ISCSI_AUTH_TYPE_CHAP) { + Session->AuthData.CHAP.Hash = NULL; + } } /**