aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Anshul Gupta <ansg191@anshulg.com> 2023-10-18 00:45:41 -0700
committerGravatar Anshul Gupta <ansg191@anshulg.com> 2023-10-18 17:09:59 -0700
commitc221b45d4a3f003f5f62405bcf2d81880d6173cb (patch)
tree0b64b932d8a0d631ea14836011eab84c0633fa9b
parent0173571b19780ab71772b1efcb9c4e623cab0aca (diff)
downloadbun-c221b45d4a3f003f5f62405bcf2d81880d6173cb.tar.gz
bun-c221b45d4a3f003f5f62405bcf2d81880d6173cb.tar.zst
bun-c221b45d4a3f003f5f62405bcf2d81880d6173cb.zip
Adds macOS Keychain certs to default CA store
-rw-r--r--Makefile3
-rw-r--r--packages/bun-usockets/src/crypto/openssl.c174
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 <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;
}