123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673 |
- /** @file
- This file is for Challenge-Handshake Authentication Protocol (CHAP)
- Configuration.
- Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
- 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_SHA256,
- SHA256_DIGEST_SIZE,
- Sha256GetContextSize,
- Sha256Init,
- Sha256Update,
- Sha256Final
- },
- #ifdef ENABLE_MD5_DEPRECATED_INTERFACES
- //
- // Keep the deprecated MD5 entry at the end of the array (making MD5 the
- // least preferred choice of the initiator).
- //
- {
- ISCSI_CHAP_ALGORITHM_MD5,
- MD5_DIGEST_SIZE,
- Md5GetContextSize,
- Md5Init,
- Md5Update,
- Md5Final
- },
- #endif // ENABLE_MD5_DEPRECATED_INTERFACES
- };
- //
- // Ordered list of mChapHash[*].Algorithm values. It is formatted for the
- // CHAP_A=<A1,A2...> 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.
- @param[in] ChapIdentifier iSCSI CHAP identifier sent by authenticator.
- @param[in] ChapSecret iSCSI CHAP secret of the authenticator.
- @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
- successfully.
- @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 Hash operation fails.
- @retval EFI_OUT_OF_RESOURCES Failure to allocate resource to complete
- hashing.
- **/
- EFI_STATUS
- IScsiCHAPCalculateResponse (
- IN UINT32 ChapIdentifier,
- IN CHAR8 *ChapSecret,
- IN UINT32 SecretLength,
- IN UINT8 *ChapChallenge,
- IN UINT32 ChallengeLength,
- IN CONST CHAP_HASH *Hash,
- OUT UINT8 *ChapResponse
- )
- {
- UINTN ContextSize;
- VOID *Ctx;
- CHAR8 IdByte[1];
- EFI_STATUS Status;
- if (SecretLength < ISCSI_CHAP_SECRET_MIN_LEN) {
- return EFI_PROTOCOL_ERROR;
- }
- ASSERT (Hash != NULL);
- ContextSize = Hash->GetContextSize ();
- Ctx = AllocatePool (ContextSize);
- if (Ctx == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- Status = EFI_PROTOCOL_ERROR;
- if (!Hash->Init (Ctx)) {
- goto Exit;
- }
- //
- // Hash Identifier - Only calculate 1 byte data (RFC1994)
- //
- IdByte[0] = (CHAR8)ChapIdentifier;
- if (!Hash->Update (Ctx, IdByte, 1)) {
- goto Exit;
- }
- //
- // Hash Secret
- //
- if (!Hash->Update (Ctx, ChapSecret, SecretLength)) {
- goto Exit;
- }
- //
- // Hash Challenge received from Target
- //
- if (!Hash->Update (Ctx, ChapChallenge, ChallengeLength)) {
- goto Exit;
- }
- if (Hash->Final (Ctx, ChapResponse)) {
- Status = EFI_SUCCESS;
- }
- Exit:
- FreePool (Ctx);
- return Status;
- }
- /**
- The initiator checks the CHAP response replied by target against its own
- calculation of the expected hash value.
- @param[in] AuthData iSCSI CHAP authentication data.
- @param[in] TargetResponse The response from target.
- @retval EFI_SUCCESS The response from target passed
- authentication.
- @retval EFI_SECURITY_VIOLATION The response from target was not expected
- value.
- @retval Others Other errors as indicated.
- **/
- EFI_STATUS
- IScsiCHAPAuthTarget (
- IN ISCSI_CHAP_AUTH_DATA *AuthData,
- IN UINT8 *TargetResponse
- )
- {
- 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,
- AuthData->Hash->DigestSize, // ChallengeLength
- AuthData->Hash,
- VerifyRsp
- );
- Mismatch = CompareMem (
- VerifyRsp,
- TargetResponse,
- AuthData->Hash->DigestSize
- );
- if (Mismatch != 0) {
- Status = EFI_SECURITY_VIOLATION;
- }
- return Status;
- }
- /**
- This function checks the received iSCSI Login Response during the security
- negotiation stage.
- @param[in] Conn The iSCSI connection.
- @retval EFI_SUCCESS The Login Response passed the CHAP validation.
- @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
- @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
- @retval Others Other errors as indicated.
- **/
- EFI_STATUS
- IScsiCHAPOnRspReceived (
- IN ISCSI_CONNECTION *Conn
- )
- {
- EFI_STATUS Status;
- ISCSI_SESSION *Session;
- ISCSI_CHAP_AUTH_DATA *AuthData;
- CHAR8 *Value;
- UINT8 *Data;
- UINT32 Len;
- LIST_ENTRY *KeyValueList;
- UINTN Algorithm;
- CHAR8 *Identifier;
- CHAR8 *Challenge;
- CHAR8 *Name;
- CHAR8 *Response;
- UINT8 TargetRsp[ISCSI_CHAP_MAX_DIGEST_SIZE];
- UINT32 RspLen;
- UINTN Result;
- UINTN HashIndex;
- ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);
- ASSERT (Conn->RspQue.BufNum != 0);
- Session = Conn->Session;
- AuthData = &Session->AuthData.CHAP;
- Len = Conn->RspQue.BufSize;
- Data = AllocateZeroPool (Len);
- if (Data == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- //
- // Copy the data in case the data spans over multiple PDUs.
- //
- NetbufQueCopy (&Conn->RspQue, 0, Len, Data);
- //
- // Build the key-value list from the data segment of the Login Response.
- //
- KeyValueList = IScsiBuildKeyValueList ((CHAR8 *)Data, Len);
- if (KeyValueList == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto ON_EXIT;
- }
- Status = EFI_PROTOCOL_ERROR;
- switch (Conn->AuthStep) {
- case ISCSI_AUTH_INITIAL:
- //
- // The first Login Response.
- //
- Value = IScsiGetValueByKeyFromList (
- KeyValueList,
- ISCSI_KEY_TARGET_PORTAL_GROUP_TAG
- );
- if (Value == NULL) {
- goto ON_EXIT;
- }
- Result = IScsiNetNtoi (Value);
- if (Result > 0xFFFF) {
- goto ON_EXIT;
- }
- Session->TargetPortalGroupTag = (UINT16)Result;
- Value = IScsiGetValueByKeyFromList (
- KeyValueList,
- ISCSI_KEY_AUTH_METHOD
- );
- if (Value == NULL) {
- goto ON_EXIT;
- }
- //
- // Initiator mandates CHAP authentication but target replies without
- // "CHAP", or initiator suggets "None" but target replies with some kind of
- // auth method.
- //
- if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {
- if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) != 0) {
- goto ON_EXIT;
- }
- } else if (Session->AuthType == ISCSI_AUTH_TYPE_CHAP) {
- if (AsciiStrCmp (Value, ISCSI_AUTH_METHOD_CHAP) != 0) {
- goto ON_EXIT;
- }
- } else {
- goto ON_EXIT;
- }
- //
- // Transit to CHAP step one.
- //
- Conn->AuthStep = ISCSI_CHAP_STEP_ONE;
- Status = EFI_SUCCESS;
- break;
- case ISCSI_CHAP_STEP_TWO:
- //
- // The Target replies with CHAP_A=<A> CHAP_I=<I> CHAP_C=<C>
- //
- Value = IScsiGetValueByKeyFromList (
- KeyValueList,
- ISCSI_KEY_CHAP_ALGORITHM
- );
- if (Value == NULL) {
- goto ON_EXIT;
- }
- Algorithm = IScsiNetNtoi (Value);
- 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,
- ISCSI_KEY_CHAP_IDENTIFIER
- );
- if (Identifier == NULL) {
- goto ON_EXIT;
- }
- Challenge = IScsiGetValueByKeyFromList (
- KeyValueList,
- ISCSI_KEY_CHAP_CHALLENGE
- );
- if (Challenge == NULL) {
- goto ON_EXIT;
- }
- //
- // Process the CHAP identifier and CHAP Challenge from Target.
- // Calculate Response value.
- //
- Result = IScsiNetNtoi (Identifier);
- if (Result > 0xFF) {
- goto ON_EXIT;
- }
- AuthData->InIdentifier = (UINT32)Result;
- AuthData->InChallengeLength = (UINT32)sizeof (AuthData->InChallenge);
- Status = IScsiHexToBin (
- (UINT8 *)AuthData->InChallenge,
- &AuthData->InChallengeLength,
- Challenge
- );
- if (EFI_ERROR (Status)) {
- Status = EFI_PROTOCOL_ERROR;
- goto ON_EXIT;
- }
- Status = IScsiCHAPCalculateResponse (
- AuthData->InIdentifier,
- AuthData->AuthConfig->CHAPSecret,
- (UINT32)AsciiStrLen (AuthData->AuthConfig->CHAPSecret),
- AuthData->InChallenge,
- AuthData->InChallengeLength,
- AuthData->Hash,
- AuthData->CHAPResponse
- );
- //
- // Transit to next step.
- //
- Conn->AuthStep = ISCSI_CHAP_STEP_THREE;
- break;
- case ISCSI_CHAP_STEP_THREE:
- //
- // One way CHAP authentication and the target would like to
- // authenticate us.
- //
- Status = EFI_SUCCESS;
- break;
- case ISCSI_CHAP_STEP_FOUR:
- ASSERT (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL);
- //
- // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.
- //
- Name = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_NAME);
- if (Name == NULL) {
- goto ON_EXIT;
- }
- Response = IScsiGetValueByKeyFromList (
- KeyValueList,
- ISCSI_KEY_CHAP_RESPONSE
- );
- if (Response == NULL) {
- goto ON_EXIT;
- }
- ASSERT (AuthData->Hash != NULL);
- RspLen = AuthData->Hash->DigestSize;
- Status = IScsiHexToBin (TargetRsp, &RspLen, Response);
- if (EFI_ERROR (Status) || (RspLen != AuthData->Hash->DigestSize)) {
- Status = EFI_PROTOCOL_ERROR;
- goto ON_EXIT;
- }
- //
- // Check the CHAP Name and Response replied by Target.
- //
- Status = IScsiCHAPAuthTarget (AuthData, TargetRsp);
- break;
- default:
- break;
- }
- ON_EXIT:
- if (KeyValueList != NULL) {
- IScsiFreeKeyValueList (KeyValueList);
- }
- FreePool (Data);
- return Status;
- }
- /**
- This function fills the CHAP authentication information into the login PDU
- during the security negotiation stage in the iSCSI connection login.
- @param[in] Conn The iSCSI connection.
- @param[in, out] Pdu The PDU to send out.
- @retval EFI_SUCCESS All check passed and the phase-related CHAP
- authentication info is filled into the iSCSI
- PDU.
- @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
- @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
- **/
- EFI_STATUS
- IScsiCHAPToSendReq (
- IN ISCSI_CONNECTION *Conn,
- IN OUT NET_BUF *Pdu
- )
- {
- EFI_STATUS Status;
- ISCSI_SESSION *Session;
- ISCSI_LOGIN_REQUEST *LoginReq;
- ISCSI_CHAP_AUTH_DATA *AuthData;
- CHAR8 *Value;
- CHAR8 ValueStr[256];
- CHAR8 *Response;
- UINT32 RspLen;
- CHAR8 *Challenge;
- UINT32 ChallengeLen;
- EFI_STATUS BinToHexStatus;
- ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);
- Session = Conn->Session;
- AuthData = &Session->AuthData.CHAP;
- LoginReq = (ISCSI_LOGIN_REQUEST *)NetbufGetByte (Pdu, 0, 0);
- if (LoginReq == NULL) {
- return EFI_PROTOCOL_ERROR;
- }
- Status = EFI_SUCCESS;
- RspLen = 2 * ISCSI_CHAP_MAX_DIGEST_SIZE + 3;
- Response = AllocateZeroPool (RspLen);
- if (Response == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- ChallengeLen = 2 * ISCSI_CHAP_MAX_DIGEST_SIZE + 3;
- Challenge = AllocateZeroPool (ChallengeLen);
- if (Challenge == NULL) {
- FreePool (Response);
- return EFI_OUT_OF_RESOURCES;
- }
- switch (Conn->AuthStep) {
- case ISCSI_AUTH_INITIAL:
- //
- // It's the initial Login Request. Fill in the key=value pairs mandatory
- // for the initial Login Request.
- //
- IScsiAddKeyValuePair (
- Pdu,
- ISCSI_KEY_INITIATOR_NAME,
- mPrivate->InitiatorName
- );
- IScsiAddKeyValuePair (Pdu, ISCSI_KEY_SESSION_TYPE, "Normal");
- IScsiAddKeyValuePair (
- Pdu,
- ISCSI_KEY_TARGET_NAME,
- Session->ConfigData->SessionConfigData.TargetName
- );
- if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {
- Value = ISCSI_KEY_VALUE_NONE;
- ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
- } else {
- Value = ISCSI_AUTH_METHOD_CHAP;
- }
- IScsiAddKeyValuePair (Pdu, ISCSI_KEY_AUTH_METHOD, Value);
- break;
- case ISCSI_CHAP_STEP_ONE:
- //
- // First step, send the Login Request with CHAP_A=<A1,A2...> key-value
- // pair.
- //
- IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, mChapHashListString);
- Conn->AuthStep = ISCSI_CHAP_STEP_TWO;
- break;
- case ISCSI_CHAP_STEP_THREE:
- //
- // Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or
- // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target authentication is
- // required too.
- //
- // CHAP_N=<N>
- //
- IScsiAddKeyValuePair (
- Pdu,
- ISCSI_KEY_CHAP_NAME,
- (CHAR8 *)&AuthData->AuthConfig->CHAPName
- );
- //
- // CHAP_R=<R>
- //
- ASSERT (AuthData->Hash != NULL);
- BinToHexStatus = IScsiBinToHex (
- (UINT8 *)AuthData->CHAPResponse,
- AuthData->Hash->DigestSize,
- Response,
- &RspLen
- );
- ASSERT_EFI_ERROR (BinToHexStatus);
- IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_RESPONSE, Response);
- if (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL) {
- //
- // CHAP_I=<I>
- //
- IScsiGenRandom ((UINT8 *)&AuthData->OutIdentifier, 1);
- AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", AuthData->OutIdentifier);
- IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_IDENTIFIER, ValueStr);
- //
- // CHAP_C=<C>
- //
- IScsiGenRandom (
- (UINT8 *)AuthData->OutChallenge,
- AuthData->Hash->DigestSize
- );
- BinToHexStatus = IScsiBinToHex (
- (UINT8 *)AuthData->OutChallenge,
- AuthData->Hash->DigestSize,
- Challenge,
- &ChallengeLen
- );
- ASSERT_EFI_ERROR (BinToHexStatus);
- IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_CHALLENGE, Challenge);
- Conn->AuthStep = ISCSI_CHAP_STEP_FOUR;
- }
- //
- // Set the stage transition flag.
- //
- ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
- break;
- default:
- Status = EFI_PROTOCOL_ERROR;
- break;
- }
- FreePool (Response);
- FreePool (Challenge);
- return Status;
- }
- /**
- Initialize the CHAP_A=<A1,A2...> *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);
- }
- }
|