From c221b45d4a3f003f5f62405bcf2d81880d6173cb Mon Sep 17 00:00:00 2001 From: Anshul Gupta Date: Wed, 18 Oct 2023 00:45:41 -0700 Subject: Adds macOS Keychain certs to default CA store --- Makefile | 3 +- packages/bun-usockets/src/crypto/openssl.c | 174 ++++++++++++++++++++++++++++- 2 files changed, 175 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 16fbbfb4f..fd7e93867 100644 --- a/Makefile +++ b/Makefile @@ -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 #endif +#if defined(__APPLE__) && defined(__MACH__) + +#include +#include + +#endif + #include "./root_certs.h" #include + +// 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; } -- cgit v1.2.3