From f9f522229d46a265d915a37bb4c950f96f1fe192 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Mon, 27 Jul 2026 01:11:26 +0200 Subject: [PATCH] src: report why --enable-fips failed The startup failure always appended the OpenSSL error queue, so when Node.js itself detected the missing fips provider it printed an error header followed by nothing. Report the reason instead. Signed-off-by: Filip Skokan --- src/crypto/crypto_util.cc | 38 ++++++++++++++++++++++--------- src/crypto/crypto_util.h | 5 +++- src/node.cc | 8 +++---- test/parallel/test-crypto-fips.js | 15 ++++++++---- 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc index 080f2cf51cec..1fccb27c7ec8 100644 --- a/src/crypto/crypto_util.cc +++ b/src/crypto/crypto_util.cc @@ -99,20 +99,36 @@ int NoPasswordCallback(char* buf, int size, int rwflag, void* u) { return 0; } -bool ProcessFipsOptions() { - /* Override FIPS settings in configuration file, if needed. */ - if (per_process::cli_options->enable_fips_crypto || - per_process::cli_options->force_fips_crypto) { +std::optional ProcessFipsOptions() { + const bool enable_fips = per_process::cli_options->enable_fips_crypto; + const bool force_fips = per_process::cli_options->force_fips_crypto; + if (!enable_fips && !force_fips) return std::nullopt; + #if OPENSSL_VERSION_MAJOR >= 3 - if (!ncrypto::testFipsEnabled()) return false; - return ncrypto::setFipsEnabled(true, nullptr); -#else - // TODO(@jasnell): Remove this ifdef branch when openssl 1.1.1 is - // no longer supported. - if (FIPS_mode() == 0) return FIPS_mode_set(1); + // Whether FIPS-approved implementations are reachable is decided by the + // OpenSSL configuration, not by Node.js. Refuse to start rather than + // restrict the default property query to a provider that is not there, + // which would leave every operation failing as unsupported. + if (!ncrypto::testFipsEnabled()) { + const std::string option = force_fips ? "--force-fips" : "--enable-fips"; + return option + " requires an active OpenSSL provider named \"fips\". " + "FIPS mode is configured through OpenSSL; see " + "https://nodejs.org/api/crypto.html#fips-mode"; + } #endif + + CryptoErrorList errors{CryptoErrorList::Option::NONE}; + if (!ncrypto::setFipsEnabled(true, &errors)) { + std::string error = "OpenSSL error when trying to enable FIPS"; + if (!errors.empty()) error += ':'; + for (const auto& openssl_error : errors) { + error += '\n'; + error += openssl_error; + } + return error; } - return true; + + return std::nullopt; } bool InitCryptoOnce(Isolate* isolate) { diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h index 76afc3dd24a3..d3ef0f82e5b7 100644 --- a/src/crypto/crypto_util.h +++ b/src/crypto/crypto_util.h @@ -62,7 +62,10 @@ constexpr T NumBitsToBytes(T bits) { return (bits / CHAR_BIT) + ((CHAR_BIT - 1 + (bits % CHAR_BIT)) / CHAR_BIT); } -bool ProcessFipsOptions(); +// Applies the FIPS related command line options. Returns a description of +// what went wrong, or std::nullopt when there was nothing to do or the +// options were applied successfully. +std::optional ProcessFipsOptions(); bool InitCryptoOnce(v8::Isolate* isolate); void InitCryptoOnce(); diff --git a/src/node.cc b/src/node.cc index b368c5873434..b5455fe40356 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1168,6 +1168,7 @@ InitializeOncePerProcessInternal(const std::vector& args, if (!(flags & ProcessInitializationFlags::kNoInitOpenSSL)) { #if HAVE_OPENSSL #ifndef OPENSSL_IS_BORINGSSL +#if OPENSSL_VERSION_MAJOR >= 3 auto GetOpenSSLErrorString = []() -> std::string { std::string ret; ERR_print_errors_cb( @@ -1183,7 +1184,6 @@ InitializeOncePerProcessInternal(const std::vector& args, // In the case of FIPS builds we should make sure // the random source is properly initialized first. -#if OPENSSL_VERSION_MAJOR >= 3 // Call OPENSSL_init_crypto to initialize OPENSSL_INIT_LOAD_CONFIG to // avoid the default behavior where errors raised during the parsing of the // OpenSSL configuration file are not propagated and cannot be detected. @@ -1244,12 +1244,10 @@ InitializeOncePerProcessInternal(const std::vector& args, OPENSSL_init(); } #endif - if (!crypto::ProcessFipsOptions()) { + if (auto fips_error = crypto::ProcessFipsOptions()) { result->exit_code_ = ExitCode::kGenericUserError; result->early_return_ = true; - result->errors_.emplace_back( - "OpenSSL error when trying to enable FIPS:\n" + - GetOpenSSLErrorString()); + result->errors_.emplace_back(std::move(*fips_error)); return result; } diff --git a/test/parallel/test-crypto-fips.js b/test/parallel/test-crypto-fips.js index 04a95fa5fd37..055de8a536fe 100644 --- a/test/parallel/test-crypto-fips.js +++ b/test/parallel/test-crypto-fips.js @@ -21,7 +21,14 @@ const FIPS_ERROR_STRING2 = 'Error [ERR_CRYPTO_FIPS_FORCED]: Cannot set FIPS mode, it was forced with ' + '--force-fips at startup.'; const FIPS_UNSUPPORTED_ERROR_STRING = 'fips mode not supported'; -const FIPS_ENABLE_ERROR_STRING = 'OpenSSL error when trying to enable FIPS:'; +const FIPS_ENABLE_ERROR_STRING = + hasOpenSSL3 ? + '--enable-fips requires an active OpenSSL provider named "fips"' : + 'OpenSSL error when trying to enable FIPS:'; +const FIPS_FORCE_ERROR_STRING = + hasOpenSSL3 ? + '--force-fips requires an active OpenSSL provider named "fips"' : + 'OpenSSL error when trying to enable FIPS:'; const CNF_FIPS_ON = fixtures.path('openssl_fips_enabled.cnf'); const CNF_FIPS_OFF = fixtures.path('openssl_fips_disabled.cnf'); @@ -75,7 +82,7 @@ testHelper( ['--enable-fips'], testFipsCrypto() ? kNoFailure : kGenericUserError, testFipsCrypto() ? FIPS_ENABLED : FIPS_ENABLE_ERROR_STRING, - 'process.versions', + 'require("crypto").getFips()', process.env); // --force-fips should raise an error if OpenSSL is not FIPS enabled. @@ -83,8 +90,8 @@ testHelper( testFipsCrypto() ? 'stdout' : 'stderr', ['--force-fips'], testFipsCrypto() ? kNoFailure : kGenericUserError, - testFipsCrypto() ? FIPS_ENABLED : FIPS_ENABLE_ERROR_STRING, - 'process.versions', + testFipsCrypto() ? FIPS_ENABLED : FIPS_FORCE_ERROR_STRING, + 'require("crypto").getFips()', process.env); // By default FIPS should be off in both FIPS and non-FIPS builds