From f6132e8b1fbee4b528eb5f4a158a3fbcffa74aa4 Mon Sep 17 00:00:00 2001 From: Daniel Gustafsson Date: Wed, 15 Jan 2025 14:56:28 +0100 Subject: [PATCH v8] 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. Author: Daniel Gustafsson Author: Joe Conway Reviewed-by: Joe Conway Reviewed-by: Peter Eisentraut Reviewed-by: Hayato Kuroda Discussion: https://postgr.es/m/16b4a157-9ea1-44d0-b7b3-4c85df5de97b@joeconway.com --- contrib/pgcrypto/expected/crypt-des.out | 7 +++++ contrib/pgcrypto/openssl.c | 41 +++++++++++++++++++++++++ contrib/pgcrypto/pgcrypto.c | 31 +++++++++++++++++++ contrib/pgcrypto/px-crypt.c | 4 +++ contrib/pgcrypto/px.h | 10 ++++++ contrib/pgcrypto/sql/crypt-des.sql | 6 ++++ doc/src/sgml/pgcrypto.sgml | 38 +++++++++++++++++++++++ 7 files changed, 137 insertions(+) diff --git a/contrib/pgcrypto/expected/crypt-des.out b/contrib/pgcrypto/expected/crypt-des.out index a462dcd580a..70ca7b357b2 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 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; diff --git a/contrib/pgcrypto/openssl.c b/contrib/pgcrypto/openssl.c index 448db331a0f..fc930e4679c 100644 --- a/contrib/pgcrypto/openssl.c +++ b/contrib/pgcrypto/openssl.c @@ -794,3 +794,44 @@ ResOwnerReleaseOSSLCipher(Datum res) { free_openssl_cipher((OSSLCipher *) DatumGetPointer(res)); } + +/* + * 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) +{ + int fips_enabled; + + 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); + + /* + * 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 OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_default_properties_is_fips_enabled(NULL); +#else + FIPS_mode(); +#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 ebd76eed702..d02a621c3c6 100644 --- a/contrib/pgcrypto/pgcrypto.c +++ b/contrib/pgcrypto/pgcrypto.c @@ -38,16 +38,47 @@ #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); diff --git a/contrib/pgcrypto/px-crypt.c b/contrib/pgcrypto/px-crypt.c index 0913ff2c1bc..96ce9384aff 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; + 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; diff --git a/contrib/pgcrypto/px.h b/contrib/pgcrypto/px.h index 471bb4ec727..c574f722fdf 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 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); @@ -182,6 +190,8 @@ void px_set_debug_handler(void (*handler) (const char *)); void px_memset(void *ptr, int c, size_t len); +void CheckBuiltinCryptoMode(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..58cc9d2720a 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 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; diff --git a/doc/src/sgml/pgcrypto.sgml b/doc/src/sgml/pgcrypto.sgml index 396c67f0cde..d1034eff78b 100644 --- a/doc/src/sgml/pgcrypto.sgml +++ b/doc/src/sgml/pgcrypto.sgml @@ -1222,6 +1222,44 @@ gen_random_uuid() returns uuid + + Configuration Parameters + + + There is one configuration parameter that controls the behavior of + pgcrypto. + + + + + + pgcrypto.builtin_crypto_enabled (enum) + + pgcrypto.builtin_crypto_enabled configuration + parameter + + + + + pgcrypto.builtin_crypto_enabled determines if the + built in crypto functions gen_salt(), 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, this parameter is set + in postgresql.conf, although superusers can alter it + on-the-fly within their own sessions. + + + Author -- 2.39.3 (Apple Git-146)