From 032c6d1d81c4e22425863b178b445419fd7d8c03 Mon Sep 17 00:00:00 2001 From: haramjeong <04harams77@gmail.com> Date: Fri, 25 Sep 2026 23:07:52 +0900 Subject: [PATCH] crypto: ignore directory paths in OPENSSL_CONF Skip directory configuration paths without suppressing errors from actual OpenSSL configuration files. An empty filename avoids reading the same directory again through OPENSSL_CONF. Add coverage for directory paths, malformed configuration files, and command-line configuration precedence. Fixes: https://github.com/nodejs/node/issues/63256 Signed-off-by: haramjeong <04harams77@gmail.com> Assisted-by: Codex --- src/node.cc | 20 ++++++++ .../parallel/test-openssl-config-directory.js | 51 +++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 test/parallel/test-openssl-config-directory.js diff --git a/src/node.cc b/src/node.cc index 2993fda0e9ca..d4c1a5e3bff3 100644 --- a/src/node.cc +++ b/src/node.cc @@ -102,6 +102,7 @@ // ========== global C headers ========== #include // _O_RDWR +#include #include #if defined(NODE_HAVE_I18N_SUPPORT) @@ -1275,6 +1276,25 @@ InitializeOncePerProcessInternal(const std::vector& args, conf_file = per_process::cli_options->openssl_config.c_str(); } + // Ignore directories without suppressing errors in actual config files. + if (conf_file != nullptr) { + struct stat st; + if (stat(conf_file, &st) == 0) { +#if defined(S_ISDIR) + if (S_ISDIR(st.st_mode)) { +#else + if ((st.st_mode & S_IFMT) == S_IFDIR) { +#endif + std::string warning = "Warning: OPENSSL_CONF path is a directory; " + "ignoring: "; + warning += conf_file; + fprintf(stderr, "%s\n", warning.c_str()); + // NULL would read OPENSSL_CONF again, including this directory. + conf_file = ""; + } + } + } + OPENSSL_INIT_SETTINGS* settings = OPENSSL_INIT_new(); CHECK_NOT_NULL(settings); OPENSSL_INIT_set_config_filename(settings, conf_file); diff --git a/test/parallel/test-openssl-config-directory.js b/test/parallel/test-openssl-config-directory.js new file mode 100644 index 000000000000..30a7525e4ae2 --- /dev/null +++ b/test/parallel/test-openssl-config-directory.js @@ -0,0 +1,51 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +if (process.features.openssl_is_boringssl) + common.skip('BoringSSL does not use OpenSSL configuration files'); +if (!require('../common/crypto').hasOpenSSL3) + common.skip('requires OpenSSL 3'); + +const assert = require('node:assert'); +const { spawnSync } = require('node:child_process'); +const { writeFileSync } = require('node:fs'); +const { join } = require('node:path'); +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +function run(conf, args = []) { + return spawnSync(process.execPath, [...args, '-p', '42'], { + encoding: 'utf8', + env: { ...process.env, OPENSSL_CONF: conf }, + }); +} + +const directory = run(tmpdir.path); +assert.ifError(directory.error); +assert.strictEqual(directory.status, 0, directory.stderr); +assert.strictEqual(directory.stdout.trim(), '42'); +assert.match(directory.stderr, /OPENSSL_CONF path is a directory; ignoring:/); + +const validConfig = join(tmpdir.path, 'valid.cnf'); +writeFileSync(validConfig, ''); +const overridden = run(tmpdir.path, [`--openssl-config=${validConfig}`]); +assert.ifError(overridden.error); +assert.strictEqual(overridden.status, 0, overridden.stderr); +assert.strictEqual(overridden.stdout.trim(), '42'); +assert.strictEqual(overridden.stderr, ''); + +// Ignoring a directory must not turn other configuration errors into warnings. +const invalidConfig = join(tmpdir.path, 'invalid.cnf'); +writeFileSync(invalidConfig, '[unterminated\n'); +for (const result of [ + run(invalidConfig), + run(tmpdir.path, [`--openssl-config=${invalidConfig}`]), +]) { + assert.ifError(result.error); + assert.strictEqual(result.signal, null); + assert.notStrictEqual(result.status, 0); + assert.strictEqual(result.stdout, ''); + assert.match(result.stderr, /OpenSSL configuration error/); +}