]> git.proxmox.com Git - mirror_edk2.git/commitdiff
NetworkPkg/IScsiDxe: support multiple hash algorithms for CHAP
authorLaszlo Ersek <lersek@redhat.com>
Tue, 29 Jun 2021 16:33:35 +0000 (18:33 +0200)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Wed, 30 Jun 2021 19:20:41 +0000 (19:20 +0000)
Introduce the "mChapHash" table, containing the hash algorithms supported
for CHAP. Hash algos listed at the beginning of the table are preferred by
the initiator.

In ISCSI_CHAP_STEP_ONE, send such a CHAP_A value that is the
comma-separated, ordered list of algorithm identifiers from "mChapHash".
Pre-format this value string at driver startup, in the new function
IScsiCHAPInitHashList().

(In IScsiCHAPInitHashList(), also enforce that every hash algo's digest
size fit into ISCSI_CHAP_MAX_DIGEST_SIZE, as the latter controls the
digest, outgoing challenge, and hex *allocations*.)

In ISCSI_CHAP_STEP_TWO, allow the target to select one of the offered hash
algorithms, and remember the selection for the later steps. For
ISCSI_CHAP_STEP_THREE, hash the challenge from the target with the
selected hash algo.

In ISCSI_CHAP_STEP_THREE, send the correctly sized digest to the target.
If the initiator wants mutual authentication, then generate a challenge
with as many bytes as the target's digest will have, in
ISCSI_CHAP_STEP_FOUR.

In ISCSI_CHAP_STEP_FOUR (i.e., when mutual authentication is required by
the initiator), verify the target's response (digest) with the selected
algorithm.

Clear the selected hash algorithm before every login (remember that in
IScsiDxe, every login is a leading login).

There is no peer-observable change from this patch, as it only reworks the
current MD5 support into the new internal representation.

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>
Message-Id: <20210629163337.14120-5-lersek@redhat.com>
Reviewed-by: Maciej Rabeda <maciej.rabeda@linux.intel.com>
NetworkPkg/IScsiDxe/IScsiCHAP.c
NetworkPkg/IScsiDxe/IScsiCHAP.h
NetworkPkg/IScsiDxe/IScsiDriver.c
NetworkPkg/IScsiDxe/IScsiProto.c

index 744824e63d233e7fa0562796b913c5e9d13b8648..351bf329b73996bec9fa101317da8624d82239fa 100644 (file)
@@ -9,6 +9,39 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 \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
@@ -17,6 +50,10 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
   @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
@@ -24,8 +61,9 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
   @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
@@ -35,11 +73,12 @@ IScsiCHAPCalculateResponse (
   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
@@ -47,15 +86,17 @@ IScsiCHAPCalculateResponse (
     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
@@ -63,30 +104,30 @@ IScsiCHAPCalculateResponse (
   // 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
@@ -113,20 +154,30 @@ IScsiCHAPAuthTarget (
   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
@@ -166,6 +217,7 @@ IScsiCHAPOnRspReceived (
   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
@@ -257,12 +309,22 @@ IScsiCHAPOnRspReceived (
     }\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
@@ -305,6 +367,7 @@ IScsiCHAPOnRspReceived (
                (UINT32) AsciiStrLen (AuthData->AuthConfig->CHAPSecret),\r
                AuthData->InChallenge,\r
                AuthData->InChallengeLength,\r
+               AuthData->Hash,\r
                AuthData->CHAPResponse\r
                );\r
 \r
@@ -340,9 +403,10 @@ IScsiCHAPOnRspReceived (
       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
@@ -458,8 +522,7 @@ IScsiCHAPToSendReq (
     // 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
@@ -480,9 +543,10 @@ IScsiCHAPToSendReq (
     //\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
@@ -499,10 +563,13 @@ IScsiCHAPToSendReq (
       //\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
@@ -527,3 +594,60 @@ IScsiCHAPToSendReq (
 \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
index b8811b7580f037e300f68ad893b9206086244159..1e5cc0b287edc398b005571902efce018eba7ffd 100644 (file)
@@ -47,6 +47,45 @@ typedef struct _ISCSI_CHAP_AUTH_CONFIG_NVDATA {
 \r
 #pragma pack()\r
 \r
+//\r
+// Typedefs for collecting sets of hash APIs from BaseCryptLib.\r
+//\r
+typedef\r
+UINTN\r
+(EFIAPI *CHAP_HASH_GET_CONTEXT_SIZE) (\r
+  VOID\r
+  );\r
+\r
+typedef\r
+BOOLEAN\r
+(EFIAPI *CHAP_HASH_INIT) (\r
+  OUT VOID *Context\r
+  );\r
+\r
+typedef\r
+BOOLEAN\r
+(EFIAPI *CHAP_HASH_UPDATE) (\r
+  IN OUT VOID       *Context,\r
+  IN     CONST VOID *Data,\r
+  IN     UINTN      DataSize\r
+  );\r
+\r
+typedef\r
+BOOLEAN\r
+(EFIAPI *CHAP_HASH_FINAL) (\r
+  IN OUT VOID  *Context,\r
+  OUT    UINT8 *HashValue\r
+  );\r
+\r
+typedef struct {\r
+  UINT8                      Algorithm;      // ISCSI_CHAP_ALGORITHM_*, CHAP_A\r
+  UINT32                     DigestSize;\r
+  CHAP_HASH_GET_CONTEXT_SIZE GetContextSize;\r
+  CHAP_HASH_INIT             Init;\r
+  CHAP_HASH_UPDATE           Update;\r
+  CHAP_HASH_FINAL            Final;\r
+} CHAP_HASH;\r
+\r
 ///\r
 /// ISCSI CHAP Authentication Data\r
 ///\r
@@ -56,6 +95,11 @@ typedef struct _ISCSI_CHAP_AUTH_DATA {
   UINT8                         InChallenge[1024];\r
   UINT32                        InChallengeLength;\r
   //\r
+  // The hash algorithm (CHAP_A) that the target selects in\r
+  // ISCSI_CHAP_STEP_TWO.\r
+  //\r
+  CONST CHAP_HASH               *Hash;\r
+  //\r
   // Calculated CHAP Response (CHAP_R) value.\r
   //\r
   UINT8                         CHAPResponse[ISCSI_CHAP_MAX_DIGEST_SIZE];\r
@@ -108,4 +152,15 @@ IScsiCHAPToSendReq (
   IN OUT  NET_BUF           *Pdu\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
 #endif\r
index 98b73308c118d675a3764e8193ae83420b145ee3..485c929721138ce4e70553441924ebd1a4738bfa 100644 (file)
@@ -1779,6 +1779,8 @@ IScsiDriverEntryPoint (
   //\r
   // Create the private data structures.\r
   //\r
+  IScsiCHAPInitHashList ();\r
+\r
   mPrivate = AllocateZeroPool (sizeof (ISCSI_PRIVATE_DATA));\r
   if (mPrivate == NULL) {\r
     Status = EFI_OUT_OF_RESOURCES;\r
index 69d1b39dbb1f9f30927cbdfbc6b49c6b7a090de5..e62736bc041f8ec665ef35832189f27fedcc0ead 100644 (file)
@@ -432,6 +432,9 @@ IScsiSessionResetAuthData (
   IN OUT ISCSI_SESSION *Session\r
   )\r
 {\r
+  if (Session->AuthType == ISCSI_AUTH_TYPE_CHAP) {\r
+    Session->AuthData.CHAP.Hash = NULL;\r
+  }\r
 }\r
 \r
 /**\r