nodejs: Fix CVE-2022-35255

Add patch to fix CVE-2022-35255

Link: https://sources.debian.org/src/nodejs/12.22.12~dfsg-1~deb11u3/debian/patches/cve-2022-35255.patch

Signed-off-by: Poonam Jadhav <Poonam.Jadhav@kpit.com>
Signed-off-by: Omkar Patil <omkarpatil10.93@gmail.com>
Signed-off-by: Armin Kuster <akuster808@gmail.com>
This commit is contained in:
Poonam Jadhav
2023-03-03 18:02:13 +05:30
committed by Armin Kuster
parent df7fba3744
commit b691797f77
2 changed files with 238 additions and 0 deletions
@@ -0,0 +1,237 @@
Origin: https://github.com/nodejs/node/commit/0c2a5723beff39d1f62daec96b5389da3d427e79
Reviewed-by: Aron Xu <aron@debian.org>
Last-Update: 2022-01-05
Comment:
Although WebCrypto is not implemented in 12.x series, this fix is introducing
enhancment to the crypto setup of V8:EntropySource().
commit 0c2a5723beff39d1f62daec96b5389da3d427e79
Author: Ben Noordhuis <info@bnoordhuis.nl>
Date: Sun Sep 11 10:48:34 2022 +0200
crypto: fix weak randomness in WebCrypto keygen
Commit dae283d96f from August 2020 introduced a call to EntropySource()
in SecretKeyGenTraits::DoKeyGen() in src/crypto/crypto_keygen.cc. There
are two problems with that:
1. It does not check the return value, it assumes EntropySource() always
succeeds, but it can (and sometimes will) fail.
2. The random data returned byEntropySource() may not be
cryptographically strong and therefore not suitable as keying
material.
An example is a freshly booted system or a system without /dev/random or
getrandom(2).
EntropySource() calls out to openssl's RAND_poll() and RAND_bytes() in a
best-effort attempt to obtain random data. OpenSSL has a built-in CSPRNG
but that can fail to initialize, in which case it's possible either:
1. No random data gets written to the output buffer, i.e., the output is
unmodified, or
2. Weak random data is written. It's theoretically possible for the
output to be fully predictable because the CSPRNG starts from a
predictable state.
Replace EntropySource() and CheckEntropy() with new function CSPRNG()
that enforces checking of the return value. Abort on startup when the
entropy pool fails to initialize because that makes it too easy to
compromise the security of the process.
Refs: https://hackerone.com/bugs?report_id=1690000
Refs: https://github.com/nodejs/node/pull/35093
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
PR-URL: #346
Backport-PR-URL: #351
CVE-ID: CVE-2022-35255
CVE: CVE-2022-35255
Upstream-Status: Backport [https://sources.debian.org/src/nodejs/12.22.12~dfsg-1~deb11u3/debian/patches/cve-2022-35255.patch]
Comment: No hunks refreshed
Signed-off-by: Poonam Jadhav <Poonam.Jadhav@kpit.com>
Index: nodejs-12.22.12~dfsg/node.gyp
===================================================================
--- nodejs-12.22.12~dfsg.orig/node.gyp
+++ nodejs-12.22.12~dfsg/node.gyp
@@ -743,6 +743,8 @@
'openssl_default_cipher_list%': '',
},
+ 'cflags': ['-Werror=unused-result'],
+
'defines': [
'NODE_ARCH="<(target_arch)"',
'NODE_PLATFORM="<(OS)"',
Index: nodejs-12.22.12~dfsg/src/node_crypto.cc
===================================================================
--- nodejs-12.22.12~dfsg.orig/src/node_crypto.cc
+++ nodejs-12.22.12~dfsg/src/node_crypto.cc
@@ -386,48 +386,14 @@ void ThrowCryptoError(Environment* env,
env->isolate()->ThrowException(exception);
}
+MUST_USE_RESULT CSPRNGResult CSPRNG(void* buffer, size_t length) {
+ do {
+ if (1 == RAND_status())
+ if (1 == RAND_bytes(static_cast<unsigned char*>(buffer), length))
+ return {true};
+ } while (1 == RAND_poll());
-// Ensure that OpenSSL has enough entropy (at least 256 bits) for its PRNG.
-// The entropy pool starts out empty and needs to fill up before the PRNG
-// can be used securely. Once the pool is filled, it never dries up again;
-// its contents is stirred and reused when necessary.
-//
-// OpenSSL normally fills the pool automatically but not when someone starts
-// generating random numbers before the pool is full: in that case OpenSSL
-// keeps lowering the entropy estimate to thwart attackers trying to guess
-// the initial state of the PRNG.
-//
-// When that happens, we will have to wait until enough entropy is available.
-// That should normally never take longer than a few milliseconds.
-//
-// OpenSSL draws from /dev/random and /dev/urandom. While /dev/random may
-// block pending "true" randomness, /dev/urandom is a CSPRNG that doesn't
-// block under normal circumstances.
-//
-// The only time when /dev/urandom may conceivably block is right after boot,
-// when the whole system is still low on entropy. That's not something we can
-// do anything about.
-inline void CheckEntropy() {
- for (;;) {
- int status = RAND_status();
- CHECK_GE(status, 0); // Cannot fail.
- if (status != 0)
- break;
-
- // Give up, RAND_poll() not supported.
- if (RAND_poll() == 0)
- break;
- }
-}
-
-
-bool EntropySource(unsigned char* buffer, size_t length) {
- // Ensure that OpenSSL's PRNG is properly seeded.
- CheckEntropy();
- // RAND_bytes() can return 0 to indicate that the entropy data is not truly
- // random. That's okay, it's still better than V8's stock source of entropy,
- // which is /dev/urandom on UNIX platforms and the current time on Windows.
- return RAND_bytes(buffer, length) != -1;
+ return {false};
}
void SecureContext::Initialize(Environment* env, Local<Object> target) {
@@ -649,9 +615,9 @@ void SecureContext::Init(const FunctionC
// OpenSSL 1.1.0 changed the ticket key size, but the OpenSSL 1.0.x size was
// exposed in the public API. To retain compatibility, install a callback
// which restores the old algorithm.
- if (RAND_bytes(sc->ticket_key_name_, sizeof(sc->ticket_key_name_)) <= 0 ||
- RAND_bytes(sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_)) <= 0 ||
- RAND_bytes(sc->ticket_key_aes_, sizeof(sc->ticket_key_aes_)) <= 0) {
+ if (CSPRNG(sc->ticket_key_name_, sizeof(sc->ticket_key_name_)).is_err() ||
+ CSPRNG(sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_)).is_err() ||
+ CSPRNG(sc->ticket_key_aes_, sizeof(sc->ticket_key_aes_)).is_err()) {
return env->ThrowError("Error generating ticket keys");
}
SSL_CTX_set_tlsext_ticket_key_cb(sc->ctx_.get(), TicketCompatibilityCallback);
@@ -1643,7 +1609,7 @@ int SecureContext::TicketCompatibilityCa
if (enc) {
memcpy(name, sc->ticket_key_name_, sizeof(sc->ticket_key_name_));
- if (RAND_bytes(iv, 16) <= 0 ||
+ if (CSPRNG(iv, 16).is_err() ||
EVP_EncryptInit_ex(ectx, EVP_aes_128_cbc(), nullptr,
sc->ticket_key_aes_, iv) <= 0 ||
HMAC_Init_ex(hctx, sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_),
@@ -5867,8 +5833,7 @@ struct RandomBytesJob : public CryptoJob
: CryptoJob(env), rc(Nothing<int>()) {}
inline void DoThreadPoolWork() override {
- CheckEntropy(); // Ensure that OpenSSL's PRNG is properly seeded.
- rc = Just(RAND_bytes(data, size));
+ rc = Just(int(CSPRNG(data, size).is_ok()));
if (0 == rc.FromJust()) errors.Capture();
}
@@ -6318,8 +6283,8 @@ class GenerateKeyPairJob : public Crypto
}
inline bool GenerateKey() {
- // Make sure that the CSPRNG is properly seeded so the results are secure.
- CheckEntropy();
+ // Make sure that the CSPRNG is properly seeded.
+ CHECK(CSPRNG(nullptr, 0).is_ok());
// Create the key generation context.
EVPKeyCtxPointer ctx = config_->Setup();
Index: nodejs-12.22.12~dfsg/src/node_crypto.h
===================================================================
--- nodejs-12.22.12~dfsg.orig/src/node_crypto.h
+++ nodejs-12.22.12~dfsg/src/node_crypto.h
@@ -840,7 +840,19 @@ class ECDH final : public BaseObject {
const EC_GROUP* group_;
};
-bool EntropySource(unsigned char* buffer, size_t length);
+struct CSPRNGResult {
+ const bool ok;
+ MUST_USE_RESULT bool is_ok() const { return ok; }
+ MUST_USE_RESULT bool is_err() const { return !ok; }
+};
+
+// Either succeeds with exactly |length| bytes of cryptographically
+// strong pseudo-random data, or fails. This function may block.
+// Don't assume anything about the contents of |buffer| on error.
+// As a special case, |length == 0| can be used to check if the CSPRNG
+// is properly seeded without consuming entropy.
+MUST_USE_RESULT CSPRNGResult CSPRNG(void* buffer, size_t length);
+
#ifndef OPENSSL_NO_ENGINE
void SetEngine(const v8::FunctionCallbackInfo<v8::Value>& args);
#endif // !OPENSSL_NO_ENGINE
Index: nodejs-12.22.12~dfsg/src/inspector_io.cc
===================================================================
--- nodejs-12.22.12~dfsg.orig/src/inspector_io.cc
+++ nodejs-12.22.12~dfsg/src/inspector_io.cc
@@ -46,8 +46,7 @@ std::string ScriptPath(uv_loop_t* loop,
// Used ver 4 - with numbers
std::string GenerateID() {
uint16_t buffer[8];
- CHECK(crypto::EntropySource(reinterpret_cast<unsigned char*>(buffer),
- sizeof(buffer)));
+ CHECK(crypto::CSPRNG(buffer, sizeof(buffer)).is_ok());
char uuid[256];
snprintf(uuid, sizeof(uuid), "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
Index: nodejs-12.22.12~dfsg/src/node.cc
===================================================================
--- nodejs-12.22.12~dfsg.orig/src/node.cc
+++ nodejs-12.22.12~dfsg/src/node.cc
@@ -969,9 +969,17 @@ InitializationResult InitializeOncePerPr
// the random source is properly initialized first.
OPENSSL_init();
#endif // NODE_FIPS_MODE
- // V8 on Windows doesn't have a good source of entropy. Seed it from
- // OpenSSL's pool.
- V8::SetEntropySource(crypto::EntropySource);
+ // Ensure CSPRNG is properly seeded.
+ CHECK(crypto::CSPRNG(nullptr, 0).is_ok());
+
+ V8::SetEntropySource([](unsigned char* buffer, size_t length) {
+ // V8 falls back to very weak entropy when this function fails
+ // and /dev/urandom isn't available. That wouldn't be so bad if
+ // the entropy was only used for Math.random() but it's also used for
+ // hash table and address space layout randomization. Better to abort.
+ CHECK(crypto::CSPRNG(buffer, length).is_ok());
+ return true;
+ });
#endif // HAVE_OPENSSL
per_process::v8_platform.Initialize(
@@ -23,6 +23,7 @@ SRC_URI = "http://nodejs.org/dist/v${PV}/node-v${PV}.tar.xz \
file://mips-warnings.patch \
file://0001-Remove-use-of-register-r7-because-llvm-now-issues-an.patch \
file://CVE-2022-32212.patch \
file://CVE-2022-35255.patch \
"
SRC_URI_append_class-target = " \
file://0002-Using-native-binaries.patch \