pgcrypto: Make it possible to disable built-in crypto
authorDaniel Gustafsson <[email protected]>
Fri, 24 Jan 2025 13:25:08 +0000 (14:25 +0100)
committerDaniel Gustafsson <[email protected]>
Fri, 24 Jan 2025 13:25:08 +0000 (14:25 +0100)
When using OpenSSL and/or the underlying operating system in FIPS
mode no non-FIPS certified crypto implementations should be used.
While that is already possible by just not invoking the built-in
crypto in pgcrypto, this adds a GUC which prohibit the code from
being called.  This doesn't change the FIPS status of PostgreSQL
but can make it easier for sites which target FIPS compliance to
ensure that violations cannot occur.

Author: Daniel Gustafsson <[email protected]>
Author: Joe Conway <[email protected]>
Reviewed-by: Joe Conway <[email protected]>
Reviewed-by: Peter Eisentraut <[email protected]>
Reviewed-by: Hayato Kuroda <[email protected]>
Discussion: https://postgr.es/m/16b4a157-9ea1-44d0-b7b3-4c85df5de97b@joeconway.com

contrib/pgcrypto/expected/crypt-des.out
contrib/pgcrypto/openssl.c
contrib/pgcrypto/pgcrypto.c
contrib/pgcrypto/px-crypt.c
contrib/pgcrypto/px.h
contrib/pgcrypto/sql/crypt-des.sql
doc/src/sgml/pgcrypto.sgml

index a462dcd580a896295e8f298b752333236154db17..70ca7b357b216dacf626e58f9d1c90a6a2023eda 100644 (file)
@@ -28,4 +28,11 @@ FROM ctest;
  t
 (1 row)
 
+-- check disabling of built in crypto functions
+SET pgcrypto.builtin_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.builtin_crypto_enabled;
 DROP TABLE ctest;
index e6870c72c9a79b35c18dc506f60f455488b9a7b1..75f40a2d034125c86840c03767efab5d4d8e273e 100644 (file)
@@ -31,6 +31,7 @@
 
 #include "postgres.h"
 
+#include <openssl/crypto.h>
 #include <openssl/evp.h>
 #include <openssl/err.h>
 #include <openssl/rand.h>
@@ -821,3 +822,28 @@ CheckFIPSMode(void)
 
    return (fips_enabled == 1);
 }
+
+/*
+ * CheckBuiltinCryptoMode
+ *
+ * Function for erroring out in case built-in crypto is executed when the user
+ * has disabled it. If builtin_crypto_enabled is set to BC_OFF or BC_FIPS and
+ * OpenSSL is operating in FIPS mode the function will error out, else the
+ * query executing built-in crypto can proceed.
+ */
+void
+CheckBuiltinCryptoMode(void)
+{
+   if (builtin_crypto_enabled == BC_ON)
+       return;
+
+   if (builtin_crypto_enabled == BC_OFF)
+       ereport(ERROR,
+               errmsg("use of built-in crypto functions is disabled"));
+
+   Assert(builtin_crypto_enabled == BC_FIPS);
+
+   if (CheckFIPSMode() == true)
+       ereport(ERROR,
+               errmsg("use of non-FIPS validated crypto not allowed when OpenSSL is in FIPS mode"));
+}
index ee2a010e402fae7729f6a9a26b3d2a4103f6be32..b7e5383b9a6a6b1282a0e412b500084eddb8eac9 100644 (file)
 #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 builtin_crypto_options[] = {
