diff --git a/.gitignore b/.gitignore index f174806..d8fc543 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ SOURCES/icu4c-67_1-src.tgz -SOURCES/node-v12.22.3-stripped.tar.gz +SOURCES/node-v12.22.5-stripped.tar.gz diff --git a/.nodejs.metadata b/.nodejs.metadata index 5577dc6..c0867c1 100644 --- a/.nodejs.metadata +++ b/.nodejs.metadata @@ -1,2 +1,2 @@ 6822a4a94324d1ba591b3e8ef084e4491af253c1 SOURCES/icu4c-67_1-src.tgz -753aeca4079c2f2dd5e4c587ae74ce0d7cd93917 SOURCES/node-v12.22.3-stripped.tar.gz +bb98afb22215e659a77853964f7575da6b1535e3 SOURCES/node-v12.22.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..fb90f8f --- /dev/null +++ b/SOURCES/0004-always-available-fips-options.patch @@ -0,0 +1,622 @@ +From 7bc4111b770ada25cdd6e1b938ca7a914617ea53 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> +--- + 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 86635f267b..6f14fa6810 100644 +--- a/doc/api/cli.md ++++ b/doc/api/cli.md +@@ -183,8 +183,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 +@@ -550,8 +550,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 b2bcc4d0a4..93d5e21fa0 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, +@@ -191,10 +189,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 +@@ -213,19 +209,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; + } +@@ -247,10 +235,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 116c1c7149..34f385f652 100644 +--- a/node.gypi ++++ b/node.gypi +@@ -320,9 +320,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 46e8f74cc2..0a5c3ee8ee 100644 +--- a/src/node.cc ++++ b/src/node.cc +@@ -964,11 +964,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 6ee3164a13..e229eee765 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 764dcb8720..f142e625ef 100644 +--- a/src/node_crypto.cc ++++ b/src/node_crypto.cc +@@ -50,6 +50,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 +@@ -97,6 +102,7 @@ using v8::Signature; + using v8::String; + using v8::Uint32; + using v8::Undefined; ++using v8::TryCatch; + using v8::Value; + + #ifdef OPENSSL_NO_OCB +@@ -3595,12 +3601,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) +@@ -3786,13 +3790,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, +@@ -4712,7 +4714,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); +@@ -4728,7 +4729,6 @@ static inline bool ValidateDSAParameters(EVP_PKEY* key) { + (L == 2048 && N == 256) || + (L == 3072 && N == 256); + } +-#endif // NODE_FIPS_MODE + + return true; + } +@@ -6889,7 +6889,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 || +@@ -6899,12 +6898,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. +@@ -6950,7 +6947,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); + } +@@ -6968,17 +6964,33 @@ 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); ++} + + + 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"), +@@ -7007,10 +7019,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 0240b2ef58..d1230da1ad 100644 +--- a/src/node_options.cc ++++ b/src/node_options.cc +@@ -729,7 +729,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, +@@ -738,7 +737,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 aa138c6970..f5e1e7da57 100644 +--- a/src/node_options.h ++++ b/src/node_options.h +@@ -236,10 +236,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 e115124b04..ed58bf085c 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; + + +@@ -29,9 +27,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 eae3134402..a1ed645184 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 0e0af9471c..af10809634 100644 +--- a/test/parallel/test-process-env-allowed-flags-are-documented.js ++++ b/test/parallel/test-process-env-allowed-flags-are-documented.js +@@ -44,17 +44,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' }, + { include: process.features.inspector, +-- +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 86a5644..28a459a 100644 --- a/SPECS/nodejs.spec +++ b/SPECS/nodejs.spec @@ -18,7 +18,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}} @@ -30,7 +30,7 @@ %global nodejs_epoch 1 %global nodejs_major 12 %global nodejs_minor 22 -%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 @@ -57,8 +57,8 @@ # c-ares - from deps/cares/include/ares_version.h # https://github.com/nodejs/node/pull/9332 %global c_ares_major 1 -%global c_ares_minor 16 -%global c_ares_patch 1 +%global c_ares_minor 17 +%global c_ares_patch 2 %global c_ares_version %{c_ares_major}.%{c_ares_minor}.%{c_ares_patch} # http-parser - from deps/http_parser/http_parser.h @@ -106,7 +106,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 @@ -170,6 +170,12 @@ Patch2: 0002-Install-both-binaries-and-use-libdir.patch # Upstream patch to use getauxval Patch3: 0003-src-use-getauxval-in-node_main.cc.patch +# Make FIPS always available +# https://github.com/nodejs/node/issues/34903 +Patch4: 0004-always-available-fips-options.patch + +Patch5: 0005-CVE-2021-23343-nodejs-path-parse.patch + BuildRequires: make BuildRequires: python2-devel BuildRequires: python3-devel @@ -866,6 +872,19 @@ end %changelog +* Mon Aug 16 2021 Zuzana Svetlikova <zsvetlik@redhat.com> - 1:12.22.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#1951621 (make FIPS always available) +- Resolves: RHBZ#1988595, RHBZ#1993992, RHBZ#1993989, RHBZ#1993093 +- Resolves: RHBZ#1994025, RHBZ#1994403, RHBZ#1994407, RHBZ#1994399 +- Resolves: RHBZ#1993927 (make FIPS always available) + +* Mon Aug 09 2021 Zuzana Svetlikova <zsvetlik@redhat.com> - 1:12.22.3-3 +- Resolves CVE-2021-23362 CVE-2021-27290 +- Resolves: RHBZ#1991584, RHBZ#1991578 +- Add missing CVE trackers + * Thu Jul 08 2021 Zuzana Svetlikova <zsvetlik@redhat.com> - 1:12.22.3-2 - Resolves: RHBZ#1980031, RHBZ#1978201 - Fix typo, BR systemtap-sdt-level always, remove y18n patch