Introduce the NETWORK_ISCSI_MD5_ENABLE feature test macro for NetworkPkg. When explicitly set to FALSE, remove MD5 from IScsiDxe's CHAP algorithm list. Set NETWORK_ISCSI_MD5_ENABLE to TRUE by default, for compatibility reasons. Not just to minimize the disruption for platforms that currently include IScsiDxe, but also because RFC 7143 mandates MD5 for CHAP, and some vendors' iSCSI targets support MD5 only. With MD5 enabled, IScsiDxe will suggest SHA256, and then fall back to MD5 if the target requests it. With MD5 disabled, IScsiDxe will suggest SHA256, and break off the connection (and session) if the target doesn't support SHA256. Cc: Jiaxin Wu <jiaxin.wu@intel.com> Cc: Maciej Rabeda <maciej.rabeda@linux.intel.com> Cc: Philippe Mathieu-Daudé <philmd@redhat.com> Cc: Siyuan Fu <siyuan.fu@intel.com> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=3355 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Maciej Rabeda <maciej.rabeda@linux.intel.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Message-Id: <20210629163337.14120-7-lersek@redhat.com>
		
			
				
	
	
		
			668 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			668 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @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);
 | |
|   }
 | |
| }
 |