From 93df67e8d130d9aab62247899b2bb54502c1f008 Mon Sep 17 00:00:00 2001 From: Daniel Gustafsson Date: Thu, 12 Dec 2024 12:54:39 +0100 Subject: [PATCH v6] pgcrypto: Make it possible to disable built-in crypto 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. Co-authored-by: Daniel Gustafsson Co-authored-by: Joe Conway Reviewed-by: Joe Conway Reviewed-by: Peter Eisentraut Discussion: https://postgr.es/m/TYCPR01MB11684E5B636E17548D4A42248FA4D2@TYCPR01MB11684.jpnprd01.prod.outlook.com --- contrib/pgcrypto/expected/crypt-des.out | 7 ++++ contrib/pgcrypto/openssl.c | 43 +++++++++++++++++++++++++ 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, 139 insertions(+) diff --git a/contrib/pgcrypto/expected/crypt-des.out b/contrib/pgcrypto/expected/crypt-des.out index a462dcd580..8993a2d104 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 448db331a0..c5683aa70e 100644 --- a/contrib/pgcrypto/openssl.c +++ b/contrib/pgcrypto/openssl.c @@ -794,3 +794,46 @@ 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) +{ + int fips_enabled; + + 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); + + /* + * EVP_default_properties_is_fips_enabled was added in OpenSSL 3.0, before + * that FIPS_mode() was used to test for FIPS being enabled. The last + * upstream OpenSSL version before 3.0 which supported FIPS was 1.0.2, but + * there are forks of 1.1.1 which are FIPS certified so we still need to + * test with FIPS_mode() even though we don't support 1.0.2. + */ + fips_enabled = +#if defined(EVP_default_properties_is_fips_enabled) + EVP_default_properties_is_fips_enabled(NULL); +#elif defined(FIPS_mode) + FIPS_mode(); +#else + 0; +#endif + + if (fips_enabled) + 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 ebd76eed70..e3d34d3a10 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.", + "\"on\" enables builtin crypto, \"off\" unconditionally disables and \"fips\" " + "will disable builtin crypto 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 0913ff2c1b..b97ea4fd36 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 471bb4ec72..8f21dad54e 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 a85ec1e655..5b75dc5bb4 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 396c67f0cd..7fe47f5f08 100644 --- a/doc/src/sgml/pgcrypto.sgml +++ b/doc/src/sgml/pgcrypto.sgml @@ -1222,6 +1222,45 @@ gen_random_uuid() returns uuid + + Configuration Parameters + + + There is one configuration parameter that controls the behavior of + pgcrypto. + + + + + + pgcrypto.legacy_crypto_enabled (enum) + + pgcrypto.legacy_crypto_enabled configuration + parameter + + + + + pgcrypto.legacy_crypto_enabled determines if the + built in legacy crypto functions gen_salt(), + pg_gen_salt_rounds(), and crypt() + are available for use. Setting this to off + disables these functions. on (the default) enables + these functions to work normally. fips disables these + functions if OpenSSL is detected to operate + in FIPS mode. + + + + + + + In ordinary usage, these parameters are set + in postgresql.conf, although superusers can alter them + on-the-fly within their own sessions. + + + Author -- 2.39.3 (Apple Git-146)