public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: Laszlo Ersek <lersek@redhat.com>
To: edk2-devel-01 <edk2-devel@lists.01.org>
Cc: Jiaxin Wu <jiaxin.wu@intel.com>, Qin Long <qin.long@intel.com>,
	Siyuan Fu <siyuan.fu@intel.com>, Ting Ye <ting.ye@intel.com>
Subject: [PATCH 13/13] CryptoPkg/TlsLib: rewrite TlsSetCipherList()
Date: Tue,  3 Apr 2018 16:51:49 +0200	[thread overview]
Message-ID: <20180403145149.8925-14-lersek@redhat.com> (raw)
In-Reply-To: <20180403145149.8925-1-lersek@redhat.com>

Rewrite the TlsSetCipherList() function in order to fix the following
issues:

- Any cipher identifier in CipherId that is not recognized by
  TlsGetCipherMapping() will cause the function to return EFI_UNSUPPORTED.

  This is a problem because CipherId is an ordered preference list, and a
  caller should not get EFI_UNSUPPORTED just because it has an elaborate
  CipherId preference list. Instead, we can filter out cipher identifiers
  that we don't recognize, as long as we keep the relative order intact.

- CipherString is allocated on the stack, with 500 bytes.

  While processing a large CipherId preference list, this room may not be
  enough. Although no buffer overflow is possible, CipherString exhaustion
  can lead to a failed TLS connection, because any cipher names that don't
  fit on CipherString cannot be negotiated.

  Compute CipherStringSize first, and allocate CipherString dynamically.

- Finally, the "@STRENGTH" pseudo cipher name is appended to CipherString.
  (Assuming there is enough room left in CipherString.) This causes
  OpenSSL to sort the cipher list "in order of encryption algorithm key
  length".

  This is a bad idea. The caller specifically passes an ordered preference
  list in CipherId. Therefore TlsSetCipherList() must not ask OpenSSL to
  reorder the list, for any reason. Drop "@STRENGTH".

Cc: Jiaxin Wu <jiaxin.wu@intel.com>
Cc: Qin Long <qin.long@intel.com>
Cc: Siyuan Fu <siyuan.fu@intel.com>
Cc: Ting Ye <ting.ye@intel.com>
Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=915
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
---
 CryptoPkg/Library/TlsLib/TlsLib.inf       |   3 +-
 CryptoPkg/Include/Library/TlsLib.h        |   3 +-
 CryptoPkg/Library/TlsLib/InternalTlsLib.h |   3 +-
 CryptoPkg/Library/TlsLib/TlsConfig.c      | 163 +++++++++++++++++---
 4 files changed, 149 insertions(+), 23 deletions(-)

diff --git a/CryptoPkg/Library/TlsLib/TlsLib.inf b/CryptoPkg/Library/TlsLib/TlsLib.inf
index dbb737b2a147..9b44c9cdab3a 100644
--- a/CryptoPkg/Library/TlsLib/TlsLib.inf
+++ b/CryptoPkg/Library/TlsLib/TlsLib.inf
@@ -37,17 +37,18 @@ [Sources]
 [Packages]
   MdePkg/MdePkg.dec
   CryptoPkg/CryptoPkg.dec
 
 [LibraryClasses]
   BaseCryptLib
-  BaseLib
   BaseMemoryLib
   DebugLib
   IntrinsicLib
+  MemoryAllocationLib
   OpensslLib
+  SafeIntLib
 
 [BuildOptions]
   #
   # suppress the following warnings so we do not break the build with warnings-as-errors:
   # C4090: 'function' : different 'const' qualifiers
   #
