diff options
author | 2023-10-22 15:11:51 -0700 | |
---|---|---|
committer | 2023-10-23 13:11:11 -0700 | |
commit | 1328d2324f6d3b2a5bd856c078df997c8ff4829d (patch) | |
tree | 595562262f06b5fe40d2ad7c4b9df00e623ab132 | |
parent | f7f2e978a153f681bdc1f1b3939aa6e76498a9d7 (diff) | |
download | bun-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.ts | 1 | ||||
-rw-r--r-- | packages/bun-usockets/src/crypto/openssl.c | 53 | ||||
-rw-r--r-- | packages/bun-usockets/src/libusockets.h | 7 | ||||
-rw-r--r-- | packages/bun-uws/src/App.h | 6 | ||||
-rw-r--r-- | src/bun.js/webcore/response.zig | 11 | ||||
-rw-r--r-- | src/cli/create_command.zig | 4 | ||||
-rw-r--r-- | src/cli/upgrade_command.zig | 2 | ||||
-rw-r--r-- | src/deps/uws.zig | 1 | ||||
-rw-r--r-- | src/env_loader.zig | 7 | ||||
-rw-r--r-- | src/http_client_async.zig | 54 | ||||
-rw-r--r-- | src/install/install.zig | 6 |
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(); } |