Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ca37352
Implement NODE_USE_SYSTEM_CA=1 feature with dynamic Security framewor…
Aug 15, 2025
8f468a9
Refactor NODE_USE_SYSTEM_CA implementation: extract platform-specific…
Aug 15, 2025
1765eec
Add --use-system-ca CLI flag matching Node.js
Aug 15, 2025
e107d0b
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 15, 2025
8ffc025
Try this
Jarred-Sumner Aug 15, 2025
6c99e1d
try
Jarred-Sumner Aug 15, 2025
f5047f6
Replace tests with Node.js-style verification tests
Jarred-Sumner Aug 15, 2025
0079384
Add Node.js system CA tests directly
Jarred-Sumner Aug 15, 2025
8850165
Add tests for --use-system-ca flag and NODE_USE_SYSTEM_CA
Jarred-Sumner Aug 15, 2025
e410d09
Implement tls.getCACertificates() API
Jarred-Sumner Aug 16, 2025
5ced742
Fix system certificate loading on macOS
Jarred-Sumner Aug 16, 2025
4430c67
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 16, 2025
35718bd
Fix
Jarred-Sumner Aug 16, 2025
74f5b7e
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 16, 2025
a646d46
Update BuildBun.cmake
Jarred-Sumner Aug 16, 2025
3251a3c
various cleanup
Jarred-Sumner Aug 16, 2025
56f543f
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 16, 2025
1e05f26
fix tests
cirospaciari Aug 30, 2025
a38bb38
Address all PR review comments for system CA implementation
Aug 30, 2025
1b4410b
Fix critical system CA handling issues
Aug 30, 2025
714bf04
Implement Arguments.zig enum-based CA store selection
Aug 30, 2025
64b8e0b
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cmake/sources/CxxSources.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
packages/bun-usockets/src/crypto/root_certs_darwin.cpp
packages/bun-usockets/src/crypto/root_certs_linux.cpp
packages/bun-usockets/src/crypto/root_certs_windows.cpp
packages/bun-usockets/src/crypto/root_certs.cpp
packages/bun-usockets/src/crypto/sni_tree.cpp
src/bake/BakeGlobalObject.cpp
Expand Down
2 changes: 2 additions & 0 deletions cmake/targets/BuildBun.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,7 @@ if(WIN32)
/delayload:WSOCK32.dll
/delayload:ADVAPI32.dll
/delayload:IPHLPAPI.dll
/delayload:CRYPT32.dll
)
endif()
endif()
Expand Down Expand Up @@ -1184,6 +1185,7 @@ if(WIN32)
ntdll
userenv
dbghelp
crypt32
wsock32 # ws2_32 required by TransmitFile aka sendfile on windows
delayimp.lib
)
Expand Down
129 changes: 122 additions & 7 deletions packages/bun-usockets/src/crypto/root_certs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,46 @@
#include <atomic>
#include <string.h>
#include "./default_ciphers.h"

// System-specific includes for certificate loading
#include "./root_certs_platform.h"
#ifdef _WIN32
#include <windows.h>
#include <wincrypt.h>
#else
// Linux/Unix includes
#include <dirent.h>
#include <stdio.h>
#include <limits.h>
#endif
static const int root_certs_size = sizeof(root_certs) / sizeof(root_certs[0]);

extern "C" void BUN__warn__extra_ca_load_failed(const char* filename, const char* error_msg);

// Forward declarations for platform-specific functions
// (Actual implementations are in platform-specific files)

// External variable from Zig CLI arguments
extern "C" bool Bun__Node__UseSystemCA;

// Helper function to check if system CA should be used
// Checks both CLI flag (--use-system-ca) and environment variable (NODE_USE_SYSTEM_CA=1)
static bool us_should_use_system_ca() {
// Check CLI flag first
if (Bun__Node__UseSystemCA) {
return true;
}

// Check environment variable
const char *use_system_ca = getenv("NODE_USE_SYSTEM_CA");
return use_system_ca && strcmp(use_system_ca, "1") == 0;
}

// Platform-specific system certificate loading implementations are separated:
// - macOS: root_certs_darwin.cpp (Security framework with dynamic loading)
// - Windows: root_certs_windows.cpp (Windows CryptoAPI)
// - Linux/Unix: us_load_system_certificates_linux() below

// This callback is used to avoid the default passphrase callback in OpenSSL
// which will typically prompt for the passphrase. The prompting is designed
// for the OpenSSL CLI, but works poorly for this case because it involves
Expand Down Expand Up @@ -101,7 +137,8 @@ static STACK_OF(X509) *us_ssl_ctx_load_all_certs_from_file(const char *filename)

