From 86d1c0cc6a5dcf57e413a1cc1c29203e87cf9a14 Mon Sep 17 00:00:00 2001
From: Daniel Bevenius <daniel.bevenius@gmail.com>
Date: Sat, 16 Oct 2021 08:50:16 +0200
Subject: [PATCH] src: add --openssl-legacy-provider option

This commit adds an option to Node.js named --openssl-legacy-provider
and if specified will load OpenSSL 3.0 Legacy provider.

$ ./node --help
...
--openssl-legacy-provider  enable OpenSSL 3.0 legacy provider

Example usage:

$ ./node --openssl-legacy-provider  -p 'crypto.createHash("md4")'
Hash {
  _options: undefined,
  [Symbol(kHandle)]: Hash {},
  [Symbol(kState)]: { [Symbol(kFinalized)]: false }
}

Co-authored-by: Richard Lau <rlau@redhat.com>
Signed-off-by: Signed-off-by: Andrej Valek <andrej.valek@siemens.com>
Upstream-Status: Backport [https://github.com/nodejs/node/issues/40455]
---
 doc/api/cli.md                                         | 10 ++++++++++
 src/crypto/crypto_util.cc                              | 10 ++++++++++
 src/node_options.cc                                    | 10 ++++++++++
 src/node_options.h                                     |  7 +++++++
 .../test-process-env-allowed-flags-are-documented.js   |  5 +++++
 5 files changed, 42 insertions(+)

diff --git a/doc/api/cli.md b/doc/api/cli.md
index 74057706bf8d..608b9cdeddf1 100644
--- a/doc/api/cli.md
+++ b/doc/api/cli.md
@@ -687,6 +687,14 @@ Load an OpenSSL configuration file on startup. Among other uses, this can be
 used to enable FIPS-compliant crypto if Node.js is built
 against FIPS-enabled OpenSSL.
 
+### `--openssl-legacy-provider`
+<!-- YAML
+added: REPLACEME
+-->
+
+Enable OpenSSL 3.0 legacy provider. For more information please see
+[providers readme][].
+
 ### `--pending-deprecation`
 
 <!-- YAML
@@ -1544,6 +1552,7 @@ Node.js options that are allowed are:
 * `--no-warnings`
 * `--node-memory-debug`
 * `--openssl-config`
+* `--openssl-legacy-provider`
 * `--pending-deprecation`
 * `--policy-integrity`
 * `--preserve-symlinks-main`
@@ -1933,6 +1942,7 @@ $ node --max-old-space-size=1536 index.js
 [emit_warning]: process.md#processemitwarningwarning-options
 [jitless]: https://v8.dev/blog/jitless
 [libuv threadpool documentation]: https://docs.libuv.org/en/latest/threadpool.html
+[providers readme]: https://github.com/openssl/openssl/blob/openssl-3.0.0/README-PROVIDERS.md
 [remote code execution]: https://www.owasp.org/index.php/Code_Injection
 [security warning]: #warning-binding-inspector-to-a-public-ipport-combination-is-insecure
 [timezone IDs]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc
index 7e0c8ba3eb60..796ea3025e41 100644
--- a/src/crypto/crypto_util.cc
+++ b/src/crypto/crypto_util.cc
@@ -148,6 +148,16 @@ void InitCryptoOnce() {
   }
 #endif
 
+#if OPENSSL_VERSION_MAJOR >= 3
+  // --openssl-legacy-provider
+  if (per_process::cli_options->openssl_legacy_provider) {
+    OSSL_PROVIDER* legacy_provider = OSSL_PROVIDER_load(nullptr, "legacy");
+    if (legacy_provider == nullptr) {
+      fprintf(stderr, "Unable to load legacy provider.\n");
+    }
+  }
+#endif
+
   OPENSSL_init_ssl(0, settings);
   OPENSSL_INIT_free(settings);
   settings = nullptr;
diff --git a/src/node_options.cc b/src/node_options.cc
index 00bdc6688a4c..3363860919a9 100644
--- a/src/node_options.cc
+++ b/src/node_options.cc
@@ -4,6 +4,9 @@
 #include "env-inl.h"
 #include "node_binding.h"
 #include "node_internals.h"
+#if HAVE_OPENSSL
+#include "openssl/opensslv.h"
+#endif
 
 #include <errno.h>
 #include <sstream>
diff --git a/src/node_options.h b/src/node_options.h
index fd772478d04d..1c0e018ab16f 100644
--- a/src/node_options.h
+++ b/src/node_options.h
@@ -11,6 +11,10 @@
 #include "node_mutex.h"
 #include "util.h"
 
+#if HAVE_OPENSSL
+#include "openssl/opensslv.h"
+#endif
+
 namespace node {
 
 class HostPort {
@@ -251,6 +255,9 @@ class PerProcessOptions : public Options {
   bool enable_fips_crypto = false;
   bool force_fips_crypto = false;
 #endif
+#if OPENSSL_VERSION_MAJOR >= 3
+  bool openssl_legacy_provider = false;
+#endif
 
   // Per-process because reports can be triggered outside a known V8 context.
   bool report_on_fatalerror = false;
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 64626b71f019..8a4e35997907 100644
--- a/test/parallel/test-process-env-allowed-flags-are-documented.js
+++ b/test/parallel/test-process-env-allowed-flags-are-documented.js
@@ -43,6 +43,10 @@ for (const line of [...nodeOptionsLines, ...v8OptionsLines]) {
   }
 }
 
+if (!common.hasOpenSSL3) {
+  documented.delete('--openssl-legacy-provider');
+}
+
 // Filter out options that are conditionally present.
 const conditionalOpts = [
   {
@@ -50,6 +54,7 @@ const conditionalOpts = [
     filter: (opt) => {
       return [
         '--openssl-config',
+        common.hasOpenSSL3 ? '--openssl-legacy-provider' : '',
         '--tls-cipher-list',
         '--use-bundled-ca',
         '--use-openssl-ca',

