Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v16.x backport] src,deps,build,test: add OpenSSL config appname #43545

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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=<some_conf_name>
```

## Building Node.js with FIPS-compliant OpenSSL

The current version of Node.js supports FIPS when statically and
Expand Down
8 changes: 8 additions & 0 deletions configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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)
Expand Down
100 changes: 70 additions & 30 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#if HAVE_OPENSSL
#include "allocated_buffer-inl.h" // Inlined functions needed by node_crypto.h
#include "node_crypto.h"
#include <openssl/conf.h>
#endif

#if defined(NODE_HAVE_I18N_SUPPORT)
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -975,6 +981,18 @@ InitializationResult InitializeOncePerProcess(int argc, char** argv) {
return InitializeOncePerProcess(argc, argv, kDefaultInitialization);
}

#if HAVE_OPENSSL
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;
}
#endif

InitializationResult InitializeOncePerProcess(
int argc,
char** argv,
Expand Down Expand Up @@ -1054,7 +1072,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.
Expand All @@ -1069,46 +1086,69 @@ 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 OPENSSL_VERSION_MAJOR < 3
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
#else
if (ERR_peek_error() != 0) {
return handle_openssl_error(ERR_GET_REASON(ERR_peek_error()),
"OpenSSL configuration error:\n",
&result);
}
#endif

#if 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<int>(per_process::cli_options->v8_thread_pool_size));
if (init_flags & kInitializeV8) {
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/openssl_fips_disabled.cnf
Original file line number Diff line number Diff line change
@@ -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]
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/openssl_fips_enabled.cnf
Original file line number Diff line number Diff line change
@@ -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]
Expand Down
2 changes: 1 addition & 1 deletion test/parallel/test-crypto-fips.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down