aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Anshul Gupta <ansg191@anshulg.com> 2023-10-22 15:11:51 -0700
committerGravatar Anshul Gupta <ansg191@anshulg.com> 2023-10-23 13:11:11 -0700
commit1328d2324f6d3b2a5bd856c078df997c8ff4829d (patch)
tree595562262f06b5fe40d2ad7c4b9df00e623ab132
parentf7f2e978a153f681bdc1f1b3939aa6e76498a9d7 (diff)
downloadbun-ansg191/system-store.tar.gz
bun-ansg191/system-store.tar.zst
bun-ansg191/system-store.zip
Adds BUN_TLS_CA_STORE env var to select CA storeansg191/system-store
BUN_TLS_CA_STORE is a comma seperated list of CA store sources for bun to retrieve certificates from. The options are currently `mozilla` for embedded Mozilla certs & `system` for loading certs from the native platform store. Defaults to `mozilla`.
-rw-r--r--packages/bun-types/globals.d.ts1
-rw-r--r--packages/bun-usockets/src/crypto/openssl.c53
-rw-r--r--packages/bun-usockets/src/libusockets.h7
-rw-r--r--packages/bun-uws/src/App.h6
-rw-r--r--src/bun.js/webcore/response.zig11
-rw-r--r--src/cli/create_command.zig4
-rw-r--r--src/cli/upgrade_command.zig2
-rw-r--r--src/deps/uws.zig1
-rw-r--r--src/env_loader.zig7
-rw-r--r--src/http_client_async.zig54
-rw-r--r--src/install/install.zig6
11 files changed, 126 insertions, 26 deletions
diff --git a/packages/bun-types/globals.d.ts b/packages/bun-types/globals.d.ts
index 57dd4aafa..bfb290134 100644
--- a/packages/bun-types/globals.d.ts
+++ b/packages/bun-types/globals.d.ts
@@ -1344,6 +1344,7 @@ interface FetchRequestInit extends RequestInit {
tls?: {
rejectUnauthorized?: boolean | undefined; // Defaults to true
checkServerIdentity?: any | undefined; // TODO: change `any` to `checkServerIdentity`
+ caStore?: number | string | string[] | undefined;
};
}
diff --git a/packages/bun-usockets/src/crypto/openssl.c b/packages/bun-usockets/src/crypto/openssl.c
index 86ce18ae6..10f8464fc 100644
--- a/packages/bun-usockets/src/crypto/openssl.c
+++ b/packages/bun-usockets/src/crypto/openssl.c
@@ -937,34 +937,39 @@ void us_internal_init_root_certs() {
atomic_flag_clear_explicit(&root_cert_instances_lock, memory_order_release);
}
-X509_STORE* us_get_default_ca_store() {
+X509_STORE* us_get_default_ca_store(enum us_bun_socket_context_ca_store s) {
X509_STORE *store = X509_STORE_new();
if (store == NULL) {
return NULL;
}
-
- if (!X509_STORE_set_default_paths(store)) {
- X509_STORE_free(store);
- return NULL;
- }
-
- us_internal_init_root_certs();
- us_internal_init_native_certs();
- // load all root_cert_instances on the default ca store
- for (size_t i = 0; i < root_certs_size; i++) {
- X509* cert = root_cert_instances[i];
- if(cert == NULL) continue;
- X509_up_ref(cert);
- X509_STORE_add_cert(store, cert);
+ if (s & US_BUN_SOCKET_CONTEXT_CA_STORE_SYSTEM) {
+ us_internal_init_native_certs();
+
+ // load all native_certs on the default ca store
+ for (size_t i = 0; i < native_certs_size; i++) {
+ X509 *cert = native_cert_instances[i];
+ if (cert == NULL) continue;
+ X509_up_ref(cert);
+ X509_STORE_add_cert(store, cert);
+ }
+
+ if (!X509_STORE_set_default_paths(store)) {
+ X509_STORE_free(store);
+ return NULL;
+ }
}
- // load all native_certs on the default ca store
- for (size_t i = 0; i < native_certs_size; i++) {
- X509 *cert = native_cert_instances[i];
- if (cert == NULL) continue;
- X509_up_ref(cert);
- X509_STORE_add_cert(store, cert);
+ if (s & US_BUN_SOCKET_CONTEXT_CA_STORE_MOZILLA) {
+ us_internal_init_root_certs();
+
+ // load all root_cert_instances on the default ca store
+ for (size_t i = 0; i < root_certs_size; i++) {
+ X509 *cert = root_cert_instances[i];
+ if (cert == NULL) continue;
+ X509_up_ref(cert);
+ X509_STORE_add_cert(store, cert);
+ }
}
return store;
@@ -1361,7 +1366,7 @@ SSL_CTX *create_ssl_context_from_bun_options(struct us_bun_socket_context_option
}
if (options.ca_file_name) {
- SSL_CTX_set_cert_store(ssl_context, us_get_default_ca_store());
+ SSL_CTX_set_cert_store(ssl_context, us_get_default_ca_store(options.ca_store));
STACK_OF(X509_NAME) *ca_list;
ca_list = SSL_load_client_CA_file(options.ca_file_name);
@@ -1393,7 +1398,7 @@ SSL_CTX *create_ssl_context_from_bun_options(struct us_bun_socket_context_option
}
if (cert_store == NULL) {
- cert_store = us_get_default_ca_store();
+ cert_store = us_get_default_ca_store(options.ca_store);
SSL_CTX_set_cert_store(ssl_context, cert_store);
}
@@ -1410,7 +1415,7 @@ SSL_CTX *create_ssl_context_from_bun_options(struct us_bun_socket_context_option
}
} else {
if(options.request_cert) {
- SSL_CTX_set_cert_store(ssl_context, us_get_default_ca_store());
+ SSL_CTX_set_cert_store(ssl_context, us_get_default_ca_store(options.ca_store));
if(options.reject_unauthorized) {
SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, us_verify_callback);
diff --git a/packages/bun-usockets/src/libusockets.h b/packages/bun-usockets/src/libusockets.h
index cff9a1bd2..80f95bc03 100644
--- a/packages/bun-usockets/src/libusockets.h
+++ b/packages/bun-usockets/src/libusockets.h
@@ -172,6 +172,12 @@ struct us_bun_verify_error_t {
const char* reason;
};
+enum us_bun_socket_context_ca_store : unsigned int {
+ US_BUN_SOCKET_CONTEXT_CA_STORE_NONE = 0,
+ US_BUN_SOCKET_CONTEXT_CA_STORE_MOZILLA = 1 << 0,
+ US_BUN_SOCKET_CONTEXT_CA_STORE_SYSTEM = 1 << 1,
+};
+
struct us_bun_socket_context_options_t {
const char *key_file_name;
const char *cert_file_name;
@@ -180,6 +186,7 @@ struct us_bun_socket_context_options_t {
const char *ca_file_name;
const char *ssl_ciphers;
int ssl_prefer_low_memory_usage; /* Todo: rename to prefer_low_memory_usage and apply for TCP as well */
+ enum us_bun_socket_context_ca_store ca_store;
const char **key;
unsigned int key_count;
const char **cert;
diff --git a/packages/bun-uws/src/App.h b/packages/bun-uws/src/App.h
index 32f8afb9a..dbb54f10e 100644
--- a/packages/bun-uws/src/App.h
+++ b/packages/bun-uws/src/App.h
@@ -56,6 +56,11 @@ namespace uWS {
#include "PerMessageDeflate.h"
namespace uWS {
+ enum SocketContextCAStore : unsigned int {
+ None = 0,
+ Mozilla = 1 << 0,
+ System = 1 << 1,
+ };
/* This one matches us_socket_context_options_t but has default values */
struct SocketContextOptions {
@@ -66,6 +71,7 @@ namespace uWS {
const char *ca_file_name = nullptr;
const char *ssl_ciphers = nullptr;
int ssl_prefer_low_memory_usage = 0;
+ SocketContextCAStore ca_store = SocketContextCAStore::None;
const char **key = nullptr;
unsigned int key_count = 0;
diff --git a/src/bun.js/webcore/response.zig b/src/bun.js/webcore/response.zig
index 8ad5442ae..3eb436e68 100644
--- a/src/bun.js/webcore/response.zig
+++ b/src/bun.js/webcore/response.zig
@@ -1504,6 +1504,7 @@ pub const Fetch = struct {
fetch_tasklet.http.?.client.disable_keepalive = fetch_options.disable_keepalive;
fetch_tasklet.http.?.client.disable_decompression = fetch_options.disable_decompression;
fetch_tasklet.http.?.client.reject_unauthorized = fetch_options.reject_unauthorized;
+ fetch_tasklet.http.?.client.ca_store = fetch_options.ca_store;
fetch_tasklet.http.?.client.tls_props = fetch_options.ssl_config;
@@ -1556,6 +1557,7 @@ pub const Fetch = struct {
memory_reporter: *JSC.MemoryReportingAllocator,
check_server_identity: JSC.Strong = .{},
ssl_config: ?SSLConfig = null,
+ ca_store: bun.HTTP.HTTPCAStore = .{},
};
pub fn queue(
@@ -1715,6 +1717,7 @@ pub const Fetch = struct {
var url_proxy_buffer: []const u8 = undefined;
var is_file_url = false;
var reject_unauthorized = script_ctx.bundler.env.getTLSRejectUnauthorized();
+ var ca_store = script_ctx.bundler.env.getTLSCAStore();
var check_server_identity: JSValue = .zero;
// TODO: move this into a DRYer implementation
// The status quo is very repetitive and very bug prone
@@ -1880,6 +1883,9 @@ pub const Fetch = struct {
check_server_identity = checkServerIdentity;
}
}
+ if (tls.get(ctx, "caStore")) |caStore| {
+ ca_store = bun.HTTP.HTTPCAStore.fromJS(globalThis, caStore);
+ }
}
}
@@ -2089,6 +2095,10 @@ pub const Fetch = struct {
check_server_identity = checkServerIdentity;
}
}
+
+ if (tls.get(ctx, "caStore")) |caStore| {
+ ca_store = bun.HTTP.HTTPCAStore.fromJS(globalThis, caStore);
+ }
}
}
@@ -2361,6 +2371,7 @@ pub const Fetch = struct {
.hostname = hostname,
.memory_reporter = memory_reporter,
.check_server_identity = if (check_server_identity.isEmptyOrUndefinedOrNull()) .{} else JSC.Strong.create(check_server_identity, globalThis),
+ .ca_store = ca_store,
},
// Pass the Strong value instead of creating a new one, or else we
// will leak it
diff --git a/src/cli/create_command.zig b/src/cli/create_command.zig
index b11a85b1e..5de433e24 100644
--- a/src/cli/create_command.zig
+++ b/src/cli/create_command.zig
@@ -1881,6 +1881,7 @@ pub const Example = struct {
);
async_http.client.progress_node = progress;
async_http.client.reject_unauthorized = env_loader.getTLSRejectUnauthorized();
+ async_http.client.ca_store = env_loader.getTLSCAStore();
const response = try async_http.sendSync(true);
@@ -1959,6 +1960,7 @@ pub const Example = struct {
);
async_http.client.progress_node = progress;
async_http.client.reject_unauthorized = env_loader.getTLSRejectUnauthorized();
+ async_http.client.ca_store = env_loader.getTLSCAStore();
var response = try async_http.sendSync(true);
@@ -2049,6 +2051,7 @@ pub const Example = struct {
);
async_http.client.progress_node = progress;
async_http.client.reject_unauthorized = env_loader.getTLSRejectUnauthorized();
+ async_http.client.ca_store = env_loader.getTLSCAStore();
refresher.maybeRefresh();
@@ -2091,6 +2094,7 @@ pub const Example = struct {
HTTP.FetchRedirect.follow,
);
async_http.client.reject_unauthorized = env_loader.getTLSRejectUnauthorized();
+ async_http.client.ca_store = env_loader.getTLSCAStore();
if (Output.enable_ansi_colors) {
async_http.client.progress_node = progress_node;
diff --git a/src/cli/upgrade_command.zig b/src/cli/upgrade_command.zig
index d865d990d..78bab4500 100644
--- a/src/cli/upgrade_command.zig
+++ b/src/cli/upgrade_command.zig
@@ -244,6 +244,7 @@ pub const UpgradeCommand = struct {
HTTP.FetchRedirect.follow,
);
async_http.client.reject_unauthorized = env_loader.getTLSRejectUnauthorized();
+ async_http.client.ca_store = env_loader.getTLSCAStore();
if (!silent) async_http.client.progress_node = progress;
const response = try async_http.sendSync(true);
@@ -491,6 +492,7 @@ pub const UpgradeCommand = struct {
async_http.client.timeout = timeout;
async_http.client.progress_node = progress;
async_http.client.reject_unauthorized = env_loader.getTLSRejectUnauthorized();
+ async_http.client.ca_store = env_loader.getTLSCAStore();
const response = try async_http.sendSync(true);
diff --git a/src/deps/uws.zig b/src/deps/uws.zig
index 8ca0f260b..0c05e811d 100644
--- a/src/deps/uws.zig
+++ b/src/deps/uws.zig
@@ -1025,6 +1025,7 @@ pub const us_bun_socket_context_options_t = extern struct {
ca_file_name: [*c]const u8 = null,
ssl_ciphers: [*c]const u8 = null,
ssl_prefer_low_memory_usage: i32 = 0,
+ ca_store: u32 = 1,
key: [*c][*c]const u8 = null,
key_count: u32 = 0,
cert: [*c][*c]const u8 = null,
diff --git a/src/env_loader.zig b/src/env_loader.zig
index f0bb91148..431e445c7 100644
--- a/src/env_loader.zig
+++ b/src/env_loader.zig
@@ -101,6 +101,13 @@ pub const Loader = struct {
return true;
}
+ pub fn getTLSCAStore(this: *Loader) bun.HTTP.HTTPCAStore {
+ if (this.map.get("BUN_TLS_CA_STORE")) |ca| {
+ return bun.HTTP.HTTPCAStore.parse(ca);
+ }
+ return bun.HTTP.HTTPCAStore{ .mozilla = true };
+ }
+
pub fn getHttpProxy(this: *Loader, url: URL) ?URL {
// TODO: When Web Worker support is added, make sure to intern these strings
var http_proxy: ?URL = null;
diff --git a/src/http_client_async.zig b/src/http_client_async.zig
index 0a18297b6..8c6cd09ee 100644
--- a/src/http_client_async.zig
+++ b/src/http_client_async.zig
@@ -365,9 +365,14 @@ fn NewHTTPContext(comptime ssl: bool) type {
unreachable;
}
- var opts = client.tls_props.?.asUSockets();
+ var opts: uws.us_bun_socket_context_options_t = .{};
+ if (client.tls_props != null) {
+ opts = client.tls_props.?.asUSockets();
+ }
opts.request_cert = 1;
opts.reject_unauthorized = 0;
+ opts.ca_store = client.ca_store.raw();
+
var socket = uws.us_create_bun_socket_context(ssl_int, http_thread.loop, @sizeOf(usize), opts);
if (socket == null) {
return error.FailedToOpenSocket;
@@ -767,7 +772,7 @@ pub const HTTPThread = struct {
pub fn connect(this: *@This(), client: *HTTPClient, comptime is_ssl: bool) !NewHTTPContext(is_ssl).HTTPSocket {
if (comptime is_ssl) {
const needs_own_context = client.tls_props != null and client.tls_props.?.requires_custom_request_ctx;
- if (needs_own_context) {
+ if (needs_own_context or client.ca_store.raw() != 1) {
var custom_context = try bun.default_allocator.create(NewHTTPContext(is_ssl));
client.custom_context = custom_context;
try custom_context.initWithClientConfig(client);
@@ -1557,6 +1562,7 @@ signals: Signals = .{},
async_http_id: u32 = 0,
hostname: ?[]u8 = null,
reject_unauthorized: bool = true,
+ca_store: HTTPCAStore = .{},
custom_context: ?*NewHTTPContext(true) = null,
pub fn init(
@@ -1720,6 +1726,50 @@ pub const HTTPChannelContext = struct {
}
};
+pub const HTTPCAStore = packed struct(u32) {
+ mozilla: bool = true,
+ system: bool = false,
+
+ _padding: u30 = 0,
+
+ pub fn raw(this: HTTPCAStore) u32 {
+ return @bitCast(this);
+ }
+
+ pub fn parse(s: []const u8) HTTPCAStore {
+ var store = HTTPCAStore{ .mozilla = false, .system = false };
+
+ if (std.mem.eql(u8, s, "none")) return store;
+
+ var split = std.mem.split(u8, s, ",");
+ while (split.next()) |value| {
+ if (std.mem.eql(u8, value, "mozilla")) store.mozilla = true;
+ if (std.mem.eql(u8, value, "system")) store.system = true;
+ }
+
+ return store;
+ }
+
+ pub fn fromJS(global: *JSC.JSGlobalObject, obj: JSC.JSValue) HTTPCAStore {
+ if (obj.isNumber()) {
+ return @bitCast(obj.to(u32));
+ } else if (obj.jsTypeLoose().isArray()) {
+ var store = HTTPCAStore{ .mozilla = false, .system = false };
+ var iter = obj.arrayIterator(global);
+ while (iter.next()) |value| {
+ if (bun.String.tryFromJS(value, global)) |str| {
+ store = @bitCast(store.raw() | HTTPCAStore.parse(str.byteSlice()).raw());
+ }
+ }
+ return store;
+ } else if (bun.String.tryFromJS(obj, global)) |str| {
+ return HTTPCAStore.parse(str.byteSlice());
+ } else {
+ return .{};
+ }
+ }
+};
+
pub const AsyncHTTP = struct {
request: ?picohttp.Request = null,
response: ?picohttp.Response = null,
diff --git a/src/install/install.zig b/src/install/install.zig
index 17c5bc4ee..38c0de64e 100644
--- a/src/install/install.zig
+++ b/src/install/install.zig
@@ -372,6 +372,7 @@ const NetworkTask = struct {
null,
);
this.http.client.reject_unauthorized = this.package_manager.tlsRejectUnauthorized();
+ this.http.client.ca_store = this.package_manager.tlsCAStore();
if (PackageManager.verbose_install) {
this.http.client.verbose = true;
@@ -456,6 +457,7 @@ const NetworkTask = struct {
null,
);
this.http.client.reject_unauthorized = this.package_manager.tlsRejectUnauthorized();
+ this.http.client.ca_store = this.package_manager.tlsCAStore();
if (PackageManager.verbose_install) {
this.http.client.verbose = true;
}
@@ -1742,6 +1744,10 @@ pub const PackageManager = struct {
return this.env.getTLSRejectUnauthorized();
}
+ pub fn tlsCAStore(this: *PackageManager) HTTP.HTTPCAStore {
+ return this.env.getTLSCAStore();
+ }
+
pub fn computeIsContinuousIntegration(this: *PackageManager) bool {
return this.env.isCI();
}