diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc index 080f2cf51ce..1fccb27c7ec 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 76afc3dd24a..d3ef0f82e5b 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 b368c587343..b5455fe4035 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 04a95fa5fd3..055de8a536f 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