static void us_internal_init_root_certs(
X509 *root_cert_instances[root_certs_size],
STACK_OF(X509) *&root_extra_cert_instances) {
STACK_OF(X509) *&root_extra_cert_instances,
STACK_OF(X509) *&root_system_cert_instances) {
static std::atomic_flag root_cert_instances_lock = ATOMIC_FLAG_INIT;
static std::atomic_bool root_cert_instances_initialized = 0;

Expand All @@ -123,6 +160,17 @@ static void us_internal_init_root_certs(
if (extra_certs && extra_certs[0]) {
root_extra_cert_instances = us_ssl_ctx_load_all_certs_from_file(extra_certs);
}

// load system certificates if NODE_USE_SYSTEM_CA=1
if (us_should_use_system_ca()) {
#ifdef __APPLE__
us_load_system_certificates_macos(&root_system_cert_instances);
#elif defined(_WIN32)
us_load_system_certificates_windows(&root_system_cert_instances);
#else
us_load_system_certificates_linux(&root_system_cert_instances);
#endif
}
}

atomic_flag_clear_explicit(&root_cert_instances_lock,
Expand All @@ -137,12 +185,15 @@ extern "C" int us_internal_raw_root_certs(struct us_cert_string_t **out) {
struct us_default_ca_certificates {
X509 *root_cert_instances[root_certs_size];
STACK_OF(X509) *root_extra_cert_instances;
STACK_OF(X509) *root_system_cert_instances;
};

us_default_ca_certificates* us_get_default_ca_certificates() {
static us_default_ca_certificates default_ca_certificates = {{NULL}, NULL};
static us_default_ca_certificates default_ca_certificates = {{NULL}, NULL, NULL};

us_internal_init_root_certs(default_ca_certificates.root_cert_instances, default_ca_certificates.root_extra_cert_instances);
us_internal_init_root_certs(default_ca_certificates.root_cert_instances,
default_ca_certificates.root_extra_cert_instances,
default_ca_certificates.root_system_cert_instances);

return &default_ca_certificates;
}
Expand All @@ -151,20 +202,33 @@ STACK_OF(X509) *us_get_root_extra_cert_instances() {
return us_get_default_ca_certificates()->root_extra_cert_instances;
}

STACK_OF(X509) *us_get_root_system_cert_instances() {
if (!us_should_use_system_ca())
return NULL;
// Ensure single-path initialization via us_internal_init_root_certs
auto certs = us_get_default_ca_certificates();
return certs->root_system_cert_instances;
}

extern "C" X509_STORE *us_get_default_ca_store() {
X509_STORE *store = X509_STORE_new();
if (store == NULL) {
return NULL;
}

if (!X509_STORE_set_default_paths(store)) {
X509_STORE_free(store);
return NULL;
// Only load system default paths when NODE_USE_SYSTEM_CA=1
// Otherwise, rely on bundled certificates only (like Node.js behavior)
if (us_should_use_system_ca()) {
if (!X509_STORE_set_default_paths(store)) {
X509_STORE_free(store);
return NULL;
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

us_default_ca_certificates *default_ca_certificates = us_get_default_ca_certificates();
X509** root_cert_instances = default_ca_certificates->root_cert_instances;
STACK_OF(X509) *root_extra_cert_instances = default_ca_certificates->root_extra_cert_instances;
STACK_OF(X509) *root_system_cert_instances = default_ca_certificates->root_system_cert_instances;

// load all root_cert_instances on the default ca store
for (size_t i = 0; i < root_certs_size; i++) {
Expand All @@ -183,8 +247,59 @@ extern "C" X509_STORE *us_get_default_ca_store() {
}
}

if (us_should_use_system_ca() && root_system_cert_instances) {
for (int i = 0; i < sk_X509_num(root_system_cert_instances); i++) {
X509 *cert = sk_X509_value(root_system_cert_instances, i);
X509_up_ref(cert);
X509_STORE_add_cert(store, cert);
}
}

return store;
}
extern "C" const char *us_get_default_ciphers() {
return DEFAULT_CIPHER_LIST;
}
}

// Platform-specific implementations for loading system certificates

#if defined(_WIN32)
// Windows implementation is split to avoid header conflicts:
// - root_certs_windows.cpp loads raw certificate data (uses Windows headers)
// - This file converts raw data to X509* (uses OpenSSL headers)

#include <vector>

struct RawCertificate {
std::vector<unsigned char> data;
};

// Defined in root_certs_windows.cpp - loads raw certificate data
extern void us_load_system_certificates_windows_raw(
std::vector<RawCertificate>& raw_certs);

// Convert raw Windows certificates to OpenSSL X509 format
void us_load_system_certificates_windows(STACK_OF(X509) **system_certs) {
*system_certs = sk_X509_new_null();
if (*system_certs == NULL) {
return;
}

// Load raw certificates from Windows stores
std::vector<RawCertificate> raw_certs;
us_load_system_certificates_windows_raw(raw_certs);

// Convert each raw certificate to X509
for (const auto& raw_cert : raw_certs) {
const unsigned char* data = raw_cert.data.data();
X509* x509_cert = d2i_X509(NULL, &data, raw_cert.data.size());
if (x509_cert != NULL) {
sk_X509_push(*system_certs, x509_cert);
}
}
}

#else
// Linux and other Unix-like systems - implementation is in root_certs_linux.cpp
extern "C" void us_load_system_certificates_linux(STACK_OF(X509) **system_certs);
#endif
Loading