[PATCH] MDEV-25785 Add support for OpenSSL 3.0
authorDebian MySQL Maintainers <pkg-mysql-maint@lists.alioth.debian.org>
Sun, 22 May 2022 23:44:02 +0000 (00:44 +0100)
committerOtto Kekäläinen <otto@debian.org>
Sun, 22 May 2022 23:44:02 +0000 (00:44 +0100)
Summary of changes

- MD_CTX_SIZE is increased

- EVP_CIPHER_CTX_buf_noconst(ctx) does not work anymore, points
  to nobody knows where. The assumption made previously was that
  (since the function does not seem to be documented)
  was that it points to the last partial source block.
  Add own partial block buffer for NOPAD encryption instead

- SECLEVEL in CipherString in openssl.cnf
  had been downgraded to 0, from 1, to make TLSv1.0 and TLSv1.1 possible

- Workaround Ssl_cipher_list issue, it now returns TLSv1.3 ciphers,
  in addition to what was set in --ssl-cipher

- ctx_buf buffer now must be aligned to 16 bytes with openssl(
  previously with WolfSSL only), or crashes will happen

- updated aes-t , to be better debuggable
  using function, rather than a huge multiline macro
  added test that does "nopad" encryption piece-wise, to test
  replacement of EVP_CIPHER_CTX_buf_noconst

Gbp-Pq: Name openssl-3-compatibility.patch

cmake/ssl.cmake
include/ssl_compat.h
mysql-test/lib/openssl.cnf
mysql-test/main/ssl_cipher.result
mysql-test/main/ssl_cipher.test
mysys_ssl/my_crypt.cc
unittest/mysys/aes-t.c

index 7c2488be8bdcc5a8e8a991530c82dd8c6283f895..64c93ff9b4f2f0816f3c262705b4237077c260c3 100644 (file)
@@ -139,9 +139,20 @@ MACRO (MYSQL_CHECK_SSL)
       SET(SSL_INTERNAL_INCLUDE_DIRS "")
       SET(SSL_DEFINES "-DHAVE_OPENSSL")
 