+   {"on", BC_ON, false},
+   {"off", BC_OFF, false},
+   {"fips", BC_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            builtin_crypto_enabled = BC_ON;
+
+/*
+ * Entrypoint of this module.
+ */
+void
+_PG_init(void)
+{
+   DefineCustomEnumVariable("pgcrypto.builtin_crypto_enabled",
+                            "Sets if builtin crypto functions are enabled.",
+                            "\"on\" enables builtin crypto, \"off\" unconditionally disables and \"fips\" "
+                            "will disable builtin crypto if OpenSSL is in FIPS mode",
+                            &builtin_crypto_enabled,
+                            BC_ON,
+                            builtin_crypto_options,
+                            PGC_SUSET,
+                            0,
+                            NULL,
+                            NULL,
+                            NULL);
+   MarkGUCPrefixReserved("pgcrypto");
+}
+
 /* SQL function: hash(bytea, text) returns bytea */
 PG_FUNCTION_INFO_V1(pg_digest);
 
index 0913ff2c1bc2e48325fbe375f7a7fd754b218855..96ce9384aff43c5c623c34b16d1403a6801d5b89 100644 (file)
@@ -91,6 +91,8 @@ px_crypt(const char *psw, const char *salt, char *buf, unsigned len)
 {
    const struct px_crypt_algo *c;
 
+   CheckBuiltinCryptoMode();
+
    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];
 
+   CheckBuiltinCryptoMode();
+
    for (g = gen_list; g->name; g++)
        if (pg_strcasecmp(g->name, salt_type) == 0)
            break;
index c2c2fc31245d6630b6e4c2d910d0a101c175dd10..37013cd9f82a97fb6442e8829ff4bf65bfdb5e7b 100644 (file)
 #define PXE_PGP_UNSUPPORTED_PUBALGO -122
 #define PXE_PGP_MULTIPLE_SUBKEYS   -123
 
+typedef enum BuiltinCryptoOptions
+{
+   BC_ON,
+   BC_OFF,
+   BC_FIPS,
+}          BuiltinCryptoOptions;
 
 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 builtin_crypto_enabled;
+
 struct px_digest
 {
    unsigned    (*result_size) (PX_MD *h);
@@ -183,6 +191,7 @@ void        px_set_debug_handler(void (*handler) (const char *));
 void       px_memset(void *ptr, int c, size_t len);
 
 bool       CheckFIPSMode(void);
+void       CheckBuiltinCryptoMode(void);
 
 #ifdef PX_DEBUG
 void       px_debug(const char *fmt,...) pg_attribute_printf(1, 2);
index a85ec1e655510d8d66e7f6f478ce63e7375e25c9..58cc9d2720a1c488b9a656675cb279e9f8f4c691 100644 (file)
@@ -18,4 +18,10 @@ UPDATE ctest SET res = crypt(data, salt);
 SELECT res = crypt(data, res) AS "worked"
 FROM ctest;
 
+-- check disabling of built in crypto functions
+SET pgcrypto.builtin_crypto_enabled = off;
+UPDATE ctest SET salt = gen_salt('des');
+UPDATE ctest SET res = crypt(data, salt);
+RESET pgcrypto.builtin_crypto_enabled;
+
 DROP TABLE ctest;
index 838d7532a526ef260a6beac26c9fa8fcce1c967a..a4d035eabddb809fad888f039525bf7a61d4e9db 100644 (file)
@@ -1165,6 +1165,44 @@ fips_mode() returns boolean
   </para>
  </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-builtin_crypto_enabled">
+    <term>
+     <varname>pgcrypto.builtin_crypto_enabled</varname> (<type>enum</type>)
+     <indexterm>
+      <primary><varname>pgcrypto.builtin_crypto_enabled</varname> configuration
+      parameter</primary>
+     </indexterm>
+    </term>
+    <listitem>
+     <para>
+      <varname>pgcrypto.builtin_crypto_enabled</varname> determines if the
+      built in crypto functions <function>gen_salt()</function>, and
+      <function>crypt()</function> 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
+      <productname>OpenSSL</productname> is detected to operate in FIPS mode.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+
+  <para>
+   In ordinary usage, this parameter is set
+   in <filename>postgresql.conf</filename>, although superusers can alter it
+   on-the-fly within their own sessions.
+  </para>
+ </sect2>
+
  <sect2 id="pgcrypto-notes">
   <title>Notes</title>