From f1081116eeb5b7cdbc5d89772058a2a25bd1a834 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Fri, 18 Sep 2020 15:51:15 +0200
Subject: [PATCH 1/2] Fix pgcrypto to support OpenSSL 3.0.0

Many of the ciphers supported in pgcrypto are deprecated in 3.0.0
and require the legacy provider in order to work. Also explicitly
set up the padding which is now required.
---
 contrib/pgcrypto/openssl.c | 35 ++++++++++++++++++++++++++++++++++-
 doc/src/sgml/pgcrypto.sgml |  6 ++++++
 2 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/contrib/pgcrypto/openssl.c b/contrib/pgcrypto/openssl.c
index 3057afb339..c9e97d5279 100644
--- a/contrib/pgcrypto/openssl.c
+++ b/contrib/pgcrypto/openssl.c
@@ -34,6 +34,14 @@
 #include <openssl/evp.h>
 #include <openssl/err.h>
 #include <openssl/rand.h>
+/*
+ * OPENSSL_VERSION_MAJOR isn't defined at all until OpenSSL 3.0.0, but since
+ * OPENSSL_VERSION_NUMBER is used by both OpenSSL and LibreSSL it's safer to
+ * check for the new macro rather than the overloaded old one.
+ */
+#if OPENSSL_VERSION_MAJOR >= 3
+#include <openssl/provider.h>
+#endif
 
 #include "px.h"
 #include "utils/memutils.h"
@@ -64,6 +72,10 @@ typedef struct OSSLDigest
 	struct OSSLDigest *prev;
 } OSSLDigest;
 
+#if OPENSSL_VERSION_MAJOR >= 3
+static OSSL_PROVIDER *legacy_provider = NULL;
+static OSSL_PROVIDER *default_provider = NULL;
+#endif
 static OSSLDigest *open_digests = NULL;
 static bool digest_resowner_callback_registered = false;
 
@@ -173,8 +185,20 @@ px_find_digest(const char *name, PX_MD **res)
 
 	if (!px_openssl_initialized)
 	{
-		px_openssl_initialized = 1;
+#if OPENSSL_VERSION_MAJOR >= 3
+		if (legacy_provider == NULL)
+			legacy_provider = OSSL_PROVIDER_load(NULL, "legacy");
+		if (default_provider == NULL)
+			default_provider = OSSL_PROVIDER_load(NULL, "default");
+#endif
+
+		/*
+		 * OpenSSL_add_all_algorithms is deprecated in OpenSSL 1.1.0 and no
+		 * longer required in 1.1.0 and later versions as initialization is
+		 * performed automatically.
+		 */
 		OpenSSL_add_all_algorithms();
+		px_openssl_initialized = 1;
 	}
 
 	if (!digest_resowner_callback_registered)
@@ -367,6 +391,7 @@ gen_ossl_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen,
 	{
 		if (!EVP_DecryptInit_ex(od->evp_ctx, od->evp_ciph, NULL, NULL, NULL))
 			return PXE_CIPHER_INIT;
+		EVP_CIPHER_CTX_set_padding(od->evp_ctx, 0);
 		if (!EVP_CIPHER_CTX_set_key_length(od->evp_ctx, od->klen))
 			return PXE_CIPHER_INIT;
 		if (!EVP_DecryptInit_ex(od->evp_ctx, NULL, NULL, od->key, od->iv))
@@ -391,6 +416,7 @@ gen_ossl_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen,
 	{
 		if (!EVP_EncryptInit_ex(od->evp_ctx, od->evp_ciph, NULL, NULL, NULL))
 			return PXE_CIPHER_INIT;
+		EVP_CIPHER_CTX_set_padding(od->evp_ctx, 0);
 		if (!EVP_CIPHER_CTX_set_key_length(od->evp_ctx, od->klen))
 			return PXE_CIPHER_INIT;
 		if (!EVP_EncryptInit_ex(od->evp_ctx, NULL, NULL, od->key, od->iv))
@@ -751,6 +777,13 @@ px_find_cipher(const char *name, PX_Cipher **res)
 	EVP_CIPHER_CTX *ctx;
 	OSSLCipher *od;
 
+#if OPENSSL_VERSION_MAJOR >= 3
+	if (legacy_provider == NULL)
+		legacy_provider = OSSL_PROVIDER_load(NULL, "legacy");
+	if (default_provider == NULL)
+		default_provider = OSSL_PROVIDER_load(NULL, "default");
+#endif
+
 	name = px_resolve_alias(ossl_aliases, name);
 	for (i = ossl_cipher_types; i->name; i++)
 		if (strcmp(i->name, name) == 0)
diff --git a/doc/src/sgml/pgcrypto.sgml b/doc/src/sgml/pgcrypto.sgml
index 8748c64e2d..5c6d54042e 100644
--- a/doc/src/sgml/pgcrypto.sgml
+++ b/doc/src/sgml/pgcrypto.sgml
@@ -1233,6 +1233,12 @@ gen_random_uuid() returns uuid
     </tgroup>
    </table>
 
+   <para>
+    When compiled against <productname>OpenSSL</productname> 3.0.0, the legacy
+    provider will be automatically loaded in order to support the ciphers in
+    the above table.
+   </para>
+
    <para>
     Notes:
    </para>
-- 
2.21.1 (Apple Git-122.3)

