// calogTrust.c -- trust anchor loading for TLS clients (see calogTrust.h). Moved out of // calogHttp.c on 2026-09-11 so the tcp transport's client-side TLS shares one implementation with // the http client rather than carrying a second copy of it. #include "calogTrust.h" #include #include #include #include // Native trust-store access is platform specific. Windows reads the system certificate stores via // wincrypt.h, whose macros collide with OpenSSL's X.509 API, so they are undefined right after // inclusion. macOS (only when built against the Apple SDK) reads the keychain via the Security // framework. Every POSIX target also probes the well-known CA-bundle files, which needs unistd.h. #if defined(_WIN32) #include #include #undef X509_NAME #undef X509_EXTENSIONS #undef PKCS7_ISSUER_AND_SERIAL #undef PKCS7_SIGNER_INFO #undef OCSP_REQUEST #undef OCSP_RESPONSE #else #include #if defined(__APPLE__) && defined(CALOG_MAC_KEYCHAIN_TRUST) #include #include #endif #endif #ifndef _WIN32 static bool trustLoadProbedPaths(SSL_CTX *ctx); #endif #if defined(__APPLE__) && defined(CALOG_MAC_KEYCHAIN_TRUST) static bool trustLoadMacRoots(SSL_CTX *ctx); #endif #if defined(_WIN32) static bool trustLoadWindowsRoots(SSL_CTX *ctx); #endif #if defined(__APPLE__) && defined(CALOG_MAC_KEYCHAIN_TRUST) // Load trust anchors from the macOS system keychain via the Security framework. This needs the // Apple SDK, so calog's SDK-less cross build leaves it disabled (falling back to // trustLoadProbedPaths); it is compiled only for a native macOS build made with // -DCALOG_MAC_KEYCHAIN_TRUST that links -framework Security -framework CoreFoundation. Each anchor // is DER, so it round-trips through OpenSSL's d2i_X509 into ctx's store. Returns true once any // anchor was added. static bool trustLoadMacRoots(SSL_CTX *ctx) { X509_STORE *store; CFArrayRef anchors; CFIndex count; CFIndex i; bool loaded; store = SSL_CTX_get_cert_store(ctx); anchors = NULL; loaded = false; if (SecTrustCopyAnchorCertificates(&anchors) != errSecSuccess || anchors == NULL) { return false; } count = CFArrayGetCount(anchors); for (i = 0; i < count; i++) { SecCertificateRef cert; CFDataRef der; const unsigned char *bytes; X509 *x; cert = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, i); if (cert == NULL) { continue; } der = SecCertificateCopyData(cert); if (der == NULL) { continue; } bytes = CFDataGetBytePtr(der); x = d2i_X509(NULL, &bytes, (long)CFDataGetLength(der)); CFRelease(der); if (x == NULL) { continue; } if (X509_STORE_add_cert(store, x) == 1) { loaded = true; } X509_free(x); } CFRelease(anchors); ERR_clear_error(); return loaded; } #endif #ifndef _WIN32 // Load trust anchors from the CA-bundle files (and hash directories) that Linux distributions and // the BSDs/macOS ship. The well-known locations are tried in order -- one bundle is enough (a // system generally has exactly one), but a hash directory is also tried for distros that ship only // that. access() gates each try so a missing path does not push errors onto OpenSSL's error queue. // Returns true once any anchors loaded. (CALOG_CA_BUNDLE / SSL_CERT_* overrides are handled, ahead // of this probe, by calogTrustLoad.) static bool trustLoadProbedPaths(SSL_CTX *ctx) { static const char *const files[] = { "/etc/ssl/certs/ca-certificates.crt", // Debian, Ubuntu, Arch, Gentoo, Alpine "/etc/pki/tls/certs/ca-bundle.crt", // Fedora, RHEL, CentOS "/etc/ssl/ca-bundle.pem", // openSUSE "/etc/pki/tls/cacert.pem", // OpenELEC "/etc/ssl/cert.pem", // Alpine, macOS, OpenBSD, FreeBSD "/usr/local/share/certs/ca-root-nss.crt", // FreeBSD (ports) "/etc/openssl/certs/ca-certificates.crt" // NetBSD }; static const char *const dirs[] = { "/etc/ssl/certs", // Debian, openSUSE (hashed) "/etc/pki/tls/certs", // Fedora, RHEL "/system/etc/security/cacerts" // Android }; size_t i; bool loaded; loaded = false; for (i = 0; i < sizeof(files) / sizeof(files[0]); i++) { if (access(files[i], R_OK) != 0) { continue; } if (SSL_CTX_load_verify_locations(ctx, files[i], NULL) == 1) { loaded = true; break; } } if (!loaded) { for (i = 0; i < sizeof(dirs) / sizeof(dirs[0]); i++) { if (access(dirs[i], R_OK) != 0) { continue; } if (SSL_CTX_load_verify_locations(ctx, NULL, dirs[i]) == 1) { loaded = true; break; } } } return loaded; } #endif // Populate ctx's certificate trust store from the host's native trust configuration. OpenSSL's // compiled-in default paths point at the vendored build prefix, which does not exist at runtime, so // they resolve nothing on their own; instead we consult the operating system directly (the Windows // system stores, the macOS keychain, or the distro CA-bundle files). Overrides are honored ahead of // the native store, in priority order: CALOG_CA_BUNDLE (authoritative and EXCLUSIVE -- trust only // that bundle, so it can pin to a private CA, and fail closed if it cannot be loaded), then the // OpenSSL-standard SSL_CERT_FILE / SSL_CERT_DIR. Returns true only if a trust source actually // loaded (not merely because an override variable is set), so a verified request can fail with a // clear "no trust store" error instead of a misleading per-certificate verification failure. bool calogTrustLoad(SSL_CTX *ctx) { const char *pin; const char *envFile; const char *envDir; bool loaded; // A pinned bundle is authoritative and exclusive: trust EXACTLY it and nothing else, and fail // closed if it will not load. Load return value (not mere presence of the variable) decides. pin = getenv("CALOG_CA_BUNDLE"); if (pin != NULL) { return SSL_CTX_load_verify_locations(ctx, pin, NULL) == 1; } loaded = false; // The OpenSSL-standard operator override. Load each explicitly and confirm it, so a broken // SSL_CERT_FILE still surfaces the clear no-trust-store error rather than counting as loaded. // (An SSL_CERT_DIR is a lazy hash-dir lookup, so its load cannot be pre-confirmed; a set-but- // empty directory is the one case that still counts as loaded, which is fail-closed-safe.) envFile = getenv("SSL_CERT_FILE"); if (envFile != NULL && SSL_CTX_load_verify_locations(ctx, envFile, NULL) == 1) { loaded = true; } envDir = getenv("SSL_CERT_DIR"); if (envDir != NULL && SSL_CTX_load_verify_locations(ctx, NULL, envDir) == 1) { loaded = true; } #if defined(_WIN32) if (trustLoadWindowsRoots(ctx)) { loaded = true; } #else #if defined(__APPLE__) && defined(CALOG_MAC_KEYCHAIN_TRUST) if (trustLoadMacRoots(ctx)) { loaded = true; } #endif if (trustLoadProbedPaths(ctx)) { loaded = true; } #endif return loaded; } #if defined(_WIN32) // Load trust anchors from the Windows system certificate stores (ROOT = trusted roots, CA = // intermediates). Each store certificate is DER, so it round-trips through OpenSSL's d2i_X509 into // ctx's store. Returns true once any certificate was added. static bool trustLoadWindowsRoots(SSL_CTX *ctx) { static const char *const stores[] = { "ROOT", "CA" }; X509_STORE *store; size_t i; bool loaded; store = SSL_CTX_get_cert_store(ctx); loaded = false; for (i = 0; i < sizeof(stores) / sizeof(stores[0]); i++) { HCERTSTORE sys; PCCERT_CONTEXT cert; sys = CertOpenSystemStoreA(0, stores[i]); if (sys == NULL) { continue; } cert = NULL; while ((cert = CertEnumCertificatesInStore(sys, cert)) != NULL) { const unsigned char *der; X509 *x; der = cert->pbCertEncoded; x = d2i_X509(NULL, &der, (long)cert->cbCertEncoded); if (x == NULL) { continue; } // X509_STORE_add_cert up-refs on success, so our reference is always released here; a // duplicate (already present) returns 0 and is simply skipped. if (X509_STORE_add_cert(store, x) == 1) { loaded = true; } X509_free(x); } CertCloseStore(sys, 0); } // Duplicate-add attempts push errors onto OpenSSL's thread-local queue; clear them so a later // SSL_get_error is not misled by this bookkeeping. ERR_clear_error(); return loaded; } #endif