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

src,deps,build,test: add OpenSSL config appname #43124

Closed
wants to merge 2 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
17 changes: 17 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 @@ -768,6 +769,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
danbev marked this conversation as resolved.
Show resolved Hide resolved
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 Expand Up @@ -819,6 +833,9 @@ $ ls out/Release/obj.target/deps/openssl/lib/openssl-modules/
fips.so
```

Running `configure` without `--openssl-is-fips` flag and rebuilding will reset
the FIPS configuration.

### FIPS support when dynamically linking OpenSSL

For quictls/openssl 3.0 it is possible to enable FIPS when dynamically linking.
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 @@ -1488,6 +1494,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
30 changes: 30 additions & 0 deletions deps/openssl/nodejs-openssl.cnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Use this in order to automatically load providers.
nodejs_conf = openssl_init

# Optionally include a file that is generated by the OpenSSL fipsinstall
# application. This file contains configuration data required by the OpenSSL
# fips provider. It contains a named section e.g. [fips_sect] which is
# referenced from the [provider_sect] below.
# Refer to the OpenSSL security policy for more information.
# .include fipsmodule.cnf

[openssl_init]
providers = provider_sect

# List of providers to load
[provider_sect]
default = default_sect
# The fips section name should match the section name inside the
# included fipsmodule.cnf.
# fips = fips_sect

# If no providers are activated explicitly, the default one is activated implicitly.
# See man 7 OSSL_PROVIDER-default for more details.
#
# If you add a section explicitly activating any other provider(s), you most
# probably need to explicitly activate the default provider, otherwise it
# becomes unavailable in openssl. As a consequence applications depending on
# OpenSSL may not work correctly which could lead to significant system
# problems including inability to remotely access the system.
[default_sect]
# activate = 1
16 changes: 16 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,21 @@ Load an OpenSSL configuration file on startup. Among other uses, this can be
used to enable FIPS-compliant crypto if Node.js is built
against FIPS-enabled OpenSSL.

### `--openssl-shared-config`

<!-- YAML
added: REPLACEME
-->

Enable OpenSSL default configuration section, `openssl_conf` to be read from
the OpenSSL configuration file. The default configuration file is named
`openssl.cnf` but this can be changed using the environment variable
`OPENSSL_CONF`, or by using the command line option `--openssl-config`.
The location of the default OpenSSL configuration file depends on how OpenSSL
is being linked to Node.js. Sharing the OpenSSL configuration may have unwanted
implications and it is recommended to use a configuration section specific to
Node.js which is `nodejs_conf` and is default when this option is not used.

### `--openssl-legacy-provider`

<!-- YAML
Expand Down Expand Up @@ -1675,6 +1690,7 @@ Node.js options that are allowed are:
* `--node-memory-debug`
* `--openssl-config`
* `--openssl-legacy-provider`
* `--openssl-shared-config`
* `--pending-deprecation`
* `--policy-integrity`
* `--preserve-symlinks-main`
Expand Down
10 changes: 5 additions & 5 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@
'variables': {
'openssl-cli': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)openssl-cli<(EXECUTABLE_SUFFIX)',
'provider_name': 'libopenssl-fipsmodule',
'opensslconfig': './deps/openssl/openssl/apps/openssl.cnf',
'opensslconfig': './deps/openssl/nodejs-openssl.cnf',
'conditions': [
['GENERATOR == "ninja"', {
'fipsmodule_internal': '<(PRODUCT_DIR)/lib/<(provider_name).so',
Expand All @@ -374,7 +374,7 @@
}, {
'fipsmodule_internal': '<(PRODUCT_DIR)/obj.target/deps/openssl/<(provider_name).so',
'fipsmodule': '<(PRODUCT_DIR)/obj.target/deps/openssl/lib/openssl-modules/fips.so',
'fipsconfig': '<(PRODUCT_DIR)/obj/deps/openssl/fipsmodule.cnf',
'fipsconfig': '<(PRODUCT_DIR)/obj.target/deps/openssl/fipsmodule.cnf',
'opensslconfig_internal': '<(PRODUCT_DIR)/obj.target/deps/openssl/openssl.cnf',
}],
],
Expand Down Expand Up @@ -426,7 +426,7 @@
}, {
'variables': {
'opensslconfig_internal': '<(obj_dir)/deps/openssl/openssl.cnf',
'opensslconfig': './deps/openssl/openssl/apps/openssl.cnf',
'opensslconfig': './deps/openssl/nodejs-openssl.cnf',
},
'actions': [
{
Expand All @@ -435,8 +435,8 @@
'outputs': [ '<(opensslconfig_internal)', ],
'action': [
'python', 'tools/copyfile.py',
'./deps/openssl/openssl/apps/openssl.cnf',
'<(obj_dir)/deps/openssl/openssl.cnf',
'<(opensslconfig)',
'<(opensslconfig_internal)',
],
},
],
Expand Down
56 changes: 38 additions & 18 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ 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);

#ifdef __POSIX__
void SignalExit(int signo, siginfo_t* info, void* ucontext) {
ResetStdio();
Expand Down Expand Up @@ -1084,27 +1087,44 @@ 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;
// To allow for using the previous default where the 'openssl_conf' appname
// was used, the command line option 'openssl-shared-config' can be used to
// force the old behavior.
RafaelGSS marked this conversation as resolved.
Show resolved Hide resolved
if (per_process::cli_options->openssl_shared_config) {
conf_section_name = "openssl_conf";
}
// 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) {
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;
}
#else // OPENSSL_VERSION_MAJOR < 3
if (FIPS_mode()) {
Expand Down
4 changes: 4 additions & 0 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,10 @@ PerProcessOptionsParser::PerProcessOptionsParser(
"enable OpenSSL 3.0 legacy provider",
&PerProcessOptions::openssl_legacy_provider,
kAllowedInEnvironment);
AddOption("--openssl-shared-config",
"enable OpenSSL shared configuration",
&PerProcessOptions::openssl_shared_config,
kAllowedInEnvironment);

#endif // OPENSSL_VERSION_MAJOR
AddOption("--use-largepages",
Expand Down
1 change: 1 addition & 0 deletions src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ class PerProcessOptions : public Options {
#endif
#if OPENSSL_VERSION_MAJOR >= 3
bool openssl_legacy_provider = false;
bool openssl_shared_config = false;
#endif

// Per-process because reports can be triggered outside a known V8 context.
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
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ for (const line of [...nodeOptionsLines, ...v8OptionsLines]) {

if (!common.hasOpenSSL3) {
documented.delete('--openssl-legacy-provider');
documented.delete('--openssl-shared-config');
}

// Filter out options that are conditionally present.
Expand All @@ -55,6 +56,7 @@ const conditionalOpts = [
return [
'--openssl-config',
common.hasOpenSSL3 ? '--openssl-legacy-provider' : '',
common.hasOpenSSL3 ? '--openssl-shared-config' : '',
'--tls-cipher-list',
'--use-bundled-ca',
'--use-openssl-ca',
Expand Down