From 95a362503f41a7cb2e26fa0b4cd0f2d09ed074af Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Sat, 14 May 2022 10:04:48 +0200 Subject: [PATCH] [v16.x backport] src,deps,build,test: add OpenSSL config appname This commit adds the setting of an appname (configuration section name), 'nodejs_conf', to be used when reading OpenSSL configuration files. The motivation for this is that currently the default OpenSSL configuration, 'openssl_conf', element will be used which may be undesirable as it might configure OpenSSL in unwanted ways. With this commit it is still possible to use a default openssl.cnf file but the only section that Node.js will read from is a section named 'nodejs_conf'. PR-URL: https://github.com/nodejs/node/pull/43124 Refs: https://github.com/nodejs/node/issues/40366 Reviewed-By: James M Snell Reviewed-By: Rich Trott Reviewed-By: Rafael Gonzaga Reviewed-By: Beth Griggs --- BUILDING.md | 14 ++++ configure.py | 8 +++ src/node.cc | 89 ++++++++++++++++--------- test/fixtures/openssl_fips_disabled.cnf | 2 +- test/fixtures/openssl_fips_enabled.cnf | 2 +- test/parallel/test-crypto-fips.js | 2 +- 6 files changed, 83 insertions(+), 34 deletions(-) diff --git a/BUILDING.md b/BUILDING.md index d6ca30104b77f4..d1fa6e0648305e 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -52,6 +52,7 @@ file a new issue. * [Build with a specific ICU](#build-with-a-specific-icu) * [Unix/macOS](#unixmacos-3) * [Windows](#windows-4) +* [Configuring OpenSSL config appname](#configure-openssl-appname) * [Building Node.js with FIPS-compliant OpenSSL](#building-nodejs-with-fips-compliant-openssl) * [Building Node.js with external core modules](#building-nodejs-with-external-core-modules) * [Unix/macOS](#unixmacos-4) @@ -779,6 +780,19 @@ as `deps/icu` (You'll have: `deps/icu/source/...`) > .\vcbuild full-icu ``` +### Configure OpenSSL appname + +Node.js can use an OpenSSL configuration file by specifying the environment +variable `OPENSSL_CONF`, or using the command line option `--openssl-conf`, and +if none of those are specified will default to reading the default OpenSSL +configuration file `openssl.cnf`. Node.js will only read a section that is by +default named `nodejs_conf`, but this name can be overridden using the following +configure option: + +```console +$ ./configure --openssl-conf-name= +``` + ## Building Node.js with FIPS-compliant OpenSSL The current version of Node.js supports FIPS when statically and diff --git a/configure.py b/configure.py index 95b31769cb5756..fed2688c792486 100755 --- a/configure.py +++ b/configure.py @@ -181,6 +181,12 @@ "e.g. /root/x/y.js will be referenced via require('root/x/y'). " "Can be used multiple times") +parser.add_argument("--openssl-conf-name", + action="store", + dest="openssl_conf_name", + default='nodejs_conf', + help="The OpenSSL config appname (config section name) used by Node.js") + parser.add_argument('--openssl-default-cipher-list', action='store', dest='openssl_default_cipher_list', @@ -1461,6 +1467,8 @@ def configure_openssl(o): if options.openssl_no_asm: variables['openssl_no_asm'] = 1 + o['defines'] += ['NODE_OPENSSL_CONF_NAME=' + options.openssl_conf_name] + if options.without_ssl: def without_ssl_error(option): error('--without-ssl is incompatible with %s' % option) diff --git a/src/node.cc b/src/node.cc index 64a910e3a283ad..d0d9dfebd64683 100644 --- a/src/node.cc +++ b/src/node.cc @@ -44,6 +44,7 @@ #if HAVE_OPENSSL #include "allocated_buffer-inl.h" // Inlined functions needed by node_crypto.h #include "node_crypto.h" +#include #endif #if defined(NODE_HAVE_I18N_SUPPORT) @@ -162,6 +163,11 @@ PVOID old_vectored_exception_handler; struct V8Platform v8_platform; } // namespace per_process +// The section in the OpenSSL configuration file to be loaded. +const char* conf_section_name = STRINGIFY(NODE_OPENSSL_CONF_NAME); + +const char* fips_error_msg = "OpenSSL error when trying to enable FIPS:\n"; + #ifdef __POSIX__ void SignalExit(int signo, siginfo_t* info, void* ucontext) { ResetStdio(); @@ -975,6 +981,16 @@ InitializationResult InitializeOncePerProcess(int argc, char** argv) { return InitializeOncePerProcess(argc, argv, kDefaultInitialization); } +InitializationResult handle_openssl_error(int exit_code, + const char* msg, + InitializationResult* result) { + result->exit_code = exit_code; + result->early_return = true; + fprintf(stderr, "%s", msg); + ERR_print_errors_fp(stderr); + return *result; +} + InitializationResult InitializeOncePerProcess( int argc, char** argv, @@ -1054,7 +1070,6 @@ InitializationResult InitializeOncePerProcess( } // 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. @@ -1069,46 +1084,58 @@ InitializationResult InitializeOncePerProcess( // CheckEntropy. CheckEntropy will call RAND_status which will now always // return 0, leading to an endless loop and the node process will appear to // hang/freeze. + + // Passing NULL as the config file will allow the default openssl.cnf file + // to be loaded, but the default section in that file will not be used, + // instead only the section that matches the value of conf_section_name + // will be read from the default configuration file. + const char* conf_file = nullptr; + // Use OPENSSL_CONF environment variable is set. std::string env_openssl_conf; credentials::SafeGetenv("OPENSSL_CONF", &env_openssl_conf); + if (!env_openssl_conf.empty()) { + conf_file = env_openssl_conf.c_str(); + } + // Use --openssl-conf command line option if specified. + if (!per_process::cli_options->openssl_config.empty()) { + conf_file = per_process::cli_options->openssl_config.c_str(); + } - bool has_cli_conf = !per_process::cli_options->openssl_config.empty(); - if (has_cli_conf || !env_openssl_conf.empty()) { - OPENSSL_INIT_SETTINGS* settings = OPENSSL_INIT_new(); - OPENSSL_INIT_set_config_file_flags(settings, CONF_MFLAGS_DEFAULT_SECTION); - if (has_cli_conf) { - const char* conf = per_process::cli_options->openssl_config.c_str(); - OPENSSL_INIT_set_config_filename(settings, conf); - } - OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, settings); - OPENSSL_INIT_free(settings); - - if (ERR_peek_error() != 0) { - result.exit_code = ERR_GET_REASON(ERR_peek_error()); - result.early_return = true; - fprintf(stderr, "OpenSSL configuration error:\n"); - ERR_print_errors_fp(stderr); - return result; + OPENSSL_INIT_SETTINGS* settings = OPENSSL_INIT_new(); + OPENSSL_INIT_set_config_filename(settings, conf_file); + OPENSSL_INIT_set_config_appname(settings, conf_section_name); + OPENSSL_INIT_set_config_file_flags(settings, + CONF_MFLAGS_IGNORE_MISSING_FILE); + + OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, settings); + OPENSSL_INIT_free(settings); + + if (ERR_peek_error() != 0) { + int ossl_error_code = ERR_GET_REASON(ERR_peek_error()); + if (ossl_error_code == EVP_R_FIPS_MODE_NOT_SUPPORTED) { + if (!crypto::ProcessFipsOptions()) { + return handle_openssl_error(ossl_error_code, fips_error_msg, &result); + } + } else { + return handle_openssl_error(ossl_error_code, + "OpenSSL configuration error:\n", + &result); } } -#else // OPENSSL_VERSION_MAJOR < 3 if (FIPS_mode()) { OPENSSL_init(); } -#endif - if (!crypto::ProcessFipsOptions()) { - result.exit_code = ERR_GET_REASON(ERR_peek_error()); - result.early_return = true; - fprintf(stderr, "OpenSSL error when trying to enable FIPS:\n"); - ERR_print_errors_fp(stderr); - return result; - } - // V8 on Windows doesn't have a good source of entropy. Seed it from - // OpenSSL's pool. - V8::SetEntropySource(crypto::EntropySource); + // V8 on Windows doesn't have a good source of entropy. Seed it from + // OpenSSL's pool. + V8::SetEntropySource(crypto::EntropySource); + if (!crypto::ProcessFipsOptions()) { + return handle_openssl_error(ERR_GET_REASON(ERR_peek_error()), + fips_error_msg, + &result); + } #endif // HAVE_OPENSSL && !defined(OPENSSL_IS_BORINGSSL) -} + } per_process::v8_platform.Initialize( static_cast(per_process::cli_options->v8_thread_pool_size)); if (init_flags & kInitializeV8) { diff --git a/test/fixtures/openssl_fips_disabled.cnf b/test/fixtures/openssl_fips_disabled.cnf index 8668370fac52f7..253c6906e3f3a3 100644 --- a/test/fixtures/openssl_fips_disabled.cnf +++ b/test/fixtures/openssl_fips_disabled.cnf @@ -1,6 +1,6 @@ # Skeleton openssl.cnf for testing with FIPS -openssl_conf = openssl_conf_section +nodejs_conf = openssl_conf_section authorityKeyIdentifier=keyid:always,issuer:always [openssl_conf_section] diff --git a/test/fixtures/openssl_fips_enabled.cnf b/test/fixtures/openssl_fips_enabled.cnf index 9c1a90f5087727..79733c657a96d0 100644 --- a/test/fixtures/openssl_fips_enabled.cnf +++ b/test/fixtures/openssl_fips_enabled.cnf @@ -1,6 +1,6 @@ # Skeleton openssl.cnf for testing with FIPS -openssl_conf = openssl_conf_section +nodejs_conf = openssl_conf_section authorityKeyIdentifier=keyid:always,issuer:always [openssl_conf_section] diff --git a/test/parallel/test-crypto-fips.js b/test/parallel/test-crypto-fips.js index ba8a1ba653ec55..cc3e4eed79d80f 100644 --- a/test/parallel/test-crypto-fips.js +++ b/test/parallel/test-crypto-fips.js @@ -83,7 +83,7 @@ testHelper( [], FIPS_DISABLED, 'require("crypto").getFips()', - { ...process.env, 'OPENSSL_CONF': '' }); + { ...process.env, 'OPENSSL_CONF': ' ' }); // This should succeed for both FIPS and non-FIPS builds in combination with // OpenSSL 1.1.1 or OpenSSL 3.0