From 243326cc90d7824ac099b2d7b4c607d158cb76c9 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Fri, 22 Nov 2024 14:49:31 +0100
Subject: [PATCH v2] Make it possible to disable built-in crypto

---
 contrib/pgcrypto/expected/crypt-des.out |  7 +++++
 contrib/pgcrypto/openssl.c              | 29 ++++++++++++++++++
 contrib/pgcrypto/pgcrypto.c             | 30 +++++++++++++++++++
 contrib/pgcrypto/px-crypt.c             |  4 +++
 contrib/pgcrypto/px.h                   | 10 +++++++
 contrib/pgcrypto/sql/crypt-des.sql      |  6 ++++
 doc/src/sgml/pgcrypto.sgml              | 39 +++++++++++++++++++++++++
 7 files changed, 125 insertions(+)

diff --git a/contrib/pgcrypto/expected/crypt-des.out b/contrib/pgcrypto/expected/crypt-des.out
index a462dcd580a..8993a2d1044 100644
--- a/contrib/pgcrypto/expected/crypt-des.out
+++ b/contrib/pgcrypto/expected/crypt-des.out
@@ -28,4 +28,11 @@ FROM ctest;
  t
 (1 row)
 
+-- check disabling of legacy crypto functions
+SET pgcrypto.legacy_crypto_enabled = off;
+UPDATE ctest SET salt = gen_salt('des');
+ERROR:  use of built-in crypto functions is disabled
+UPDATE ctest SET res = crypt(data, salt);
+ERROR:  use of built-in crypto functions is disabled
+RESET pgcrypto.legacy_crypto_enabled;
 DROP TABLE ctest;
diff --git a/contrib/pgcrypto/openssl.c b/contrib/pgcrypto/openssl.c
index 26454bc3e29..c29940f3d06 100644
--- a/contrib/pgcrypto/openssl.c
+++ b/contrib/pgcrypto/openssl.c
@@ -794,3 +794,32 @@ ResOwnerReleaseOSSLCipher(Datum res)
 {
 	free_openssl_cipher((OSSLCipher *) DatumGetPointer(res));
 }
+
+/*
+ * CheckLegacyCryptoMode
+ *
+ * Function for erroring out in case built-in crypto is executed when the user
+ * has disabled it. If legacy_crypto_enabled is set to LGC_OFF or LGC_FIPS and
+ * OpenSSL is operating in FIPS mode the function will error out, else the
+ * query executing built-in crypto can proceed.
+ */
+void
+CheckLegacyCryptoMode(void)
+{
+	if (legacy_crypto_enabled == LGC_ON)
+		return;
+
+	if (legacy_crypto_enabled == LGC_OFF)
+		ereport(ERROR,
+				errmsg("use of built-in crypto functions is disabled"));
+
+	Assert(legacy_crypto_enabled == LGC_FIPS);
+
+#if defined FIPS_mode
+	if (FIPS_mode())
+#else
+	if (EVP_default_properties_is_fips_enabled(OSSL_LIB_CTX_get0_global_default()))
+#endif
+		ereport(ERROR,
+				errmsg("use of non-FIPS certified crypto not allowed when OpenSSL is in FIPS mode"));
+}
diff --git a/contrib/pgcrypto/pgcrypto.c b/contrib/pgcrypto/pgcrypto.c
index ebd76eed702..fe0e2b9242a 100644
--- a/contrib/pgcrypto/pgcrypto.c
+++ b/contrib/pgcrypto/pgcrypto.c
@@ -38,16 +38,46 @@
 #include "px-crypt.h"
 #include "px.h"
 #include "utils/builtins.h"
+#include "utils/guc.h"
 #include "varatt.h"
 
 PG_MODULE_MAGIC;
 
 /* private stuff */
 
+static const struct config_enum_entry legacy_crypto_options[] = {
+	{"on", LGC_ON, false},
+	{"off", LGC_OFF, false},
+	{"fips", LGC_FIPS, false},
+	{NULL, 0, false}
+};
+
 typedef int (*PFN) (const char *name, void **res);
 static void *find_provider(text *name, PFN provider_lookup, const char *desc,
 						   int silent);
 
+int legacy_crypto_enabled = LGC_ON;
+
+/*
+ * Entrypoint of this module.
+ */
+void
+_PG_init(void)
+{
+	DefineCustomEnumVariable("pgcrypto.legacy_crypto_enabled",
+							 "Sets if builtin crypto functions are enabled.",
+							 "Yes enables builtin crypto, No unconditionally disables and OpenSSL "
+							 "will disable if OpenSSL is in FIPS mode",
+							 &legacy_crypto_enabled,
+							 LGC_ON,
+							 legacy_crypto_options,
+							 PGC_SUSET,
+							 0,
+							 NULL,
+							 NULL,
+							 NULL);
+}
+
 /* SQL function: hash(bytea, text) returns bytea */
 PG_FUNCTION_INFO_V1(pg_digest);
 