+      FOREACH(x INCLUDES LIBRARIES DEFINITIONS)
+        SET(SAVE_CMAKE_REQUIRED_${x} ${CMAKE_REQUIRED_${x}})
+      ENDFOREACH()
+
+      # Silence "deprecated in OpenSSL 3.0"
+      IF((NOT OPENSSL_VERSION) # 3.0 not determined by older cmake
+         OR NOT(OPENSSL_VERSION VERSION_LESS "3.0.0"))
+        SET(SSL_DEFINES "${SSL_DEFINES} -DOPENSSL_API_COMPAT=0x10100000L")
+        SET(CMAKE_REQUIRED_DEFINITIONS -DOPENSSL_API_COMPAT=0x10100000L)
+      ENDIF()
+
       SET(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
       SET(CMAKE_REQUIRED_LIBRARIES ${SSL_LIBRARIES})
-      SET(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
+
       CHECK_SYMBOL_EXISTS(ERR_remove_thread_state "openssl/err.h"
                           HAVE_ERR_remove_thread_state)
       CHECK_SYMBOL_EXISTS(EVP_aes_128_ctr "openssl/evp.h"
@@ -150,8 +161,10 @@ MACRO (MYSQL_CHECK_SSL)
                           HAVE_EncryptAes128Gcm)
       CHECK_SYMBOL_EXISTS(X509_check_host "openssl/x509v3.h"
                           HAVE_X509_check_host)
-      SET(CMAKE_REQUIRED_INCLUDES)
-      SET(CMAKE_REQUIRED_LIBRARIES)
+
+      FOREACH(x INCLUDES LIBRARIES DEFINITIONS)
+        SET(CMAKE_REQUIRED_${x} ${SAVE_CMAKE_REQUIRED_${x}})
+      ENDFOREACH()
     ELSE()
       IF(WITH_SSL STREQUAL "system")
         MESSAGE(FATAL_ERROR "Cannot find appropriate system libraries for SSL. Use WITH_SSL=bundled to enable SSL support")
index 8dc1225407ed23489776746d529d1b90081a9be2..f73a6b0b9a7fcb4eb569f8973a37fc36d99b32d3 100644 (file)
@@ -24,7 +24,7 @@
 #define SSL_LIBRARY OpenSSL_version(OPENSSL_VERSION)
 #define ERR_remove_state(X) ERR_clear_error()
 #define EVP_CIPHER_CTX_SIZE 176
-#define EVP_MD_CTX_SIZE 48
+#define EVP_MD_CTX_SIZE 72
 #undef EVP_MD_CTX_init
 #define EVP_MD_CTX_init(X) do { memset((X), 0, EVP_MD_CTX_SIZE); EVP_MD_CTX_reset(X); } while(0)
 #undef EVP_CIPHER_CTX_init
index b9ab37ac3a17f31f47da3ba68b2759b20a0f4b57..7cd6f748af2052cbc0ac579c0d818e2105b64ab0 100644 (file)
@@ -9,4 +9,4 @@ ssl_conf = ssl_section
 system_default = system_default_section
 
 [system_default_section]
-CipherString = ALL:@SECLEVEL=1
+CipherString = ALL:@SECLEVEL=0
index 930d384eda962f00c3fd023219734fc76d2b51f6..fcc0034bf77fb7d0357de8831ae9ca26e631c908 100644 (file)
@@ -58,11 +58,8 @@ Ssl_cipher   AES256-SHA
 drop user mysqltest_1@localhost;
 # restart: --ssl-cipher=AES128-SHA
 connect  ssl_con,localhost,root,,,,,SSL;
-SHOW STATUS LIKE 'Ssl_cipher';
-Variable_name  Value
-Ssl_cipher     AES128-SHA
-SHOW STATUS LIKE 'Ssl_cipher_list';
-Variable_name  Value
-Ssl_cipher_list        AES128-SHA
+SELECT VARIABLE_VALUE like '%AES128-SHA%' FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME='Ssl_cipher_list';
+VARIABLE_VALUE like '%AES128-SHA%'
+1
 disconnect ssl_con;
 connection default;
index 36549d76d026706431e910c7072188a27f117cdf..f4ee8737f4156294ae81a0fad3579422db78da34 100644 (file)
@@ -97,7 +97,6 @@ drop user mysqltest_1@localhost;
 let $restart_parameters=--ssl-cipher=AES128-SHA;
 source include/restart_mysqld.inc;
 connect (ssl_con,localhost,root,,,,,SSL);
-SHOW STATUS LIKE 'Ssl_cipher';
-SHOW STATUS LIKE 'Ssl_cipher_list';
+SELECT VARIABLE_VALUE like '%AES128-SHA%' FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME='Ssl_cipher_list';
 disconnect ssl_con;
 connection default;
index e512eee9066c4b5d365571d998bfebbdb19557ef..4d7ebc7bd276514cd01abb2e4c7559225c4ed935 100644 (file)
 #include <ssl_compat.h>
 #include <cstdint>
 
-#ifdef HAVE_WOLFSSL
 #define CTX_ALIGN 16
-#else
-#define CTX_ALIGN 0
-#endif
 
 class MyCTX
 {
@@ -100,8 +96,9 @@ class MyCTX_nopad : public MyCTX
 {
 public:
   const uchar *key;
-  uint klen, buf_len;
+  uint klen, source_tail_len;
   uchar oiv[MY_AES_BLOCK_SIZE];
+  uchar source_tail[MY_AES_BLOCK_SIZE];
 
   MyCTX_nopad() : MyCTX() { }
   ~MyCTX_nopad() { }
@@ -112,7 +109,7 @@ public:
     compile_time_assert(MY_AES_CTX_SIZE >= sizeof(MyCTX_nopad));
     this->key= key;
     this->klen= klen;
-    this->buf_len= 0;
+    this->source_tail_len= 0;
     if (ivlen)
       memcpy(oiv, iv, ivlen);
     DBUG_ASSERT(ivlen == 0 || ivlen == sizeof(oiv));
@@ -123,26 +120,41 @@ public:
     return res;
   }
 
+  /** Update last partial source block, stored in source_tail array. */
+  void update_source_tail(const uchar* src, uint slen)
+  {
+    if (!slen)
+      return;
+    uint new_tail_len= (source_tail_len + slen) % MY_AES_BLOCK_SIZE;
+    if (new_tail_len)
+    {
+      if (slen + source_tail_len < MY_AES_BLOCK_SIZE)
+      {
+        memcpy(source_tail + source_tail_len, src, slen);
+      }
+      else
+      {
+        DBUG_ASSERT(slen > new_tail_len);
+        memcpy(source_tail, src + slen - new_tail_len, new_tail_len);
+      }
+    }
+    source_tail_len= new_tail_len;
+  }
+
   int update(const uchar *src, uint slen, uchar *dst, uint *dlen)
   {
-    buf_len+= slen;
+    update_source_tail(src, slen);
     return MyCTX::update(src, slen, dst, dlen);
   }
 
   int finish(uchar *dst, uint *dlen)
   {
-    buf_len %= MY_AES_BLOCK_SIZE;
-    if (buf_len)
+    if (source_tail_len)
     {
-      uchar *buf= EVP_CIPHER_CTX_buf_noconst(ctx);
       /*
         Not much we can do, block ciphers cannot encrypt data that aren't
         a multiple of the block length. At least not without padding.
         Let's do something CTR-like for the last partial block.
-
-        NOTE this assumes that there are only buf_len bytes in the buf.
-        If OpenSSL will change that, we'll need to change the implementation
-        of this class too.
       */
       uchar mask[MY_AES_BLOCK_SIZE];
       uint mlen;
@@ -154,10 +166,10 @@ public:
         return rc;
       DBUG_ASSERT(mlen == sizeof(mask));
 
-      for (uint i=0; i < buf_len; i++)
-        dst[i]= buf[i] ^ mask[i];
+      for (uint i=0; i < source_tail_len; i++)
+        dst[i]= source_tail[i] ^ mask[i];
     }
-    *dlen= buf_len;
+    *dlen= source_tail_len;
     return MY_AES_OK;
   }
 };
index 34704e067496e992144fdd949f83af92016d3fd4..cbec27609413f5b538922f14829d1bf0dae4d15d 100644 (file)
 #include <string.h>
 #include <ctype.h>
 
-#define DO_TEST(mode, nopad, slen, fill, dlen, hash)                    \
-  SKIP_BLOCK_IF(mode == 0xDEADBEAF, nopad ? 4 : 5, #mode " not supported")     \
-  {                                                                     \
-    memset(src, fill, src_len= slen);                                   \
-    ok(my_aes_crypt(mode, nopad | ENCRYPTION_FLAG_ENCRYPT,              \
-                    src, src_len, dst, &dst_len,                        \
-                    key, sizeof(key), iv, sizeof(iv)) == MY_AES_OK,     \
-      "encrypt " #mode " %u %s", src_len, nopad ? "nopad" : "pad");     \
-    if (!nopad)                                                         \
-      ok (dst_len == my_aes_get_size(mode, src_len), "my_aes_get_size");\
-    my_md5(md5, (char*)dst, dst_len);                                   \
-    ok(dst_len == dlen && memcmp(md5, hash, sizeof(md5)) == 0, "md5");  \
-    ok(my_aes_crypt(mode, nopad | ENCRYPTION_FLAG_DECRYPT,              \
-                    dst, dst_len, ddst, &ddst_len,                      \
-                    key, sizeof(key), iv, sizeof(iv)) == MY_AES_OK,     \
-       "decrypt " #mode " %u", dst_len);                                \
-    ok(ddst_len == src_len && memcmp(src, ddst, src_len) == 0, "memcmp"); \
+
+/** Test streaming encryption, bytewise update.*/
+static int aes_crypt_bytewise(enum my_aes_mode mode, int flags, const unsigned char *src,
+                 unsigned int slen, unsigned char *dst, unsigned int *dlen,
+                 const unsigned char *key, unsigned int klen,
+                 const unsigned char *iv, unsigned int ivlen)
+{
+  /* Allocate context on odd address on stack, in order to
+   catch misalignment errors.*/
+  void *ctx= (char *)alloca(MY_AES_CTX_SIZE+1)+1;
+
+  int res1, res2;
+  uint d1= 0, d2;
+  uint i;
+
+  if ((res1= my_aes_crypt_init(ctx, mode, flags, key, klen, iv, ivlen)))
+    return res1;
+  for (i= 0; i < slen; i++)
+  {
+    uint tmp_d1=0;
+    res1= my_aes_crypt_update(ctx, src+i,1, dst, &tmp_d1);
+    if (res1)
+      return res1;
+    d1+= tmp_d1;
+    dst+= tmp_d1;
+  }
+  res2= my_aes_crypt_finish(ctx, dst, &d2);
+  *dlen= d1 + d2;
+  return res1 ? res1 : res2;
+}
+
+
+#ifndef HAVE_EncryptAes128Ctr
+const uint MY_AES_CTR=0xDEADBEAF;
+#endif
+#ifndef HAVE_EncryptAes128Gcm
+const uint MY_AES_GCM=0xDEADBEAF;
+#endif
+
+#define MY_AES_UNSUPPORTED(x)  (x == 0xDEADBEAF)
+
+static void do_test(uint mode, const char *mode_str, int nopad, uint slen,
+                    char fill, size_t dlen, const char *hash)
+{
+  uchar key[16]= {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6};
+  uchar iv[16]= {2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7};
+  uchar src[1000], dst[1100], dst2[1100], ddst[1000];
+  uchar md5[MY_MD5_HASH_SIZE];
+  uint src_len, dst_len, dst_len2, ddst_len;
+  int result;
+
+  if (MY_AES_UNSUPPORTED(mode))
+  {
+    skip(nopad?7:6, "%s not supported", mode_str);
+    return;
+  }
+  memset(src, fill, src_len= slen);
+  result= my_aes_crypt(mode, nopad | ENCRYPTION_FLAG_ENCRYPT, src, src_len,
+                       dst, &dst_len, key, sizeof(key), iv, sizeof(iv));
+  ok(result == MY_AES_OK, "encrypt %s %u %s", mode_str, src_len,
+     nopad ? "nopad" : "pad");
+
+  if (nopad)
+  {
+    result= aes_crypt_bytewise(mode, nopad | ENCRYPTION_FLAG_ENCRYPT, src,
+                                src_len, dst2, &dst_len2, key, sizeof(key),
+                                iv, sizeof(iv));
+    ok(result == MY_AES_OK, "encrypt bytewise %s %u", mode_str, src_len);
+    /* Compare with non-bytewise encryption result*/
+    ok(dst_len == dst_len2 && memcmp(dst, dst2, dst_len) == 0,
+       "memcmp bytewise  %s %u", mode_str, src_len);
+  }
+  else
+  {
+    int dst_len_real= my_aes_get_size(mode, src_len);
+    ok(dst_len_real= dst_len, "my_aes_get_size");
   }
+  my_md5(md5, (char *) dst, dst_len);
+  ok(dst_len == dlen, "md5 len");
+  ok(memcmp(md5, hash, sizeof(md5)) == 0, "md5");
+  result= my_aes_crypt(mode, nopad | ENCRYPTION_FLAG_DECRYPT,
+                       dst, dst_len, ddst, &ddst_len, key, sizeof(key), iv,
+                       sizeof(iv));
+
+  ok(result == MY_AES_OK, "decrypt %s %u", mode_str, dst_len);
+  ok(ddst_len == src_len && memcmp(src, ddst, src_len) == 0, "memcmp");
+}
 
-#define DO_TEST_P(M,S,F,D,H) DO_TEST(M,0,S,F,D,H)
-#define DO_TEST_N(M,S,F,D,H) DO_TEST(M,ENCRYPTION_FLAG_NOPAD,S,F,D,H)
+#define DO_TEST_P(M, S, F, D, H) do_test(M, #M, 0, S, F, D, H)
+#define DO_TEST_N(M, S, F, D, H) do_test(M, #M, ENCRYPTION_FLAG_NOPAD, S, F, D, H)
 
 /* useful macro for debugging */
 #define PRINT_MD5()                                     \
     printf("\"\n");                                     \
   } while(0);
 
-#ifndef HAVE_EncryptAes128Ctr
-const uint MY_AES_CTR=0xDEADBEAF;
-#endif
-#ifndef HAVE_EncryptAes128Gcm
-const uint MY_AES_GCM=0xDEADBEAF;
-#endif
 
 int
 main(int argc __attribute__((unused)),char *argv[])
 {
-  uchar key[16]= {1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6};
-  uchar iv[16]=  {2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7};
-  uchar src[1000], dst[1100], ddst[1000];
-  uchar md5[MY_MD5_HASH_SIZE];
-  uint src_len, dst_len, ddst_len;
 
   MY_INIT(argv[0]);
 
-  plan(87);
+  plan(122);
+
   DO_TEST_P(MY_AES_ECB, 200, '.', 208, "\xd8\x73\x8e\x3a\xbc\x66\x99\x13\x7f\x90\x23\x52\xee\x97\x6f\x9a");
   DO_TEST_P(MY_AES_ECB, 128, '?', 144, "\x19\x58\x33\x85\x4c\xaa\x7f\x06\xd1\xb2\xec\xd7\xb7\x6a\xa9\x5b");
   DO_TEST_P(MY_AES_CBC, 159, '%', 160, "\x4b\x03\x18\x3d\xf1\xa7\xcd\xa1\x46\xb3\xc6\x8a\x92\xc0\x0f\xc9");