diff --git a/CryptoPkg/Include/Library/TlsLib.h b/CryptoPkg/Include/Library/TlsLib.h
index 0ffbcb2b7c2a..e71291eaea45 100644
--- a/CryptoPkg/Include/Library/TlsLib.h
+++ b/CryptoPkg/Include/Library/TlsLib.h
@@ -353,13 +353,14 @@ TlsSetConnectionEnd (
                            Registry of the IANA, interpreting Byte1 and Byte2
                            in network (big endian) byte order.
   @param[in]  CipherNum    The number of cipher in the list.
 
   @retval  EFI_SUCCESS           The ciphers list was set successfully.
   @retval  EFI_INVALID_PARAMETER The parameter is invalid.
-  @retval  EFI_UNSUPPORTED       Unsupported TLS cipher in the list.
+  @retval  EFI_UNSUPPORTED       No supported TLS cipher was found in CipherId.
+  @retval  EFI_OUT_OF_RESOURCES  Memory allocation failed.
 
 **/
 EFI_STATUS
 EFIAPI
 TlsSetCipherList (
   IN     VOID                     *Tls,
diff --git a/CryptoPkg/Library/TlsLib/InternalTlsLib.h b/CryptoPkg/Library/TlsLib/InternalTlsLib.h
index 3f18a461a8d1..b6cf9816aa38 100644
--- a/CryptoPkg/Library/TlsLib/InternalTlsLib.h
+++ b/CryptoPkg/Library/TlsLib/InternalTlsLib.h
@@ -16,15 +16,16 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 #define __INTERNAL_TLS_LIB_H__
 
 #undef _WIN32
 #undef _WIN64
 
 #include <Library/BaseCryptLib.h>
-#include <Library/BaseLib.h>
 #include <Library/BaseMemoryLib.h>
 #include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/SafeIntLib.h>
 #include <openssl/ssl.h>
 #include <openssl/bio.h>
 #include <openssl/err.h>
 
 typedef struct {
   //
diff --git a/CryptoPkg/Library/TlsLib/TlsConfig.c b/CryptoPkg/Library/TlsLib/TlsConfig.c
index ab786fc23849..c7d643fd81f7 100644
--- a/CryptoPkg/Library/TlsLib/TlsConfig.c
+++ b/CryptoPkg/Library/TlsLib/TlsConfig.c
@@ -409,69 +409,192 @@ TlsSetConnectionEnd (
                            Registry of the IANA, interpreting Byte1 and Byte2
                            in network (big endian) byte order.
   @param[in]  CipherNum    The number of cipher in the list.
 
   @retval  EFI_SUCCESS           The ciphers list was set successfully.
   @retval  EFI_INVALID_PARAMETER The parameter is invalid.
-  @retval  EFI_UNSUPPORTED       Unsupported TLS cipher in the list.
+  @retval  EFI_UNSUPPORTED       No supported TLS cipher was found in CipherId.
+  @retval  EFI_OUT_OF_RESOURCES  Memory allocation failed.
 
 **/
 EFI_STATUS
 EFIAPI
 TlsSetCipherList (
   IN     VOID                     *Tls,
   IN     UINT16                   *CipherId,
   IN     UINTN                    CipherNum
   )
 {
   TLS_CONNECTION           *TlsConn;
+  EFI_STATUS               Status;
+  CONST TLS_CIPHER_MAPPING **MappedCipher;
+  UINTN                    MappedCipherBytes;
+  UINTN                    MappedCipherCount;
+  UINTN                    CipherStringSize;
   UINTN                    Index;
   CONST TLS_CIPHER_MAPPING *Mapping;
-  CONST CHAR8              *MappingName;
-  CHAR8                    CipherString[500];
+  CHAR8                    *CipherString;
+  CHAR8                    *CipherStringPosition;
 
   TlsConn = (TLS_CONNECTION *) Tls;
   if (TlsConn == NULL || TlsConn->Ssl == NULL || CipherId == NULL) {
     return EFI_INVALID_PARAMETER;
   }
 
-  Mapping     = NULL;
-  MappingName = NULL;
-
-  memset (CipherString, 0, sizeof (CipherString));
+  //
+  // Allocate the MappedCipher array for recording the mappings that we find
+  // for the input IANA identifiers in CipherId.
+  //
+  Status = SafeUintnMult (CipherNum, sizeof (*MappedCipher),
+             &MappedCipherBytes);
+  if (EFI_ERROR (Status)) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  MappedCipher = AllocatePool (MappedCipherBytes);
+  if (MappedCipher == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
 
+  //
+  // Map the cipher IDs, and count the number of bytes for the full
+  // CipherString.
+  //
+  MappedCipherCount = 0;
+  CipherStringSize = 0;
   for (Index = 0; Index < CipherNum; Index++) {
     //
-    // Handling OpenSSL / RFC Cipher name mapping.
+    // Look up the IANA-to-OpenSSL mapping.
     //
-    Mapping = TlsGetCipherMapping (*(CipherId + Index));
+    Mapping = TlsGetCipherMapping (CipherId[Index]);
     if (Mapping == NULL) {
-      return EFI_UNSUPPORTED;
-    }
-    MappingName = Mapping->OpensslCipher;
-
-    if (Index != 0) {
+      DEBUG ((DEBUG_VERBOSE, "%a:%a: skipping CipherId=0x%04x\n",
+        gEfiCallerBaseName, __FUNCTION__, CipherId[Index]));
       //
-      // The ciphers were separated by a colon.
+      // Skipping the cipher is valid because CipherId is an ordered
+      // preference list of ciphers, thus we can filter it as long as we
+      // don't change the relative order of elements on it.
       //
-      AsciiStrCatS (CipherString, sizeof (CipherString), ":");
+      continue;
+    }
+    //
+    // Accumulate Mapping->OpensslCipherLength into CipherStringSize. If this
+    // is not the first successful mapping, account for a colon (":") prefix
+    // too.
+    //
+    if (MappedCipherCount > 0) {
+      Status = SafeUintnAdd (CipherStringSize, 1, &CipherStringSize);
+      if (EFI_ERROR (Status)) {
+        Status = EFI_OUT_OF_RESOURCES;
+        goto FreeMappedCipher;
+      }
+    }
+    Status = SafeUintnAdd (CipherStringSize, Mapping->OpensslCipherLength,
+               &CipherStringSize);
+    if (EFI_ERROR (Status)) {
+      Status = EFI_OUT_OF_RESOURCES;
+      goto FreeMappedCipher;
     }
+    //
+    // Record the mapping.
+    //
+    MappedCipher[MappedCipherCount++] = Mapping;
+  }
 
-    AsciiStrCatS (CipherString, sizeof (CipherString), MappingName);
+  //
+  // Verify that at least one IANA cipher ID could be mapped; account for the
+  // terminating NUL character in CipherStringSize; allocate CipherString.
+  //
+  if (MappedCipherCount == 0) {
+    DEBUG ((DEBUG_ERROR, "%a:%a: no CipherId could be mapped\n",
+      gEfiCallerBaseName, __FUNCTION__));
+    Status = EFI_UNSUPPORTED;
+    goto FreeMappedCipher;
+  }
+  Status = SafeUintnAdd (CipherStringSize, 1, &CipherStringSize);
+  if (EFI_ERROR (Status)) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto FreeMappedCipher;
+  }
+  CipherString = AllocatePool (CipherStringSize);
+  if (CipherString == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto FreeMappedCipher;
   }
 
-  AsciiStrCatS (CipherString, sizeof (CipherString), ":@STRENGTH");
+  //
+  // Go over the collected mappings and populate CipherString.
+  //
+  CipherStringPosition = CipherString;
+  for (Index = 0; Index < MappedCipherCount; Index++) {
+    Mapping = MappedCipher[Index];
+    //
+    // Append the colon (":") prefix except for the first mapping, then append
+    // Mapping->OpensslCipher.
+    //
+    if (Index > 0) {
+      *(CipherStringPosition++) = ':';
+    }
+    CopyMem (CipherStringPosition, Mapping->OpensslCipher,
+      Mapping->OpensslCipherLength);
+    CipherStringPosition += Mapping->OpensslCipherLength;
+  }
+
+  //
+  // NUL-terminate CipherString.
+  //
+  *(CipherStringPosition++) = '\0';
+  ASSERT (CipherStringPosition == CipherString + CipherStringSize);
+
+  //
+  // Log CipherString for debugging. CipherString can be very long if the
+  // caller provided a large CipherId array, so log CipherString in segments of
+  // 79 non-newline characters. (MAX_DEBUG_MESSAGE_LENGTH is usually 0x100 in
+  // DebugLib instances.)
+  //
+  DEBUG_CODE (
+    UINTN FullLength;
+    UINTN SegmentLength;
+
+    FullLength = CipherStringSize - 1;
+    DEBUG ((DEBUG_VERBOSE, "%a:%a: CipherString={\n", gEfiCallerBaseName,
+      __FUNCTION__));
+    for (CipherStringPosition = CipherString;
+         CipherStringPosition < CipherString + FullLength;
+         CipherStringPosition += SegmentLength) {
+      SegmentLength = FullLength - (CipherStringPosition - CipherString);
+      if (SegmentLength > 79) {
+        SegmentLength = 79;
+      }
+      DEBUG ((DEBUG_VERBOSE, "%.*a\n", SegmentLength, CipherStringPosition));
+    }
+    DEBUG ((DEBUG_VERBOSE, "}\n"));
+    //
+    // Restore the pre-debug value of CipherStringPosition by skipping over the
+    // trailing NUL.
+    //
+    CipherStringPosition++;
+    ASSERT (CipherStringPosition == CipherString + CipherStringSize);
+  );
 
   //
   // Sets the ciphers for use by the Tls object.
   //
   if (SSL_set_cipher_list (TlsConn->Ssl, CipherString) <= 0) {
-    return EFI_UNSUPPORTED;
+    Status = EFI_UNSUPPORTED;
+    goto FreeCipherString;
   }
 
-  return EFI_SUCCESS;
+  Status = EFI_SUCCESS;
+
+FreeCipherString:
+  FreePool (CipherString);
+
+FreeMappedCipher:
+  FreePool (MappedCipher);
+
+  return Status;
 }
 
 /**
   Set the compression method for TLS/SSL operations.
 
   This function handles TLS/SSL integrated compression methods.
-- 
2.14.1.3.gb7cf6e02401b



  parent reply	other threads:[~2018-04-03 14:52 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-04-03 14:51 [PATCH 00/13] {Ovmf, Mde, Network, Crypto}Pkg: fixes+features for setting HTTPS cipher suites Laszlo Ersek
2018-04-03 14:51 ` [PATCH 01/13] OvmfPkg/TlsAuthConfigLib: configure trusted cipher suites for HTTPS boot Laszlo Ersek
2018-04-03 14:51 ` [PATCH 02/13] MdePkg/Include/Protocol/Tls.h: pack structures from the TLS RFC Laszlo Ersek
2018-04-03 15:08   ` Gao, Liming
2018-04-04 10:32     ` Laszlo Ersek
2018-04-10  1:51   ` Fu, Siyuan
2018-04-03 14:51 ` [PATCH 03/13] NetworkPkg/TlsDxe: verify DataSize for EfiTlsCipherList Laszlo Ersek
2018-04-10  1:51   ` Fu, Siyuan
2018-04-03 14:51 ` [PATCH 04/13] NetworkPkg/TlsDxe: clean up byte order conversion " Laszlo Ersek
2018-04-10  1:53   ` Fu, Siyuan
2018-04-03 14:51 ` [PATCH 05/13] CryptoPkg/TlsLib: replace TlsGetCipherString() with TlsGetCipherMapping() Laszlo Ersek
2018-04-03 14:51 ` [PATCH 06/13] CryptoPkg/TlsLib: use binary search in the TlsGetCipherMapping() function Laszlo Ersek
2018-04-03 14:51 ` [PATCH 07/13] CryptoPkg/TlsLib: pre-compute OpensslCipherLength in TlsCipherMappingTable Laszlo Ersek
2018-04-03 14:51 ` [PATCH 08/13] CryptoPkg/TlsLib: add the "TlsMappingTable.sh" POSIX shell script Laszlo Ersek
2018-04-03 14:51 ` [PATCH 09/13] CryptoPkg/TlsLib: extend "TlsCipherMappingTable" Laszlo Ersek
2018-04-03 14:51 ` [PATCH 10/13] CryptoPkg/TlsLib: sort [LibraryClasses] section in the INF file Laszlo Ersek
2018-04-03 14:51 ` [PATCH 11/13] CryptoPkg/TlsLib: sanitize lib classes in internal header and INF Laszlo Ersek
2018-04-03 14:51 ` [PATCH 12/13] CryptoPkg/TlsLib: clean up leading comment for TlsSetCipherList() Laszlo Ersek
2018-04-03 14:51 ` Laszlo Ersek [this message]
2018-04-10  4:09 ` [PATCH 00/13] {Ovmf, Mde, Network, Crypto}Pkg: fixes+features for setting HTTPS cipher suites Wu, Jiaxin
2018-04-10  7:40   ` Long, Qin
2018-04-10 10:02     ` Laszlo Ersek
2018-04-10 10:10       ` Laszlo Ersek
2018-04-10 16:56         ` Long, Qin
2018-04-10  9:47   ` Laszlo Ersek
2018-04-10 17:06     ` Long, Qin
2018-04-10 20:06       ` Laszlo Ersek
2018-04-11  1:59         ` Wu, Jiaxin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180403145149.8925-14-lersek@redhat.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox