Skip to content

Commit

Permalink
[v16.x backport] src,deps,build,test: add OpenSSL config appname
Browse files Browse the repository at this point in the history
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: nodejs#43124
Refs: nodejs#40366
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: Beth Griggs <bgriggs@redhat.com>
  • Loading branch information
danbev committed Jun 23, 2022
1 parent 0284901 commit 95a3625
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 34 deletions.
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
89 changes: 58 additions & 31 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,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,
Expand Down Expand Up @@ -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.
Expand All @@ -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<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

0 comments on commit 95a3625

Please sign in to comment.