diff --git a/.gitignore b/.gitignore index 6d0e110..ef831e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ SOURCES/icu4c-69_1-src.tgz -SOURCES/node-v14.17.3-stripped.tar.gz +SOURCES/node-v14.17.5-stripped.tar.gz diff --git a/.nodejs.metadata b/.nodejs.metadata index 957a374..7f1e486 100644 --- a/.nodejs.metadata +++ b/.nodejs.metadata @@ -1,2 +1,2 @@ 620a71c84428758376baa0fb81a581c3daa866ce SOURCES/icu4c-69_1-src.tgz -03c817ff5bbebe21d120a2ddee9a87ff223914db SOURCES/node-v14.17.3-stripped.tar.gz +cdb2e0bdf9693d85a58d7b8576a4595618e0909e SOURCES/node-v14.17.5-stripped.tar.gz diff --git a/SOURCES/0004-always-available-fips-options.patch b/SOURCES/0004-always-available-fips-options.patch new file mode 100644 index 0000000..26d4853 --- /dev/null +++ b/SOURCES/0004-always-available-fips-options.patch @@ -0,0 +1,624 @@ +From 7c7f5159fcc71d915dfcc5f97ab18d5f8912f1b5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?V=C3=ADt=20Ondruch?= <vondruch@redhat.com> +Date: Tue, 25 Aug 2020 14:04:54 +0200 +Subject: [PATCH] crypto: make FIPS related options always awailable +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There is no reason to hide FIPS functionality behind build flags. +OpenSSL always provide the information about FIPS availability via +`FIPS_mode()` function. + +This makes the user experience more consistent, because the OpenSSL +library is always queried and the `crypto.getFips()` always returns +OpenSSL settings. + +Fixes #34903 + +PR-URL: https://github.com/nodejs/node/pull/36341 +Reviewed-By: Anna Henningsen <anna@addaleax.net> +Reviewed-By: Michael Dawson <midawson@redhat.com> +Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> +Signed-off-by: Jan Staněk <jstanek@redhat.com> +Signed-off-by: rpm-build <rpm-build> +--- + doc/api/cli.md | 8 +-- + lib/crypto.js | 22 ++---- + node.gypi | 3 - + src/node.cc | 6 +- + src/node_config.cc | 2 - + src/node_crypto.cc | 45 +++++++----- + src/node_options.cc | 2 - + src/node_options.h | 2 - + test/parallel/test-cli-node-print-help.js | 7 +- + test/parallel/test-crypto-fips.js | 71 +++++++++---------- + ...rocess-env-allowed-flags-are-documented.js | 11 +-- + 11 files changed, 74 insertions(+), 105 deletions(-) + +diff --git a/doc/api/cli.md b/doc/api/cli.md +index a8ef339..c41bd49 100644 +--- a/doc/api/cli.md ++++ b/doc/api/cli.md +@@ -182,8 +182,8 @@ code from strings throw an exception instead. This does not affect the Node.js + added: v6.0.0 + --> + +-Enable FIPS-compliant crypto at startup. (Requires Node.js to be built with +-`./configure --openssl-fips`.) ++Enable FIPS-compliant crypto at startup. (Requires Node.js to be built ++against FIPS-compatible OpenSSL.) + + ### `--enable-source-maps` + <!-- YAML +@@ -543,8 +543,8 @@ added: v6.9.0 + --> + + Load an OpenSSL configuration file on startup. Among other uses, this can be +-used to enable FIPS-compliant crypto if Node.js is built with +-`./configure --openssl-fips`. ++used to enable FIPS-compliant crypto if Node.js is built ++against FIPS-enabled OpenSSL. + + ### `--pending-deprecation` + <!-- YAML +diff --git a/lib/crypto.js b/lib/crypto.js +index a41b02d..5c15ab3 100644 +--- a/lib/crypto.js ++++ b/lib/crypto.js +@@ -37,12 +37,10 @@ assertCrypto(); + + const { + ERR_CRYPTO_FIPS_FORCED, +- ERR_CRYPTO_FIPS_UNAVAILABLE + } = require('internal/errors').codes; + const constants = internalBinding('constants').crypto; + const { getOptionValue } = require('internal/options'); + const pendingDeprecation = getOptionValue('--pending-deprecation'); +-const { fipsMode } = internalBinding('config'); + const fipsForced = getOptionValue('--force-fips'); + const { + getFipsCrypto, +@@ -193,10 +191,8 @@ module.exports = { + sign: signOneShot, + setEngine, + timingSafeEqual, +- getFips: !fipsMode ? getFipsDisabled : +- fipsForced ? getFipsForced : getFipsCrypto, +- setFips: !fipsMode ? setFipsDisabled : +- fipsForced ? setFipsForced : setFipsCrypto, ++ getFips: fipsForced ? getFipsForced : getFipsCrypto, ++ setFips: fipsForced ? setFipsForced : setFipsCrypto, + verify: verifyOneShot, + + // Classes +@@ -215,19 +211,11 @@ module.exports = { + Verify + }; + +-function setFipsDisabled() { +- throw new ERR_CRYPTO_FIPS_UNAVAILABLE(); +-} +- + function setFipsForced(val) { + if (val) return; + throw new ERR_CRYPTO_FIPS_FORCED(); + } + +-function getFipsDisabled() { +- return 0; +-} +- + function getFipsForced() { + return 1; + } +@@ -249,10 +237,8 @@ ObjectDefineProperties(module.exports, { + }, + // crypto.fips is deprecated. DEP0093. Use crypto.getFips()/crypto.setFips() + fips: { +- get: !fipsMode ? getFipsDisabled : +- fipsForced ? getFipsForced : getFipsCrypto, +- set: !fipsMode ? setFipsDisabled : +- fipsForced ? setFipsForced : setFipsCrypto ++ get: fipsForced ? getFipsForced : getFipsCrypto, ++ set: fipsForced ? setFipsForced : setFipsCrypto + }, + DEFAULT_ENCODING: { + enumerable: false, +diff --git a/node.gypi b/node.gypi +index 070f212..45f6a9f 100644 +--- a/node.gypi ++++ b/node.gypi +@@ -319,9 +319,6 @@ + [ 'node_use_openssl=="true"', { + 'defines': [ 'HAVE_OPENSSL=1' ], + 'conditions': [ +- ['openssl_fips != "" or openssl_is_fips=="true"', { +- 'defines': [ 'NODE_FIPS_MODE' ], +- }], + [ 'node_shared_openssl=="false"', { + 'dependencies': [ + './deps/openssl/openssl.gyp:openssl', +diff --git a/src/node.cc b/src/node.cc +index 905afd8..c04d199 100644 +--- a/src/node.cc ++++ b/src/node.cc +@@ -1035,11 +1035,11 @@ InitializationResult InitializeOncePerProcess(int argc, char** argv) { + if (credentials::SafeGetenv("NODE_EXTRA_CA_CERTS", &extra_ca_certs)) + crypto::UseExtraCaCerts(extra_ca_certs); + } +-#ifdef NODE_FIPS_MODE + // In the case of FIPS builds we should make sure + // the random source is properly initialized first. +- OPENSSL_init(); +-#endif // NODE_FIPS_MODE ++ if (FIPS_mode()) { ++ OPENSSL_init(); ++ } + // V8 on Windows doesn't have a good source of entropy. Seed it from + // OpenSSL's pool. + V8::SetEntropySource(crypto::EntropySource); +diff --git a/src/node_config.cc b/src/node_config.cc +index 6ee3164..e229eee 100644 +--- a/src/node_config.cc ++++ b/src/node_config.cc +@@ -42,9 +42,7 @@ static void Initialize(Local<Object> target, + READONLY_FALSE_PROPERTY(target, "hasOpenSSL"); + #endif // HAVE_OPENSSL + +-#ifdef NODE_FIPS_MODE + READONLY_TRUE_PROPERTY(target, "fipsMode"); +-#endif + + #ifdef NODE_HAVE_I18N_SUPPORT + +diff --git a/src/node_crypto.cc b/src/node_crypto.cc +index 31e8276..721c9d1 100644 +--- a/src/node_crypto.cc ++++ b/src/node_crypto.cc +@@ -51,6 +51,11 @@ + #include <openssl/hmac.h> + #include <openssl/rand.h> + #include <openssl/pkcs12.h> ++// The FIPS-related functions are only available ++// when the OpenSSL itself was compiled with FIPS support. ++#ifdef OPENSSL_FIPS ++#include <openssl/fips.h> ++#endif // OPENSSL_FIPS + + #include <cerrno> + #include <climits> // INT_MAX +@@ -101,6 +106,7 @@ using v8::String; + using v8::Uint32; + using v8::Uint8Array; + using v8::Undefined; ++using v8::TryCatch; + using v8::Value; + + #ifdef OPENSSL_NO_OCB +@@ -3706,12 +3712,10 @@ void CipherBase::Init(const char* cipher_type, + HandleScope scope(env()->isolate()); + MarkPopErrorOnReturn mark_pop_error_on_return; + +-#ifdef NODE_FIPS_MODE + if (FIPS_mode()) { + return env()->ThrowError( + "crypto.createCipher() is not supported in FIPS mode."); + } +-#endif // NODE_FIPS_MODE + + const EVP_CIPHER* const cipher = EVP_get_cipherbyname(cipher_type); + if (cipher == nullptr) +@@ -3897,13 +3901,11 @@ bool CipherBase::InitAuthenticated(const char* cipher_type, int iv_len, + return false; + } + +-#ifdef NODE_FIPS_MODE + // TODO(tniessen) Support CCM decryption in FIPS mode + if (mode == EVP_CIPH_CCM_MODE && kind_ == kDecipher && FIPS_mode()) { + env()->ThrowError("CCM decryption not supported in FIPS mode"); + return false; + } +-#endif + + // Tell OpenSSL about the desired length. + if (!EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_AEAD_SET_TAG, auth_tag_len, +@@ -4778,7 +4780,6 @@ static AllocatedBuffer Node_SignFinal(Environment* env, + } + + static inline bool ValidateDSAParameters(EVP_PKEY* key) { +-#ifdef NODE_FIPS_MODE + /* Validate DSA2 parameters from FIPS 186-4 */ + if (FIPS_mode() && EVP_PKEY_DSA == EVP_PKEY_base_id(key)) { + DSA* dsa = EVP_PKEY_get0_DSA(key); +@@ -4794,7 +4795,6 @@ static inline bool ValidateDSAParameters(EVP_PKEY* key) { + (L == 2048 && N == 256) || + (L == 3072 && N == 256); + } +-#endif // NODE_FIPS_MODE + + return true; + } +@@ -7032,7 +7032,6 @@ void InitCryptoOnce() { + settings = nullptr; + #endif + +-#ifdef NODE_FIPS_MODE + /* Override FIPS settings in cnf file, if needed. */ + unsigned long err = 0; // NOLINT(runtime/int) + if (per_process::cli_options->enable_fips_crypto || +@@ -7042,12 +7041,10 @@ void InitCryptoOnce() { + } + } + if (0 != err) { +- fprintf(stderr, +- "openssl fips failed: %s\n", +- ERR_error_string(err, nullptr)); +- UNREACHABLE(); ++ auto* isolate = Isolate::GetCurrent(); ++ auto* env = Environment::GetCurrent(isolate); ++ return ThrowCryptoError(env, err); + } +-#endif // NODE_FIPS_MODE + + + // Turn off compression. Saves memory and protects against CRIME attacks. +@@ -7093,7 +7090,6 @@ void SetEngine(const FunctionCallbackInfo<Value>& args) { + } + #endif // !OPENSSL_NO_ENGINE + +-#ifdef NODE_FIPS_MODE + void GetFipsCrypto(const FunctionCallbackInfo<Value>& args) { + args.GetReturnValue().Set(FIPS_mode() ? 1 : 0); + } +@@ -7111,7 +7107,16 @@ void SetFipsCrypto(const FunctionCallbackInfo<Value>& args) { + return ThrowCryptoError(env, err); + } + } +-#endif /* NODE_FIPS_MODE */ ++ ++void TestFipsCrypto(const v8::FunctionCallbackInfo<v8::Value>& args) { ++#ifdef OPENSSL_FIPS ++ const auto enabled = FIPS_selftest() ? 1 : 0; ++#else // OPENSSL_FIPS ++ const auto enabled = 0; ++#endif // OPENSSL_FIPS ++ ++ args.GetReturnValue().Set(enabled); ++} + + namespace { + // SecureBuffer uses openssl to allocate a Uint8Array using +@@ -7147,10 +7152,17 @@ void Initialize(Local<Object> target, + Local<Value> unused, + Local<Context> context, + void* priv) { ++ Environment* env = Environment::GetCurrent(context); ++ + static uv_once_t init_once = UV_ONCE_INIT; ++ TryCatch try_catch{env->isolate()}; + uv_once(&init_once, InitCryptoOnce); + +- Environment* env = Environment::GetCurrent(context); ++ if (try_catch.HasCaught() && !try_catch.HasTerminated()) { ++ try_catch.ReThrow(); ++ return; ++ } ++ + SecureContext::Initialize(env, target); + target->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "KeyObjectHandle"), +@@ -7179,10 +7191,9 @@ void Initialize(Local<Object> target, + env->SetMethod(target, "setEngine", SetEngine); + #endif // !OPENSSL_NO_ENGINE + +-#ifdef NODE_FIPS_MODE + env->SetMethodNoSideEffect(target, "getFipsCrypto", GetFipsCrypto); + env->SetMethod(target, "setFipsCrypto", SetFipsCrypto); +-#endif ++ env->SetMethodNoSideEffect(target, "testFipsCrypto", TestFipsCrypto); + + env->SetMethod(target, "pbkdf2", PBKDF2); + env->SetMethod(target, "generateKeyPairRSA", GenerateKeyPairRSA); +diff --git a/src/node_options.cc b/src/node_options.cc +index 0102329..1ad44f4 100644 +--- a/src/node_options.cc ++++ b/src/node_options.cc +@@ -753,7 +753,6 @@ PerProcessOptionsParser::PerProcessOptionsParser( + &PerProcessOptions::ssl_openssl_cert_store); + Implies("--use-openssl-ca", "[ssl_openssl_cert_store]"); + ImpliesNot("--use-bundled-ca", "[ssl_openssl_cert_store]"); +-#if NODE_FIPS_MODE + AddOption("--enable-fips", + "enable FIPS crypto at startup", + &PerProcessOptions::enable_fips_crypto, +@@ -762,7 +761,6 @@ PerProcessOptionsParser::PerProcessOptionsParser( + "force FIPS crypto (cannot be disabled)", + &PerProcessOptions::force_fips_crypto, + kAllowedInEnvironment); +-#endif + #endif + AddOption("--use-largepages", + "Map the Node.js static code to large pages. Options are " +diff --git a/src/node_options.h b/src/node_options.h +index 58a21e0..f22b254 100644 +--- a/src/node_options.h ++++ b/src/node_options.h +@@ -243,10 +243,8 @@ class PerProcessOptions : public Options { + #endif + bool use_openssl_ca = false; + bool use_bundled_ca = false; +-#if NODE_FIPS_MODE + bool enable_fips_crypto = false; + bool force_fips_crypto = false; +-#endif + #endif + + // Per-process because reports can be triggered outside a known V8 context. +diff --git a/test/parallel/test-cli-node-print-help.js b/test/parallel/test-cli-node-print-help.js +index ab8cd10..7e7c77f 100644 +--- a/test/parallel/test-cli-node-print-help.js ++++ b/test/parallel/test-cli-node-print-help.js +@@ -8,8 +8,6 @@ const common = require('../common'); + + const assert = require('assert'); + const { exec } = require('child_process'); +-const { internalBinding } = require('internal/test/binding'); +-const { fipsMode } = internalBinding('config'); + let stdOut; + + +@@ -28,9 +26,8 @@ function validateNodePrintHelp() { + const cliHelpOptions = [ + { compileConstant: HAVE_OPENSSL, + flags: [ '--openssl-config=...', '--tls-cipher-list=...', +- '--use-bundled-ca', '--use-openssl-ca' ] }, +- { compileConstant: fipsMode, +- flags: [ '--enable-fips', '--force-fips' ] }, ++ '--use-bundled-ca', '--use-openssl-ca', ++ '--enable-fips', '--force-fips' ] }, + { compileConstant: NODE_HAVE_I18N_SUPPORT, + flags: [ '--icu-data-dir=...', 'NODE_ICU_DATA' ] }, + { compileConstant: HAVE_INSPECTOR, +diff --git a/test/parallel/test-crypto-fips.js b/test/parallel/test-crypto-fips.js +index eae3134..a1ed645 100644 +--- a/test/parallel/test-crypto-fips.js ++++ b/test/parallel/test-crypto-fips.js +@@ -9,27 +9,20 @@ const spawnSync = require('child_process').spawnSync; + const path = require('path'); + const fixtures = require('../common/fixtures'); + const { internalBinding } = require('internal/test/binding'); +-const { fipsMode } = internalBinding('config'); ++const { testFipsCrypto } = internalBinding('crypto'); + + const FIPS_ENABLED = 1; + const FIPS_DISABLED = 0; +-const FIPS_ERROR_STRING = +- 'Error [ERR_CRYPTO_FIPS_UNAVAILABLE]: Cannot set FIPS mode in a ' + +- 'non-FIPS build.'; + const FIPS_ERROR_STRING2 = + 'Error [ERR_CRYPTO_FIPS_FORCED]: Cannot set FIPS mode, it was forced with ' + + '--force-fips at startup.'; +-const OPTION_ERROR_STRING = 'bad option'; ++const FIPS_UNSUPPORTED_ERROR_STRING = 'fips mode not supported'; + + const CNF_FIPS_ON = fixtures.path('openssl_fips_enabled.cnf'); + const CNF_FIPS_OFF = fixtures.path('openssl_fips_disabled.cnf'); + + let num_children_ok = 0; + +-function compiledWithFips() { +- return fipsMode ? true : false; +-} +- + function sharedOpenSSL() { + return process.config.variables.node_shared_openssl; + } +@@ -75,17 +68,17 @@ testHelper( + + // --enable-fips should turn FIPS mode on + testHelper( +- compiledWithFips() ? 'stdout' : 'stderr', ++ testFipsCrypto() ? 'stdout' : 'stderr', + ['--enable-fips'], +- compiledWithFips() ? FIPS_ENABLED : OPTION_ERROR_STRING, ++ testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING, + 'require("crypto").getFips()', + process.env); + + // --force-fips should turn FIPS mode on + testHelper( +- compiledWithFips() ? 'stdout' : 'stderr', ++ testFipsCrypto() ? 'stdout' : 'stderr', + ['--force-fips'], +- compiledWithFips() ? FIPS_ENABLED : OPTION_ERROR_STRING, ++ testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING, + 'require("crypto").getFips()', + process.env); + +@@ -106,7 +99,7 @@ if (!sharedOpenSSL()) { + testHelper( + 'stdout', + [`--openssl-config=${CNF_FIPS_ON}`], +- compiledWithFips() ? FIPS_ENABLED : FIPS_DISABLED, ++ testFipsCrypto() ? FIPS_ENABLED : FIPS_DISABLED, + 'require("crypto").getFips()', + process.env); + +@@ -114,7 +107,7 @@ if (!sharedOpenSSL()) { + testHelper( + 'stdout', + [], +- compiledWithFips() ? FIPS_ENABLED : FIPS_DISABLED, ++ testFipsCrypto() ? FIPS_ENABLED : FIPS_DISABLED, + 'require("crypto").getFips()', + Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_ON })); + +@@ -122,7 +115,7 @@ if (!sharedOpenSSL()) { + testHelper( + 'stdout', + [`--openssl-config=${CNF_FIPS_ON}`], +- compiledWithFips() ? FIPS_ENABLED : FIPS_DISABLED, ++ testFipsCrypto() ? FIPS_ENABLED : FIPS_DISABLED, + 'require("crypto").getFips()', + Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_OFF })); + } +@@ -136,50 +129,50 @@ testHelper( + + // --enable-fips should take precedence over OpenSSL config file + testHelper( +- compiledWithFips() ? 'stdout' : 'stderr', ++ testFipsCrypto() ? 'stdout' : 'stderr', + ['--enable-fips', `--openssl-config=${CNF_FIPS_OFF}`], +- compiledWithFips() ? FIPS_ENABLED : OPTION_ERROR_STRING, ++ testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING, + 'require("crypto").getFips()', + process.env); + + // OPENSSL_CONF should _not_ make a difference to --enable-fips + testHelper( +- compiledWithFips() ? 'stdout' : 'stderr', ++ testFipsCrypto() ? 'stdout' : 'stderr', + ['--enable-fips'], +- compiledWithFips() ? FIPS_ENABLED : OPTION_ERROR_STRING, ++ testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING, + 'require("crypto").getFips()', + Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_OFF })); + + // --force-fips should take precedence over OpenSSL config file + testHelper( +- compiledWithFips() ? 'stdout' : 'stderr', ++ testFipsCrypto() ? 'stdout' : 'stderr', + ['--force-fips', `--openssl-config=${CNF_FIPS_OFF}`], +- compiledWithFips() ? FIPS_ENABLED : OPTION_ERROR_STRING, ++ testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING, + 'require("crypto").getFips()', + process.env); + + // Using OPENSSL_CONF should not make a difference to --force-fips + testHelper( +- compiledWithFips() ? 'stdout' : 'stderr', ++ testFipsCrypto() ? 'stdout' : 'stderr', + ['--force-fips'], +- compiledWithFips() ? FIPS_ENABLED : OPTION_ERROR_STRING, ++ testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING, + 'require("crypto").getFips()', + Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_OFF })); + + // setFipsCrypto should be able to turn FIPS mode on + testHelper( +- compiledWithFips() ? 'stdout' : 'stderr', ++ testFipsCrypto() ? 'stdout' : 'stderr', + [], +- compiledWithFips() ? FIPS_ENABLED : FIPS_ERROR_STRING, ++ testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING, + '(require("crypto").setFips(true),' + + 'require("crypto").getFips())', + process.env); + + // setFipsCrypto should be able to turn FIPS mode on and off + testHelper( +- compiledWithFips() ? 'stdout' : 'stderr', ++ testFipsCrypto() ? 'stdout' : 'stderr', + [], +- compiledWithFips() ? FIPS_DISABLED : FIPS_ERROR_STRING, ++ testFipsCrypto() ? FIPS_DISABLED : FIPS_UNSUPPORTED_ERROR_STRING, + '(require("crypto").setFips(true),' + + 'require("crypto").setFips(false),' + + 'require("crypto").getFips())', +@@ -187,27 +180,27 @@ testHelper( + + // setFipsCrypto takes precedence over OpenSSL config file, FIPS on + testHelper( +- compiledWithFips() ? 'stdout' : 'stderr', ++ testFipsCrypto() ? 'stdout' : 'stderr', + [`--openssl-config=${CNF_FIPS_OFF}`], +- compiledWithFips() ? FIPS_ENABLED : FIPS_ERROR_STRING, ++ testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING, + '(require("crypto").setFips(true),' + + 'require("crypto").getFips())', + process.env); + + // setFipsCrypto takes precedence over OpenSSL config file, FIPS off + testHelper( +- compiledWithFips() ? 'stdout' : 'stderr', ++ 'stdout', + [`--openssl-config=${CNF_FIPS_ON}`], +- compiledWithFips() ? FIPS_DISABLED : FIPS_ERROR_STRING, ++ FIPS_DISABLED, + '(require("crypto").setFips(false),' + + 'require("crypto").getFips())', + process.env); + + // --enable-fips does not prevent use of setFipsCrypto API + testHelper( +- compiledWithFips() ? 'stdout' : 'stderr', ++ testFipsCrypto() ? 'stdout' : 'stderr', + ['--enable-fips'], +- compiledWithFips() ? FIPS_DISABLED : OPTION_ERROR_STRING, ++ testFipsCrypto() ? FIPS_DISABLED : FIPS_UNSUPPORTED_ERROR_STRING, + '(require("crypto").setFips(false),' + + 'require("crypto").getFips())', + process.env); +@@ -216,15 +209,15 @@ testHelper( + testHelper( + 'stderr', + ['--force-fips'], +- compiledWithFips() ? FIPS_ERROR_STRING2 : OPTION_ERROR_STRING, ++ testFipsCrypto() ? FIPS_ERROR_STRING2 : FIPS_UNSUPPORTED_ERROR_STRING, + 'require("crypto").setFips(false)', + process.env); + + // --force-fips makes setFipsCrypto enable a no-op (FIPS stays on) + testHelper( +- compiledWithFips() ? 'stdout' : 'stderr', ++ testFipsCrypto() ? 'stdout' : 'stderr', + ['--force-fips'], +- compiledWithFips() ? FIPS_ENABLED : OPTION_ERROR_STRING, ++ testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING, + '(require("crypto").setFips(true),' + + 'require("crypto").getFips())', + process.env); +@@ -233,7 +226,7 @@ testHelper( + testHelper( + 'stderr', + ['--force-fips', '--enable-fips'], +- compiledWithFips() ? FIPS_ERROR_STRING2 : OPTION_ERROR_STRING, ++ testFipsCrypto() ? FIPS_ERROR_STRING2 : FIPS_UNSUPPORTED_ERROR_STRING, + 'require("crypto").setFips(false)', + process.env); + +@@ -241,6 +234,6 @@ testHelper( + testHelper( + 'stderr', + ['--enable-fips', '--force-fips'], +- compiledWithFips() ? FIPS_ERROR_STRING2 : OPTION_ERROR_STRING, ++ testFipsCrypto() ? FIPS_ERROR_STRING2 : FIPS_UNSUPPORTED_ERROR_STRING, + 'require("crypto").setFips(false)', + process.env); +diff --git a/test/parallel/test-process-env-allowed-flags-are-documented.js b/test/parallel/test-process-env-allowed-flags-are-documented.js +index 1c91444..1d1605d 100644 +--- a/test/parallel/test-process-env-allowed-flags-are-documented.js ++++ b/test/parallel/test-process-env-allowed-flags-are-documented.js +@@ -45,17 +45,8 @@ const conditionalOpts = [ + include: common.hasCrypto, + filter: (opt) => { + return ['--openssl-config', '--tls-cipher-list', '--use-bundled-ca', +- '--use-openssl-ca' ].includes(opt); ++ '--use-openssl-ca', '--enable-fips', '--force-fips' ].includes(opt); + } +- }, { +- // We are using openssl_is_fips from the configuration because it could be +- // the case that OpenSSL is FIPS compatible but fips has not been enabled +- // (starting node with --enable-fips). If we use common.hasFipsCrypto +- // that would only tells us if fips has been enabled, but in this case we +- // want to check options which will be available regardless of whether fips +- // is enabled at runtime or not. +- include: process.config.variables.openssl_is_fips, +- filter: (opt) => opt.includes('-fips') + }, { + include: common.hasIntl, + filter: (opt) => opt === '--icu-data-dir' +-- +2.31.1 + diff --git a/SOURCES/0005-CVE-2021-23343-nodejs-path-parse.patch b/SOURCES/0005-CVE-2021-23343-nodejs-path-parse.patch new file mode 100644 index 0000000..201721d --- /dev/null +++ b/SOURCES/0005-CVE-2021-23343-nodejs-path-parse.patch @@ -0,0 +1,180 @@ +https://github.com/jbgutierrez/path-parse/pull/10 + +From 72c38e3a36b8ed2ec03960ac659aa114cbe6a420 Mon Sep 17 00:00:00 2001 +From: Jeffrey Pinyan <jeffrey.pinyan@ithreat.com> +Date: Thu, 13 May 2021 10:53:50 -0400 +Subject: [PATCH 1/2] fixed regexes to avoid ReDoS attacks + +Signed-off-by: rpm-build <rpm-build> +--- + deps/npm/node_modules/path-parse/index.js | 6 +++--- + deps/npm/node_modules/path-parse/redos.js | 20 ++++++++++++++++++++ + 2 files changed, 23 insertions(+), 3 deletions(-) + create mode 100644 deps/npm/node_modules/path-parse/redos.js + +diff --git a/deps/npm/node_modules/path-parse/index.js b/deps/npm/node_modules/path-parse/index.js +index 3b7601f..e6b2af1 100644 +--- a/deps/npm/node_modules/path-parse/index.js ++++ b/deps/npm/node_modules/path-parse/index.js +@@ -5,11 +5,11 @@ var isWindows = process.platform === 'win32'; + // Regex to split a windows path into three parts: [*, device, slash, + // tail] windows-only + var splitDeviceRe = +- /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; ++ /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?(.*)$/s; + + // Regex to split the tail part of the above into [*, dir, basename, ext] + var splitTailRe = +- /^([\s\S]*?)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/; ++ /^((?:[^\\\/]*[\\\/])*)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/; + + var win32 = {}; + +@@ -51,7 +51,7 @@ win32.parse = function(pathString) { + // Split a filename into [root, dir, basename, ext], unix version + // 'root' is just a slash, or nothing. + var splitPathRe = +- /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; ++ /^(\/?|)((?:[^\/]*\/)*)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; + var posix = {}; + + +diff --git a/deps/npm/node_modules/path-parse/redos.js b/deps/npm/node_modules/path-parse/redos.js +new file mode 100644 +index 0000000..261947f +--- /dev/null ++++ b/deps/npm/node_modules/path-parse/redos.js +@@ -0,0 +1,20 @@ ++var pathParse = require('.'); ++ ++function build_attack(n) { ++ var ret = "" ++ for (var i = 0; i < n; i++) { ++ ret += "/" ++ } ++ return ret + "◎"; ++} ++ ++for(var i = 1; i <= 5000000; i++) { ++ if (i % 10000 == 0) { ++ var time = Date.now(); ++ var attack_str = build_attack(i) ++ pathParse.posix(attack_str); ++ pathParse.win32(attack_str); ++ var time_cost = Date.now() - time; ++ console.log("attack_str.length: " + attack_str.length + ": " + time_cost+" ms") ++ } ++} +-- +2.31.1 + + +From 44d1c9cd047988bb819707c726d9640f8aabe04d Mon Sep 17 00:00:00 2001 +From: Jeffrey Pinyan <jeffrey.pinyan@ithreat.com> +Date: Thu, 13 May 2021 11:51:45 -0400 +Subject: [PATCH 2/2] streamlined regexes, simplified parse() returns + +Signed-off-by: rpm-build <rpm-build> +--- + deps/npm/node_modules/path-parse/index.js | 52 ++++++++--------------- + 1 file changed, 17 insertions(+), 35 deletions(-) + +diff --git a/deps/npm/node_modules/path-parse/index.js b/deps/npm/node_modules/path-parse/index.js +index e6b2af1..f062d0a 100644 +--- a/deps/npm/node_modules/path-parse/index.js ++++ b/deps/npm/node_modules/path-parse/index.js +@@ -2,29 +2,14 @@ + + var isWindows = process.platform === 'win32'; + +-// Regex to split a windows path into three parts: [*, device, slash, +-// tail] windows-only +-var splitDeviceRe = +- /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?(.*)$/s; +- +-// Regex to split the tail part of the above into [*, dir, basename, ext] +-var splitTailRe = +- /^((?:[^\\\/]*[\\\/])*)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/; ++// Regex to split a windows path into into [dir, root, basename, name, ext] ++var splitWindowsRe = ++ /^(((?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?[\\\/]?)(?:[^\\\/]*[\\\/])*)((\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))[\\\/]*$/; + + var win32 = {}; + +-// Function to split a filename into [root, dir, basename, ext] + function win32SplitPath(filename) { +- // Separate device+slash from tail +- var result = splitDeviceRe.exec(filename), +- device = (result[1] || '') + (result[2] || ''), +- tail = result[3] || ''; +- // Split the tail into dir, basename and extension +- var result2 = splitTailRe.exec(tail), +- dir = result2[1], +- basename = result2[2], +- ext = result2[3]; +- return [device, dir, basename, ext]; ++ return splitWindowsRe.exec(filename).slice(1); + } + + win32.parse = function(pathString) { +@@ -34,24 +19,24 @@ win32.parse = function(pathString) { + ); + } + var allParts = win32SplitPath(pathString); +- if (!allParts || allParts.length !== 4) { ++ if (!allParts || allParts.length !== 5) { + throw new TypeError("Invalid path '" + pathString + "'"); + } + return { +- root: allParts[0], +- dir: allParts[0] + allParts[1].slice(0, -1), ++ root: allParts[1], ++ dir: allParts[0] === allParts[1] ? allParts[0] : allParts[0].slice(0, -1), + base: allParts[2], +- ext: allParts[3], +- name: allParts[2].slice(0, allParts[2].length - allParts[3].length) ++ ext: allParts[4], ++ name: allParts[3] + }; + }; + + + +-// Split a filename into [root, dir, basename, ext], unix version ++// Split a filename into [dir, root, basename, name, ext], unix version + // 'root' is just a slash, or nothing. + var splitPathRe = +- /^(\/?|)((?:[^\/]*\/)*)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; ++ /^((\/?)(?:[^\/]*\/)*)((\.{1,2}|[^\/]+?|)(\.[^.\/]*|))[\/]*$/; + var posix = {}; + + +@@ -67,19 +52,16 @@ posix.parse = function(pathString) { + ); + } + var allParts = posixSplitPath(pathString); +- if (!allParts || allParts.length !== 4) { ++ if (!allParts || allParts.length !== 5) { + throw new TypeError("Invalid path '" + pathString + "'"); + } +- allParts[1] = allParts[1] || ''; +- allParts[2] = allParts[2] || ''; +- allParts[3] = allParts[3] || ''; +- ++ + return { +- root: allParts[0], +- dir: allParts[0] + allParts[1].slice(0, -1), ++ root: allParts[1], ++ dir: allParts[0].slice(0, -1), + base: allParts[2], +- ext: allParts[3], +- name: allParts[2].slice(0, allParts[2].length - allParts[3].length) ++ ext: allParts[4], ++ name: allParts[3], + }; + }; + +-- +2.31.1 + diff --git a/SPECS/nodejs.spec b/SPECS/nodejs.spec index d514358..2d55f11 100644 --- a/SPECS/nodejs.spec +++ b/SPECS/nodejs.spec @@ -15,7 +15,7 @@ # This is used by both the nodejs package and the npm subpackage thar # has a separate version - the name is special so that rpmdev-bumpspec # will bump this rather than adding .1 to the end. -%global baserelease 2 +%global baserelease 1 %{?!_pkgdocdir:%global _pkgdocdir %{_docdir}/%{name}-%{version}} @@ -27,7 +27,7 @@ %global nodejs_epoch 1 %global nodejs_major 14 %global nodejs_minor 17 -%global nodejs_patch 3 +%global nodejs_patch 5 %global nodejs_abi %{nodejs_major}.%{nodejs_minor} %if %{?with_libs} == 1 # nodejs_soversion - from NODE_MODULE_VERSION in src/node_version.h @@ -55,7 +55,7 @@ # https://github.com/nodejs/node/pull/9332 %global c_ares_major 1 %global c_ares_minor 17 -%global c_ares_patch 1 +%global c_ares_patch 2 %global c_ares_version %{c_ares_major}.%{c_ares_minor}.%{c_ares_patch} # llhttp - from deps/llhttp/include/llhttp.h @@ -110,7 +110,7 @@ %global npm_epoch 1 %global npm_major 6 %global npm_minor 14 -%global npm_patch 13 +%global npm_patch 14 %global npm_version %{npm_major}.%{npm_minor}.%{npm_patch} # uvwasi - from deps/uvwasi/include/uvwasi.h @@ -165,8 +165,11 @@ Patch1: 0001-Disable-running-gyp-on-shared-deps.patch Patch2: 0002-Install-both-binaries-and-use-libdir.patch %endif -# RHBZ#1915296 - yarn install crashes with nodejs:14 on aarch64 -# Patch3: 0003-yarn-not-installable-on-aarch64.patch +# Make FIPS always available +# https://github.com/nodejs/node/issues/34903 +Patch3: 0004-always-available-fips-options.patch + +Patch4: 0005-CVE-2021-23343-nodejs-path-parse.patch BuildRequires: make BuildRequires: python3-devel @@ -825,6 +828,19 @@ end %changelog +* Tue Aug 17 2021 Zuzana Svetlikova <zsvetlik@redhat.com> - 1:14.17.5-1 +- Resolves CVE-2021-22930, CVE-2021-22931, CVE-2021-22939, CVE-2021-22940, +- CVE-2021-23343, CVE-2021-32803, CVE-2021-32804, CVE-2021-3672 +- Resolves RHBZ#1847529 (make FIPS always available) +- Resolves: RHBZ#1988599, RHBZ#1994000, RHBZ#1993998, RHBZ#1993095 +- Resolves: RHBZ#1994028, RHBZ#1994402, RHBZ#1994406, RHBZ#1994398 +- Resolves: RHBZ#1993924 (make FIPS always available) + +* Mon Aug 09 2021 Zuzana Svetlikova <zsvetlik@redhat.com> - 1:14.17.3-3 +- Resolves: RHBZ#1991584, RHBZ#1991578 +- Resolves CVE-2021-23362 CVE-2021-27290 +- Bump for missing mentions of CVEs + * Thu Jul 08 2021 Zuzana Svetlikova <zsvetlik@redhat.com> - 1:14.17.3-2 - Resolves: RHBZ#1980032, RHBZ#1978203 - Resolves RHBZ#1842826