]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/IScsiDxe/IScsiCHAP.c
Add IScsiDxe driver to NetworkPkg in order to support iSCSI over IPv6 stack and iSCSI...
[mirror_edk2.git] / NetworkPkg / IScsiDxe / IScsiCHAP.c
diff --git a/NetworkPkg/IScsiDxe/IScsiCHAP.c b/NetworkPkg/IScsiDxe/IScsiCHAP.c
new file mode 100644 (file)
index 0000000..4cfbba7
--- /dev/null
@@ -0,0 +1,474 @@
+/** @file\r
+  This file is for Challenge-Handshake Authentication Protocol (CHAP) 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
+\r
+**/\r
+\r
+#include "IScsiImpl.h"\r
+\r
+/**\r
+  Initator caculates 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[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
+**/\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
+  )\r
+{\r
+  UINTN       Md5ContextSize;\r
+  VOID        *Md5Ctx;\r
+  CHAR8       IdByte[1];\r
+  EFI_STATUS  Status;\r
+\r
+  if (SecretLength < ISCSI_CHAP_SECRET_MIN_LEN) {\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  Md5ContextSize = Md5GetContextSize ();\r
+  Md5Ctx = AllocatePool (Md5ContextSize);\r
+  if (Md5Ctx == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = EFI_PROTOCOL_ERROR;\r
+\r
+  if (!Md5Init (Md5Ctx)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Hash Identifier - Only calculate 1 byte data (RFC1994)\r
+  //\r
+  IdByte[0] = (CHAR8) ChapIdentifier;\r
+  if (!Md5Update (Md5Ctx, IdByte, 1)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Hash Secret\r
+  //\r
+  if (!Md5Update (Md5Ctx, ChapSecret, SecretLength)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Hash Challenge received from Target\r
+  //\r
+  if (!Md5Update (Md5Ctx, ChapChallenge, ChallengeLength)) {\r
+    goto Exit;\r
+  }\r
+\r
+  if (Md5Final (Md5Ctx, ChapResponse)) {\r
+    Status = EFI_SUCCESS;\r
+  }\r
+\r
+Exit:\r
+  FreePool (Md5Ctx);\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
+  @retval Others                    Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiCHAPAuthTarget (\r
+  IN  ISCSI_CHAP_AUTH_DATA  *AuthData,\r
+  IN  UINT8                 *TargetResponse\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT32      SecretSize;\r
+  UINT8       VerifyRsp[ISCSI_CHAP_RSP_LEN];\r
+\r
+  Status      = EFI_SUCCESS;\r
+\r
+  SecretSize  = (UINT32) AsciiStrLen (AuthData->AuthConfig->ReverseCHAPSecret);\r
+  Status = IScsiCHAPCalculateResponse (\r
+             AuthData->OutIdentifier,\r
+             AuthData->AuthConfig->ReverseCHAPSecret,\r
+             SecretSize,\r
+             AuthData->OutChallenge,\r
+             AuthData->OutChallengeLength,\r
+             VerifyRsp\r
+             );\r
+\r
+  if (CompareMem (VerifyRsp, TargetResponse, ISCSI_CHAP_RSP_LEN) != 0) {\r
+    Status = EFI_SECURITY_VIOLATION;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function checks the received iSCSI Login Response during the security\r
+  negotiation stage.\r
+\r
+  @param[in] Conn             The iSCSI connection.\r
+\r
+  @retval EFI_SUCCESS          The Login Response passed the CHAP validation.\r
+  @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+  @retval EFI_PROTOCOL_ERROR   Some kind of protocol error occurred.\r
+  @retval Others               Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiCHAPOnRspReceived (\r
+  IN ISCSI_CONNECTION  *Conn\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  ISCSI_SESSION               *Session;\r
+  ISCSI_CHAP_AUTH_DATA        *AuthData;\r
+  CHAR8                       *Value;\r
+  UINT8                       *Data;\r
+  UINT32                      Len;\r
+  LIST_ENTRY                  *KeyValueList;\r
+  UINTN                       Algorithm;\r
+  CHAR8                       *Identifier;\r
+  CHAR8                       *Challenge;\r
+  CHAR8                       *Name;\r
+  CHAR8                       *Response;\r
+  UINT8                       TargetRsp[ISCSI_CHAP_RSP_LEN];\r
+  UINT32                      RspLen;\r
+  UINTN                       Result;\r
+\r
+  ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);\r
+  ASSERT (Conn->RspQue.BufNum != 0);\r
+\r
+  Session     = Conn->Session;\r
+  AuthData    = &Session->AuthData.CHAP;\r
+  Len         = Conn->RspQue.BufSize;\r
+  Data        = AllocateZeroPool (Len);\r
+  if (Data == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // Copy the data in case the data spans over multiple PDUs.\r
+  //\r
+  NetbufQueCopy (&Conn->RspQue, 0, Len, Data);\r
+\r
+  //\r
+  // Build the key-value list from the data segment of the Login Response.\r
+  //\r
+  KeyValueList = IScsiBuildKeyValueList ((CHAR8 *) Data, Len);\r
+  if (KeyValueList == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = EFI_PROTOCOL_ERROR;\r
+\r
+  switch (Conn->AuthStep) {\r
+  case ISCSI_AUTH_INITIAL:\r
+    //\r
+    // The first Login Response.\r
+    //\r
+    Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG);\r
+    if (Value == NULL) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Result = IScsiNetNtoi (Value);\r
+    if (Result > 0xFFFF) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Session->TargetPortalGroupTag = (UINT16) Result;\r
+\r
+    Value                         = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_AUTH_METHOD);\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
+    //\r
+    if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {\r
+      if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) != 0) {\r
+        goto ON_EXIT;\r
+      }\r
+    } else if (Session->AuthType == ISCSI_AUTH_TYPE_CHAP) {\r
+      if (AsciiStrCmp (Value, ISCSI_AUTH_METHOD_CHAP) != 0) {\r
+        goto ON_EXIT;\r
+      }\r
+    } else {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    //\r
+    // Transit to CHAP step one.\r
+    //\r
+    Conn->AuthStep  = ISCSI_CHAP_STEP_ONE;\r
+    Status          = EFI_SUCCESS;\r
+    break;\r
+\r
+  case ISCSI_CHAP_STEP_TWO:\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
+    if (Value == NULL) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Algorithm = IScsiNetNtoi (Value);\r
+    if (Algorithm != ISCSI_CHAP_ALGORITHM_MD5) {\r
+      //\r
+      // Unsupported algorithm is chosen by target.\r
+      //\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Identifier = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_IDENTIFIER);\r
+    if (Identifier == NULL) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Challenge = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_CHALLENGE);\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
+    Result = IScsiNetNtoi (Identifier);\r
+    if (Result > 0xFF) {\r
+      goto ON_EXIT;\r
+    }    \r
+    \r
+    AuthData->InIdentifier      = (UINT32) Result;\r
+    AuthData->InChallengeLength = ISCSI_CHAP_AUTH_MAX_LEN;\r
+    IScsiHexToBin ((UINT8 *) AuthData->InChallenge, &AuthData->InChallengeLength, Challenge);\r
+    Status = IScsiCHAPCalculateResponse (\r
+               AuthData->InIdentifier,\r
+               AuthData->AuthConfig->CHAPSecret,\r
+               (UINT32) AsciiStrLen (AuthData->AuthConfig->CHAPSecret),\r
+               AuthData->InChallenge,\r
+               AuthData->InChallengeLength,\r
+               AuthData->CHAPResponse\r
+               );\r
+\r
+    //\r
+    // Transit to next step.\r
+    //\r
+    Conn->AuthStep = ISCSI_CHAP_STEP_THREE;\r
+    break;\r
+\r
+  case ISCSI_CHAP_STEP_THREE:\r
+    //\r
+    // One way CHAP authentication and the target would like to\r
+    // authenticate us.\r
+    //\r
+    Status = EFI_SUCCESS;\r
+    break;\r
+\r
+  case ISCSI_CHAP_STEP_FOUR:\r
+    ASSERT (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL);\r
+    //\r
+    // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.\r
+    //\r
+    Name = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_NAME);\r
+    if (Name == NULL) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Response = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_RESPONSE);\r
+    if (Response == NULL) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    RspLen = ISCSI_CHAP_RSP_LEN;\r
+    IScsiHexToBin (TargetRsp, &RspLen, Response);\r
+\r
+    //\r
+    // Check the CHAP Name and Response replied by Target.\r
+    //\r
+    Status = IScsiCHAPAuthTarget (AuthData, TargetRsp);\r
+    break;\r
+\r
+  default:\r
+    break;\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  if (KeyValueList != NULL) {\r
+    IScsiFreeKeyValueList (KeyValueList);\r
+  }  \r
+\r
+  FreePool (Data);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function fills the CHAP authentication information into the login PDU\r
+  during the security negotiation stage in the iSCSI connection login.\r
+\r
+  @param[in]       Conn        The iSCSI connection.\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
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.\r
+  @retval EFI_PROTOCOL_ERROR    Some kind of protocol error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiCHAPToSendReq (\r
+  IN      ISCSI_CONNECTION  *Conn,\r
+  IN OUT  NET_BUF           *Pdu\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  ISCSI_SESSION               *Session;\r
+  ISCSI_LOGIN_REQUEST         *LoginReq;\r
+  ISCSI_CHAP_AUTH_DATA        *AuthData;\r
+  CHAR8                       *Value;\r
+  CHAR8                       ValueStr[256];\r
+  CHAR8                       *Response;\r
+  UINT32                      RspLen;\r
+  CHAR8                       *Challenge;\r
+  UINT32                      ChallengeLen;\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
+  Status      = EFI_SUCCESS;\r
+\r
+  RspLen      = 2 * ISCSI_CHAP_RSP_LEN + 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
+  Challenge     = AllocateZeroPool (ChallengeLen);\r
+  if (Challenge == NULL) {\r
+    FreePool (Response);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  switch (Conn->AuthStep) {\r
+  case ISCSI_AUTH_INITIAL:\r
+    //\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 (Pdu, ISCSI_KEY_SESSION_TYPE, "Normal");\r
+    IScsiAddKeyValuePair (\r
+      Pdu,\r
+      ISCSI_KEY_TARGET_NAME,\r
+      Session->ConfigData->SessionConfigData.TargetName\r
+      );\r
+\r
+    if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {\r
+      Value = ISCSI_KEY_VALUE_NONE;\r
+      ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);\r
+    } else {\r
+      Value = ISCSI_AUTH_METHOD_CHAP;\r
+    }\r
+\r
+    IScsiAddKeyValuePair (Pdu, ISCSI_KEY_AUTH_METHOD, Value);\r
+\r
+    break;\r
+\r
+  case ISCSI_CHAP_STEP_ONE:\r
+    //\r
+    // First step, send the Login Request with CHAP_A=<A1,A2...> key-value pair.\r
+    //\r
+    AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", ISCSI_CHAP_ALGORITHM_MD5);\r
+    IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, ValueStr);\r
+\r
+    Conn->AuthStep = ISCSI_CHAP_STEP_TWO;\r
+    break;\r
+\r
+  case ISCSI_CHAP_STEP_THREE:\r
+    //\r
+    // Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or\r
+    // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target authentication is\r
+    // required too.\r
+    //\r
+    // CHAP_N=<N>\r
+    //\r
+    IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_NAME, (CHAR8 *) &AuthData->AuthConfig->CHAPName);\r
+    //\r
+    // CHAP_R=<R>\r
+    //\r
+    IScsiBinToHex ((UINT8 *) AuthData->CHAPResponse, ISCSI_CHAP_RSP_LEN, Response, &RspLen);\r
+    IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_RESPONSE, Response);\r
+\r
+    if (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL) {\r
+      //\r
+      // CHAP_I=<I>\r
+      //\r
+      IScsiGenRandom ((UINT8 *) &AuthData->OutIdentifier, 1);\r
+      AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", AuthData->OutIdentifier);\r
+      IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_IDENTIFIER, ValueStr);\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
+      IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_CHALLENGE, Challenge);\r
+\r
+      Conn->AuthStep = ISCSI_CHAP_STEP_FOUR;\r
+    }\r
+    //\r
+    // Set the stage transition flag.\r
+    //\r
+    ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);\r
+    break;\r
+\r
+  default:\r
+    Status = EFI_PROTOCOL_ERROR;\r
+    break;\r
+  }\r
+\r
+  FreePool (Response);\r
+  FreePool (Challenge);\r
+\r
+  return Status;\r
+}\r