diff options
author | 2023-10-18 00:45:41 -0700 | |
---|---|---|
committer | 2023-10-18 17:09:59 -0700 | |
commit | c221b45d4a3f003f5f62405bcf2d81880d6173cb (patch) | |
tree | 0b64b932d8a0d631ea14836011eab84c0633fa9b | |
parent | 0173571b19780ab71772b1efcb9c4e623cab0aca (diff) | |
download | bun-c221b45d4a3f003f5f62405bcf2d81880d6173cb.tar.gz bun-c221b45d4a3f003f5f62405bcf2d81880d6173cb.tar.zst bun-c221b45d4a3f003f5f62405bcf2d81880d6173cb.zip |
Adds macOS Keychain certs to default CA store
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | packages/bun-usockets/src/crypto/openssl.c | 174 |
2 files changed, 175 insertions, 2 deletions
@@ -424,7 +424,8 @@ ifeq ($(OS_NAME), darwin) SYMBOLS=-exported_symbols_list $(realpath src/symbols.txt) PLATFORM_LINKER_FLAGS += -DDU_DISABLE_RENAMING=1 \ -lstdc++ \ - -fno-keep-static-consts -lresolv + -fno-keep-static-consts -lresolv \ + -framework Security -framework CoreFoundation endif ifeq ($(OS_NAME),linux) diff --git a/packages/bun-usockets/src/crypto/openssl.c b/packages/bun-usockets/src/crypto/openssl.c index a03bf3520..3c2c85225 100644 --- a/packages/bun-usockets/src/crypto/openssl.c +++ b/packages/bun-usockets/src/crypto/openssl.c @@ -43,13 +43,28 @@ void *sni_find(void *sni, const char *hostname); #include <wolfssl/openssl/dh.h> #endif +#if defined(__APPLE__) && defined(__MACH__) + +#include <CoreFoundation/CoreFoundation.h> +#include <Security/Security.h> + +#endif + #include "./root_certs.h" #include <stdatomic.h> + +// Embedded root certs static const size_t root_certs_size = sizeof(root_certs) / sizeof(root_certs[0]); static X509* root_cert_instances[root_certs_size] = {NULL}; static atomic_flag root_cert_instances_lock = ATOMIC_FLAG_INIT; static atomic_bool root_cert_instances_initialized = 0; +// Native root certs +static size_t native_certs_size = 0; +static X509 **native_cert_instances = NULL; +static atomic_flag native_cert_instances_lock = ATOMIC_FLAG_INIT; +static atomic_bool native_cert_instances_initialized = 0; + struct loop_ssl_data { char *ssl_read_input, *ssl_read_output; unsigned int ssl_read_input_length; @@ -586,6 +601,154 @@ int us_no_password_callback(char* buf, int size, int rwflag, void* u) { return 0; } +#if defined(__APPLE__) && defined(__MACH__) + +// Places aggregate trust setting for a given certificate from a domain into result. +int us_internal_osx_cert_trust(SecTrustSettingsDomain domain, SecCertificateRef cert, SecTrustSettingsResult *result) { + // Trust settings: CFArray of CFDictionary objects + CFArrayRef trust_settings = NULL; + + if (SecTrustSettingsCopyTrustSettings(cert, domain, &trust_settings) == errSecItemNotFound) + return 0; + + // From Apple Docs: + // The trustSettings parameter can return a valid but empty CFArrayRef. This + // empty trust-settings array means “always trust this certificate” with an + // overall trust setting for the certificate of + // kSecTrustSettingsResultTrustRoot. However, an empty trust settings array + // isn’t the same as no trust settings, where the trustSettings parameter + // returns NULL. No trust-settings array means “this certificate must be + // verifiable using a known trusted certificate”. + if (trust_settings == NULL) + return 0; + + for (CFIndex i = 0; i < CFArrayGetCount(trust_settings); i++) { + CFStringRef policy_name; + int32_t trust_result; + + CFDictionaryRef setting = (CFDictionaryRef) CFArrayGetValueAtIndex(trust_settings, i); + + // Reject settings for non-ssl policies + if (!CFDictionaryGetValueIfPresent(setting, CFSTR("kSecTrustSettingsPolicyName"), + (const void **) &policy_name)) { + continue; + } + if (CFStringCompare(policy_name, CFSTR("sslServer"), 0) != kCFCompareEqualTo) + continue; + + // Get the trust result + CFNumberRef cf_trust_result = CFDictionaryGetValue(setting, kSecTrustSettingsResult); + if (cf_trust_result == NULL) + // See large comment above + trust_result = kSecTrustSettingsResultTrustRoot; + else if (!CFNumberGetValue(cf_trust_result, kCFNumberSInt32Type, &trust_result)) + trust_result = kSecTrustSettingsResultInvalid; + + switch (trust_result) { + case kSecTrustSettingsResultInvalid: + case kSecTrustSettingsResultUnspecified: + continue; + default: + *result = (SecTrustSettingsResult) trust_result; + goto end; + } + } + + // See large comment above + *result = kSecTrustSettingsResultTrustRoot; +end: + CFRelease(trust_settings); + return 1; +} + +// Appends the trusted/root certificates in a given domain to certs. +int us_internal_osx_trusted_certs(SecTrustSettingsDomain domain, CFMutableArrayRef certs) { + int ret = 1; + // CFArray of SecCertificateRef + CFArrayRef tmp; + + OSStatus result = SecTrustSettingsCopyCertificates(domain, &tmp); + switch (result) { + case errSecSuccess: + break; + case errSecNoTrustSettings: + // No trust settings means no trusted certificates. + // Do nothing & return success. + return 1; + default: + return 0; + } + + for (CFIndex i = 0; i < CFArrayGetCount(tmp); i++) { + SecTrustSettingsResult trust = kSecTrustSettingsResultUnspecified; + + SecCertificateRef cert = (SecCertificateRef) CFArrayGetValueAtIndex(tmp, i); + if (!us_internal_osx_cert_trust(domain, cert, &trust)) { + ret = 0; + goto end; + } + + if (trust == kSecTrustSettingsResultTrustRoot || trust == kSecTrustSettingsResultTrustAsRoot) + CFArrayAppendValue(certs, cert); + } + +end: + CFRelease(tmp); + return ret; +} + +// Initializes native_certs with the trusted certificates in the OSX keychain. +// Must be called with native_cert_instances_lock held. +void us_internal_osx_init_native_certs(void) { + // CFMutableArray of SecCertificateRef + CFMutableArrayRef certs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + + if (!us_internal_osx_trusted_certs(kSecTrustSettingsDomainUser, certs) || + !us_internal_osx_trusted_certs(kSecTrustSettingsDomainAdmin, certs) || + !us_internal_osx_trusted_certs(kSecTrustSettingsDomainSystem, certs)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB); + return; + } + + native_certs_size = (size_t) CFArrayGetCount(certs); + native_cert_instances = us_malloc(sizeof(X509 *) * native_certs_size); + + for (CFIndex i = 0; i < (CFIndex) native_certs_size; ++i) { + SecCertificateRef sec_cert = (SecCertificateRef) CFArrayGetValueAtIndex(certs, i); + + CFDataRef der = SecCertificateCopyData(sec_cert); + const unsigned char *buf = CFDataGetBytePtr(der); + + X509 *ssl_cert = d2i_X509(NULL, &buf, CFDataGetLength(der)); + + if (ssl_cert == NULL) + OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB); + + native_cert_instances[i] = ssl_cert; + + CFRelease(der); + } + + CFRelease(certs); +} + +#endif + +void us_internal_init_native_certs(void) { + if (atomic_load(&native_cert_instances_initialized) == 1) return; + + while (atomic_flag_test_and_set_explicit(&native_cert_instances_lock, memory_order_acquire)); + + if (atomic_load(&native_cert_instances_initialized) == 0) { +#if defined(__APPLE__) && defined(__MACH__) + us_internal_osx_init_native_certs(); +#endif + + atomic_store(&native_cert_instances_initialized, 1); + } + + atomic_flag_clear_explicit(&native_cert_instances_lock, memory_order_release); +} static X509 * us_ssl_ctx_get_X509_without_callback_from(struct us_cert_string_t content) { X509 *x = NULL; @@ -647,6 +810,7 @@ X509_STORE* us_get_default_ca_store() { } 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++) { @@ -655,7 +819,15 @@ X509_STORE* us_get_default_ca_store() { X509_up_ref(cert); X509_STORE_add_cert(store, cert); } - + + // 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); + } + return store; } |