diff --git a/contrib/pgcrypto/px-crypt.c b/contrib/pgcrypto/px-crypt.c
index 0913ff2c1bc..b97ea4fd36b 100644
--- a/contrib/pgcrypto/px-crypt.c
+++ b/contrib/pgcrypto/px-crypt.c
@@ -91,6 +91,8 @@ px_crypt(const char *psw, const char *salt, char *buf, unsigned len)
 {
 	const struct px_crypt_algo *c;
 
+	CheckLegacyCryptoMode();
+
 	for (c = px_crypt_list; c->id; c++)
 	{
 		if (!c->id_len)
@@ -135,6 +137,8 @@ px_gen_salt(const char *salt_type, char *buf, int rounds)
 	char	   *p;
 	char		rbuf[16];
 
+	CheckLegacyCryptoMode();
+
 	for (g = gen_list; g->name; g++)
 		if (pg_strcasecmp(g->name, salt_type) == 0)
 			break;
diff --git a/contrib/pgcrypto/px.h b/contrib/pgcrypto/px.h
index 471bb4ec727..8f21dad54ec 100644
--- a/contrib/pgcrypto/px.h
+++ b/contrib/pgcrypto/px.h
@@ -89,6 +89,12 @@
 #define PXE_PGP_UNSUPPORTED_PUBALGO -122
 #define PXE_PGP_MULTIPLE_SUBKEYS	-123
 
+typedef enum LegacyCryptoOptions
+{
+	LGC_ON,
+	LGC_OFF,
+	LGC_FIPS,
+} LegacyCryptoOptions;
 
 typedef struct px_digest PX_MD;
 typedef struct px_alias PX_Alias;
@@ -96,6 +102,8 @@ typedef struct px_hmac PX_HMAC;
 typedef struct px_cipher PX_Cipher;
 typedef struct px_combo PX_Combo;
 
+extern int legacy_crypto_enabled;
+
 struct px_digest
 {
 	unsigned	(*result_size) (PX_MD *h);
@@ -182,6 +190,8 @@ void		px_set_debug_handler(void (*handler) (const char *));
 
 void		px_memset(void *ptr, int c, size_t len);
 
+void		CheckLegacyCryptoMode(void);
+
 #ifdef PX_DEBUG
 void		px_debug(const char *fmt,...) pg_attribute_printf(1, 2);
 #else
diff --git a/contrib/pgcrypto/sql/crypt-des.sql b/contrib/pgcrypto/sql/crypt-des.sql
index a85ec1e6555..5b75dc5bb49 100644
--- a/contrib/pgcrypto/sql/crypt-des.sql
+++ b/contrib/pgcrypto/sql/crypt-des.sql
@@ -18,4 +18,10 @@ UPDATE ctest SET res = crypt(data, salt);
 SELECT res = crypt(data, res) AS "worked"
 FROM ctest;
 
+-- check disabling of legacy crypto functions
+SET pgcrypto.legacy_crypto_enabled = off;
+UPDATE ctest SET salt = gen_salt('des');
+UPDATE ctest SET res = crypt(data, salt);
+RESET pgcrypto.legacy_crypto_enabled;
+
 DROP TABLE ctest;
diff --git a/doc/src/sgml/pgcrypto.sgml b/doc/src/sgml/pgcrypto.sgml
index 396c67f0cde..c54b0e8459f 100644
--- a/doc/src/sgml/pgcrypto.sgml
+++ b/doc/src/sgml/pgcrypto.sgml
@@ -1222,6 +1222,45 @@ gen_random_uuid() returns uuid
   </sect3>
  </sect2>
 
+ <sect2 id="pgcrypto-configuration-parameters">
+  <title>Configuration Parameters</title>
+
+ <para>
+  There is one configuration parameter that controls the behavior of
+  <filename>pgcrypto</filename>.
+ </para>
+
+  <variablelist>
+   <varlistentry id="pgcrypto-configuration-parameters-legacy_crypto_enabled">
+    <term>
+     <varname>pgcrypto.legacy_crypto_enabled</varname> (<type>enum</type>)
+     <indexterm>
+      <primary><varname>pgcrypto.legacy_crypto_enabled</varname> configuration
+      parameter</primary>
+     </indexterm>
+    </term>
+    <listitem>
+     <para>
+      <varname>pgcrypto.legacy_crypto_enabled</varname> determines if the
+      built in legacy crypto functions <literal>pg_gen_salt</literal>,
+      <literal>pg_gen_salt_rounds</literal>, and <literal>pg_crypt</literal>
+      are available for use. Setting this to <literal>off</literal>
+      disables these functions. <literal>on</literal> (the default) enables
+      these functions to work normally. <literal>fips</literal> disables these
+      functions if <application>OpenSSL</application> is detected to operate
+      in FIPS mode.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+
+  <para>
+   In ordinary usage, these parameters are set
+   in <filename>postgresql.conf</filename>, although superusers can alter them
+   on-the-fly within their own sessions.
+  </para>
+ </sect2>
+
  <sect2 id="pgcrypto-author">
   <title>Author</title>
 
-- 
2.39.3 (Apple Git-146)

