From c2002f6e06bb8dadc19123898cb531418ebdadc1 Mon Sep 17 00:00:00 2001 From: yhirose Date: Sun, 22 Feb 2026 19:18:40 -0500 Subject: [PATCH] Make loading system certificates from the Keychain on macOS an opt-out feature (#2377) --- CMakeLists.txt | 15 +++++++------ README.md | 2 +- httplib.h | 54 +++++++++++++++++++++++++++++------------------ meson.build | 10 +++++---- meson_options.txt | 3 ++- test/Makefile | 6 +++--- 6 files changed, 54 insertions(+), 36 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 45a0e16..96ede13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ * HTTPLIB_REQUIRE_ZLIB (default off) * HTTPLIB_REQUIRE_BROTLI (default off) * HTTPLIB_REQUIRE_ZSTD (default off) - * HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN (default on) + * HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES (default off) * HTTPLIB_USE_NON_BLOCKING_GETADDRINFO (default on) * HTTPLIB_COMPILE (default off) * HTTPLIB_INSTALL (default on) @@ -51,7 +51,7 @@ * HTTPLIB_IS_USING_ZLIB - a bool for if ZLIB support is enabled. * HTTPLIB_IS_USING_BROTLI - a bool for if Brotli support is enabled. * HTTPLIB_IS_USING_ZSTD - a bool for if ZSTD support is enabled. - * HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN - a bool for if support of loading system certs from the Apple Keychain is enabled. + * HTTPLIB_IS_USING_MACOSX_AUTOMATIC_ROOT_CERTIFICATES - a bool for if support of loading system certs from the Apple Keychain is enabled. * HTTPLIB_IS_USING_NON_BLOCKING_GETADDRINFO - a bool for if nonblocking getaddrinfo is enabled. * HTTPLIB_IS_COMPILED - a bool for if the library is compiled, or otherwise header-only. * HTTPLIB_INCLUDE_DIR - the root path to httplib's header (e.g. /usr/include). @@ -107,7 +107,7 @@ option(HTTPLIB_INSTALL "Enables the installation target" ON) option(HTTPLIB_TEST "Enables testing and builds tests" OFF) option(HTTPLIB_REQUIRE_BROTLI "Requires Brotli to be found & linked, or fails build." OFF) option(HTTPLIB_USE_BROTLI_IF_AVAILABLE "Uses Brotli (if available) to enable Brotli decompression support." ON) -option(HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN "Enable feature to load system certs from the Apple Keychain." ON) +option(HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES "Disable loading system certs from the Apple Keychain on macOS." OFF) option(HTTPLIB_USE_NON_BLOCKING_GETADDRINFO "Enables the non-blocking alternatives for getaddrinfo." ON) option(HTTPLIB_REQUIRE_ZSTD "Requires ZSTD to be found & linked, or fails build." OFF) option(HTTPLIB_USE_ZSTD_IF_AVAILABLE "Uses ZSTD (if available) to enable zstd support." ON) @@ -152,7 +152,10 @@ endif() # Set some variables that are used in-tree and while building based on our options set(HTTPLIB_IS_COMPILED ${HTTPLIB_COMPILE}) -set(HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN ${HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN}) +set(HTTPLIB_IS_USING_MACOSX_AUTOMATIC_ROOT_CERTIFICATES TRUE) +if(HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES) + set(HTTPLIB_IS_USING_MACOSX_AUTOMATIC_ROOT_CERTIFICATES FALSE) +endif() set(HTTPLIB_IS_USING_NON_BLOCKING_GETADDRINFO ${HTTPLIB_USE_NON_BLOCKING_GETADDRINFO}) # Threads needed for on some systems, and for on Linux @@ -316,7 +319,7 @@ target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC} $<$:ws2_32> $<$:crypt32> # Needed for API from MacOS Security framework - "$<$,$,$>:-framework CFNetwork -framework CoreFoundation -framework Security>" + "$<$,$,$>:-framework CFNetwork -framework CoreFoundation -framework Security>" # Needed for non-blocking getaddrinfo on MacOS "$<$,$>:-framework CFNetwork -framework CoreFoundation>" # Can't put multiple targets in a single generator expression or it bugs out. @@ -336,7 +339,7 @@ target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC} $<$:CPPHTTPLIB_ZLIB_SUPPORT> $<$:CPPHTTPLIB_ZSTD_SUPPORT> $<$:CPPHTTPLIB_OPENSSL_SUPPORT> - $<$,$,$>:CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN> + $<$,$>:CPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES> $<$:CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO> ) diff --git a/README.md b/README.md index a60ee22..565b052 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ cpp-httplib supports multiple TLS backends through an abstraction layer: > **Mbed TLS / wolfSSL limitation:** `get_ca_certs()` and `get_ca_names()` only reflect CA certificates loaded via `load_ca_cert_store()` or `load_ca_cert_store(pem, size)`. Certificates loaded through `set_ca_cert_path()` or system certificates (`load_system_certs`) are not enumerable with these backends. > [!TIP] -> For macOS: cpp-httplib can use system certs with `CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN`. `CoreFoundation` and `Security` should be linked with `-framework`. +> For macOS: cpp-httplib automatically loads system certs from the Keychain when a TLS backend is enabled. `CoreFoundation` and `Security` must be linked with `-framework`. To disable this, define `CPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES`. ```c++ // Use either OpenSSL, Mbed TLS, or wolfSSL diff --git a/httplib.h b/httplib.h index a4634d3..9286a17 100644 --- a/httplib.h +++ b/httplib.h @@ -357,14 +357,32 @@ using socket_t = int; #include #endif +// On macOS with a TLS backend, enable Keychain root certificates by default +// unless the user explicitly opts out. +#if defined(__APPLE__) && \ + !defined(CPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES) && \ + (defined(CPPHTTPLIB_OPENSSL_SUPPORT) || \ + defined(CPPHTTPLIB_MBEDTLS_SUPPORT) || \ + defined(CPPHTTPLIB_WOLFSSL_SUPPORT)) +#ifndef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN +#define CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN +#endif +#endif + +// On Windows, enable Schannel certificate verification by default +// unless the user explicitly opts out. +#if defined(_WIN32) && \ + !defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE) +#define CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE +#endif + #if defined(CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO) || \ defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) #if TARGET_OS_MAC #include #include #endif -#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO or - // CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN +#endif #ifdef CPPHTTPLIB_OPENSSL_SUPPORT #ifdef _WIN32 @@ -382,11 +400,11 @@ using socket_t = int; #endif #endif // _WIN32 -#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) +#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN #if TARGET_OS_MAC #include #endif -#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO +#endif #include #include @@ -430,11 +448,11 @@ using socket_t = int; #pragma comment(lib, "crypt32.lib") #endif #endif // _WIN32 -#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) +#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN #if TARGET_OS_MAC #include #endif -#endif // CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN +#endif // Mbed TLS 3.x API compatibility #if MBEDTLS_VERSION_MAJOR >= 3 @@ -473,11 +491,11 @@ using socket_t = int; #pragma comment(lib, "crypt32.lib") #endif #endif // _WIN32 -#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) +#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN #if TARGET_OS_MAC #include #endif -#endif // CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN +#endif #endif // CPPHTTPLIB_WOLFSSL_SUPPORT // Define CPPHTTPLIB_SSL_ENABLED if any SSL backend is available @@ -2561,8 +2579,7 @@ public: tls::ctx_t tls_context() const; -#if defined(_WIN32) && \ - !defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE) +#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE void enable_windows_certificate_verification(bool enabled); #endif @@ -2683,8 +2700,7 @@ public: tls::ctx_t tls_context() const { return ctx_; } -#if defined(_WIN32) && \ - !defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE) +#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE void enable_windows_certificate_verification(bool enabled); #endif @@ -2716,8 +2732,7 @@ private: std::function session_verifier_; -#if defined(_WIN32) && \ - !defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE) +#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE bool enable_windows_cert_verification_ = true; #endif @@ -15423,8 +15438,7 @@ inline void SSLClient::set_session_verifier( session_verifier_ = std::move(verifier); } -#if defined(_WIN32) && \ - !defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE) +#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE inline void SSLClient::enable_windows_certificate_verification(bool enabled) { enable_windows_cert_verification_ = enabled; } @@ -15582,8 +15596,7 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) { } } -#if defined(_WIN32) && \ - !defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE) +#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE // Additional Windows Schannel verification. // This provides real-time certificate validation with Windows Update // integration, working with both OpenSSL and MbedTLS backends. @@ -15629,8 +15642,7 @@ inline void Client::enable_server_hostname_verification(bool enabled) { cli_->enable_server_hostname_verification(enabled); } -#if defined(_WIN32) && \ - !defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE) +#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE inline void Client::enable_windows_certificate_verification(bool enabled) { if (is_ssl_) { static_cast(*cli_).enable_windows_certificate_verification( @@ -15753,7 +15765,7 @@ inline bool enumerate_windows_system_certs(Callback cb) { } #endif -#if defined(__APPLE__) && defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) +#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN // Enumerate macOS Keychain certificates and call callback with DER data template inline bool enumerate_macos_keychain_certs(Callback cb) { diff --git a/meson.build b/meson.build index 0a11c6b..319120d 100644 --- a/meson.build +++ b/meson.build @@ -62,11 +62,13 @@ if get_option('tls').allowed() endif if tls_found and host_machine.system() == 'darwin' - macosx_keychain_dep = dependency('appleframeworks', modules: ['CFNetwork', 'CoreFoundation', 'Security'], required: get_option('macosx_keychain')) - if macosx_keychain_dep.found() + disable_macosx_certs = get_option('disable_macosx_automatic_root_certificates') + if disable_macosx_certs + args += '-DCPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES' + else + macosx_keychain_dep = dependency('appleframeworks', modules: ['CFNetwork', 'CoreFoundation', 'Security'], required: true) deps += macosx_keychain_dep - args += '-DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN' - endif + endif endif endif diff --git a/meson_options.txt b/meson_options.txt index 05c4e19..2dd9f14 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -7,7 +7,7 @@ option('tls_backend', type: 'combo', choices: ['openssl', 'mbedtls'], value: 'op option('zlib', type: 'feature', value: 'auto', description: 'Enable zlib support') option('brotli', type: 'feature', value: 'auto', description: 'Enable Brotli support') option('zstd', type: 'feature', value: 'auto', description: 'Enable zstd support') -option('macosx_keychain', type: 'feature', value: 'auto', description: 'Enable loading certs from the Keychain on Apple devices') +option('disable_macosx_automatic_root_certificates', type: 'boolean', value: false, description: 'Disable loading system certs from the Apple Keychain on macOS') option('non_blocking_getaddrinfo', type: 'feature', value: 'auto', description: 'Enable asynchronous name lookup') option('compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file (requires python3)') option('test', type: 'boolean', value: false, description: 'Build tests') @@ -17,6 +17,7 @@ option('openssl', type: 'feature', deprecated: 'tls') option('cpp-httplib_openssl', type: 'feature', deprecated: 'openssl') option('cpp-httplib_zlib', type: 'feature', deprecated: 'zlib') option('cpp-httplib_brotli', type: 'feature', deprecated: 'brotli') +option('macosx_keychain', type: 'feature', deprecated: true, description: 'Deprecated: use disable_macosx_automatic_root_certificates instead') option('cpp-httplib_macosx_keychain', type: 'feature', deprecated: 'macosx_keychain') option('cpp-httplib_non_blocking_getaddrinfo', type: 'feature', deprecated: 'non_blocking_getaddrinfo') option('cpp-httplib_compile', type: 'boolean', value: false, deprecated: 'compile') diff --git a/test/Makefile b/test/Makefile index 4060466..62e5756 100644 --- a/test/Makefile +++ b/test/Makefile @@ -7,13 +7,13 @@ ifneq ($(OS), Windows_NT) PREFIX ?= $(shell brew --prefix) OPENSSL_DIR = $(PREFIX)/opt/openssl@3 OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto - OPENSSL_SUPPORT += -DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN -framework Security + OPENSSL_SUPPORT += -framework CoreFoundation -framework Security MBEDTLS_DIR ?= $(shell brew --prefix mbedtls@3) MBEDTLS_SUPPORT = -DCPPHTTPLIB_MBEDTLS_SUPPORT -I$(MBEDTLS_DIR)/include -L$(MBEDTLS_DIR)/lib -lmbedtls -lmbedx509 -lmbedcrypto - MBEDTLS_SUPPORT += -DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN -framework Security + MBEDTLS_SUPPORT += -framework CoreFoundation -framework Security WOLFSSL_DIR ?= $(shell brew --prefix wolfssl) WOLFSSL_SUPPORT = -DCPPHTTPLIB_WOLFSSL_SUPPORT -I$(WOLFSSL_DIR)/include -I$(WOLFSSL_DIR)/include/wolfssl -L$(WOLFSSL_DIR)/lib -lwolfssl - WOLFSSL_SUPPORT += -DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN -framework Security + WOLFSSL_SUPPORT += -framework CoreFoundation -framework Security else OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -lssl -lcrypto MBEDTLS_SUPPORT = -DCPPHTTPLIB_MBEDTLS_SUPPORT -lmbedtls -lmbedx509 -lmbedcrypto