From 4296591154fcccd830f6abe36e9420d8eadafaff Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Mon, 26 Apr 2021 05:52:16 +0200 Subject: [PATCH] build,src,test,doc: enable FIPS for OpenSSL 3.0 This commit enables FIPS when Node.js is dynamically linking against quictls/openssl-3.0. BUILDING.md has been updated with instructions to configure and build quictls/openssl 3.0.0-alpha-15 and includes a couple of work-arounds which I believe are fixed in alpha-16 and can be removed when alpha-16 is available. The information might be a little too detailed/verbose but I thought it would be helpful to at least initially include all the steps. PR-URL: https://github.com/nodejs/node/pull/38633 Reviewed-By: James M Snell Reviewed-By: Rich Trott Reviewed-By: Richard Lau Reviewed-By: Michael Dawson --- BUILDING.md | 130 +++++++++++++++++++++++++++++- common.gypi | 2 +- configure.py | 6 ++ src/crypto/crypto_util.cc | 15 ++++ src/crypto/crypto_util.h | 2 +- test/parallel/test-crypto-fips.js | 6 +- 6 files changed, 157 insertions(+), 4 deletions(-) diff --git a/BUILDING.md b/BUILDING.md index 973c87b73ea1a5..6ebe84f6e1aad5 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -759,7 +759,135 @@ as `deps/icu` (You'll have: `deps/icu/source/...`) ## Building Node.js with FIPS-compliant OpenSSL -The current version of Node.js does not support FIPS. +The current version of Node.js does not support FIPS when statically linking +(the default) with OpenSSL 1.1.1 but for dynamically linking it is possible +to enable FIPS using the configuration flag `--openssl-is-fips`. + +### Configuring and building quictls/openssl for FIPS + +For quictls/openssl 3.0 it is possible to enable FIPS when dynamically linking. +Node.js currently uses openssl-3.0.0+quic which can be configured as +follows: +```console +$ git clone git@github.com:quictls/openssl.git +$ cd openssl +$ ./config --prefix=/path/to/install/dir/ shared enable-fips linux-x86_64 +``` +This can be compiled and installed using the following commands: +```console +$ make -j8 +$ make install_ssldirs +$ make install_fips +``` + +After the FIPS module and configuration file have been installed by the above +instructions we also need to update `/path/to/install/dir/ssl/openssl.cnf` to +use the generated FIPS configuration file (`fipsmodule.cnf`): +```text +.include fipsmodule.cnf + +# List of providers to load +[provider_sect] +default = default_sect +# The fips section name should match the section name inside the +# included /path/to/install/dir/ssl/fipsmodule.cnf. +fips = fips_sect + +[default_sect] +activate = 1 +``` + +In the above case OpenSSL is not installed in the default location so two +environment variables need to be set, `OPENSSL_CONF`, and `OPENSSL_MODULES` +which should point to the OpenSSL configuration file and the directory where +OpenSSL modules are located: +```console +$ export OPENSSL_CONF=/path/to/install/dir/ssl/openssl.cnf +$ export OPENSSL_MODULES=/path/to/install/dir/lib/ossl-modules +``` + +Node.js can then be configured to enable FIPS: +```console +$ ./configure --shared-openssl --shared-openssl-libpath=/path/to/install/dir/lib --shared-openssl-includes=/path/to/install/dir/include --shared-openssl-libname=crypto,ssl --openssl-is-fips +$ export LD_LIBRARY_PATH=/path/to/install/dir/lib +$ make -j8 +``` + +Verify the produced executable: +```console +$ ldd ./node + linux-vdso.so.1 (0x00007ffd7917b000) + libcrypto.so.81.3 => /path/to/install/dir/lib/libcrypto.so.81.3 (0x00007fd911321000) + libssl.so.81.3 => /path/to/install/dir/lib/libssl.so.81.3 (0x00007fd91125e000) + libdl.so.2 => /usr/lib64/libdl.so.2 (0x00007fd911232000) + libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007fd911039000) + libm.so.6 => /usr/lib64/libm.so.6 (0x00007fd910ef3000) + libgcc_s.so.1 => /usr/lib64/libgcc_s.so.1 (0x00007fd910ed9000) + libpthread.so.0 => /usr/lib64/libpthread.so.0 (0x00007fd910eb5000) + libc.so.6 => /usr/lib64/libc.so.6 (0x00007fd910cec000) + /lib64/ld-linux-x86-64.so.2 (0x00007fd9117f2000) +``` +If the `ldd` command says that `libcrypto` cannot be found one needs to set +`LD_LIBRARY_PATH` to point to the directory used above for +`--shared-openssl-libpath` (see previous step). + +Verify the OpenSSL version: +```console +$ ./node -p process.versions.openssl +3.0.0-alpha16+quic +``` + +Verify that FIPS is available: +```console +$ ./node -p 'process.config.variables.openssl_is_fips' +true +$ ./node --enable-fips -p 'crypto.getFips()' +1 +``` + +FIPS support can then be enable via the OpenSSL configuration file or +using `--enable-fips` or `--force-fips` command line options to the Node.js +executable. See sections +[Enabling FIPS using Node.js options](#enabling-fips-using-node.js-options) and +[Enabling FIPS using OpenSSL config](#enabling-fips-using-openssl-config) below. + +### Enabling FIPS using Node.js options +This is done using one of the Node.js options `--enable-fips` or +`--force-fips`, for example: +```console +$ node --enable-fips -p 'crypto.getFips()' +``` + +### Enabling FIPS using OpenSSL config +This example show that using OpenSSL's configuration file, FIPS can be enabled +without specifying the `--enable-fips` or `--force-fips` options by setting +`default_properties = fips=yes` in the FIPS configuration file. See +[link](https://github.com/openssl/openssl/blob/master/README-FIPS.md#loading-the-fips-module-at-the-same-time-as-other-providers) +for details. + +For this to work the OpenSSL configuration file (default openssl.cnf) needs to +be updated. The following shows an example: +```console +openssl_conf = openssl_init + +.include /path/to/install/dir/ssl/fipsmodule.cnf + +[openssl_init] +providers = prov +alg_section = algorithm_sect + +[prov] +fips = fips_sect +default = default_sect + +[default_sect] +activate = 1 + +[algorithm_sect] +default_properties = fips=yes +``` +After this change Node.js can be run without the `--enable-fips` or `--force-fips` +options. ## Building Node.js with external core modules diff --git a/common.gypi b/common.gypi index 75d4b692e9b62b..4bc75c7c41c6dd 100644 --- a/common.gypi +++ b/common.gypi @@ -99,7 +99,7 @@ 'v8_base': '<(PRODUCT_DIR)/obj.target/tools/v8_gypfiles/libv8_snapshot.a', }], ['openssl_fips != ""', { - 'openssl_product': '<(STATIC_LIB_PREFIX)crypto<(STATIC_LIB_SUFFIX)', + 'openssl_product': '<(STATIC_LIB_PREFIX)openssl<(STATIC_LIB_SUFFIX)', }, { 'openssl_product': '<(STATIC_LIB_PREFIX)openssl<(STATIC_LIB_SUFFIX)', }], diff --git a/configure.py b/configure.py index 9ec7da8924b132..895a0869cbc9d1 100755 --- a/configure.py +++ b/configure.py @@ -1456,6 +1456,12 @@ def without_ssl_error(option): if options.openssl_fips or options.openssl_fips == '': error('FIPS is not supported in this version of Node.js') + if options.openssl_is_fips and not options.shared_openssl: + error('--openssl-is-fips is only available with --shared-openssl') + + if options.openssl_is_fips: + o['defines'] += ['OPENSSL_FIPS'] + if options.shared_openssl: variables['openssl_quic'] = b(getsharedopensslhasquic.get_has_quic(options.__dict__['shared_openssl_includes'])) diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc index 9c35a7cabbf551..0d533ce42531d1 100644 --- a/src/crypto/crypto_util.cc +++ b/src/crypto/crypto_util.cc @@ -14,6 +14,12 @@ #include "math.h" +#ifdef OPENSSL_FIPS +#if OPENSSL_VERSION_MAJOR >= 3 +#include "openssl/provider.h" +#endif +#endif + namespace node { using v8::ArrayBuffer; @@ -197,7 +203,16 @@ void SetFipsCrypto(const FunctionCallbackInfo& args) { void TestFipsCrypto(const v8::FunctionCallbackInfo& args) { #ifdef OPENSSL_FIPS +#if OPENSSL_VERSION_MAJOR >= 3 + OSSL_PROVIDER* fips_provider = nullptr; + if (OSSL_PROVIDER_available(nullptr, "fips")) { + fips_provider = OSSL_PROVIDER_load(nullptr, "fips"); + } + const auto enabled = fips_provider == nullptr ? 0 : + OSSL_PROVIDER_self_test(fips_provider) ? 1 : 0; +#else const auto enabled = FIPS_selftest() ? 1 : 0; +#endif #else // OPENSSL_FIPS const auto enabled = 0; #endif // OPENSSL_FIPS diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h index 27bb6310b884d1..f2f61aa4518581 100644 --- a/src/crypto/crypto_util.h +++ b/src/crypto/crypto_util.h @@ -24,7 +24,7 @@ #endif // !OPENSSL_NO_ENGINE // The FIPS-related functions are only available // when the OpenSSL itself was compiled with FIPS support. -#ifdef OPENSSL_FIPS +#if defined(OPENSSL_FIPS) && OPENSSL_VERSION_MAJOR < 3 # include #endif // OPENSSL_FIPS diff --git a/test/parallel/test-crypto-fips.js b/test/parallel/test-crypto-fips.js index 204951514a8ede..b6e70b62be68b9 100644 --- a/test/parallel/test-crypto-fips.js +++ b/test/parallel/test-crypto-fips.js @@ -66,6 +66,10 @@ testHelper( 'require("crypto").getFips()', { ...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 +const test_result = testFipsCrypto(); +assert.ok(test_result === 1 || test_result === 0); // If Node was configured using --shared-openssl fips support might be // available depending on how OpenSSL was built. If fips support is @@ -79,7 +83,7 @@ testHelper( // ("Error: Cannot set FIPS mode in a non-FIPS build."). // Due to this uncertainty the following tests are skipped when configured // with --shared-openssl. -if (!sharedOpenSSL()) { +if (!sharedOpenSSL() && !common.hasOpenSSL3) { // OpenSSL config file should be able to turn on FIPS mode testHelper( 'stdout',