mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-17 10:59:39 +00:00
Fix iOS build break and modernize macOS Keychain cert loading (#2455)
* Replace deprecated SecTrustCopyAnchorCertificates on macOS SecTrustCopyAnchorCertificates was deprecated in macOS 13. Switch to SecTrustSettingsCopyCertificates, iterating over the System, Admin, and User trust domains to retain equivalent coverage of anchor certificates. * Restrict Keychain cert loading to macOS TARGET_OS_MAC is true on all Apple platforms including iOS, tvOS, and watchOS, which caused the keychain enumeration path to be compiled on iOS where SecTrustSettingsCopyCertificates is unavailable. Narrow the auto-enable and the Security.h include guards to TARGET_OS_OSX, and emit an explicit #error when the user defines CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN on a non-macOS Apple platform, directing them to use set_ca_cert_path() with a bundled CA file. Addresses the iOS build break reported in #2454. * Add iOS header parse check to CI Run a cross-compile syntax check against the iOS SDK to catch accidental use of macOS-only APIs or guards (e.g. TARGET_OS_MAC vs TARGET_OS_OSX) that would silently break iOS builds. Also verify that defining CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN on iOS fires the expected #error. iOS is not officially supported as a runtime target; this job only guarantees the header stays parse-clean on iOS toolchains.
This commit is contained in:
1 parent
b1cc8095a8
commit
1ff0c8588d
2 files changed
+101
-26
No files matched your search
@@ -250,6 +250,54 @@ jobs:
|
|||||||
- name: build and run ThreadPool test
|
- name: build and run ThreadPool test
|
||||||
run: cd test && make test_thread_pool && ./test_thread_pool
|
run: cd test && make test_thread_pool && ./test_thread_pool
|
||||||
|
|
||||||
|
ios-parse-check:
|
||||||
|
runs-on: macos-latest
|
||||||
|
if: >
|
||||||
|
(github.event_name == 'push') ||
|
||||||
|
(github.event_name == 'pull_request' &&
|
||||||
|
github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name) ||
|
||||||
|
(github.event_name == 'workflow_dispatch' && github.event.inputs.test_macos == 'true')
|
||||||
|
name: ios header parse check (not officially supported)
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: install OpenSSL headers
|
||||||
|
run: brew install openssl@3
|
||||||
|
- name: verify header parses on iOS target
|
||||||
|
run: |
|
||||||
|
IOS_SDK=$(xcrun --sdk iphoneos --show-sdk-path)
|
||||||
|
OPENSSL_INC=$(brew --prefix openssl@3)/include
|
||||||
|
echo "Using iOS SDK: $IOS_SDK"
|
||||||
|
echo '#include "httplib.h"' | clang++ \
|
||||||
|
-isysroot "$IOS_SDK" \
|
||||||
|
-target arm64-apple-ios16.0 \
|
||||||
|
-std=c++11 \
|
||||||
|
-DCPPHTTPLIB_OPENSSL_SUPPORT \
|
||||||
|
-I"$OPENSSL_INC" \
|
||||||
|
-I. -Wall -Wextra \
|
||||||
|
-fsyntax-only -x c++ -
|
||||||
|
- name: verify CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN is rejected on iOS
|
||||||
|
run: |
|
||||||
|
IOS_SDK=$(xcrun --sdk iphoneos --show-sdk-path)
|
||||||
|
OPENSSL_INC=$(brew --prefix openssl@3)/include
|
||||||
|
out=$(echo '#include "httplib.h"' | clang++ \
|
||||||
|
-isysroot "$IOS_SDK" \
|
||||||
|
-target arm64-apple-ios16.0 \
|
||||||
|
-std=c++11 \
|
||||||
|
-DCPPHTTPLIB_OPENSSL_SUPPORT \
|
||||||
|
-DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN \
|
||||||
|
-I"$OPENSSL_INC" \
|
||||||
|
-I. \
|
||||||
|
-fsyntax-only -x c++ - 2>&1 || true)
|
||||||
|
if echo "$out" | grep -q "only supported on macOS"; then
|
||||||
|
echo "OK: #error fired as expected"
|
||||||
|
else
|
||||||
|
echo "FAIL: expected #error did not fire"
|
||||||
|
echo "--- compiler output ---"
|
||||||
|
echo "$out"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
windows:
|
windows:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
if: >
|
if: >
|
||||||
|
|||||||
@@ -339,16 +339,26 @@ using socket_t = int;
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
// On macOS with a TLS backend, enable Keychain root certificates by default
|
// On macOS with a TLS backend, enable Keychain root certificates by default
|
||||||
// unless the user explicitly opts out.
|
// unless the user explicitly opts out. Not enabled on iOS/tvOS/watchOS since
|
||||||
|
// the SecTrustSettings APIs used to enumerate anchor certificates are macOS
|
||||||
|
// only; on those platforms the user must provide a CA bundle explicitly.
|
||||||
#if defined(__APPLE__) && defined(__clang__) && \
|
#if defined(__APPLE__) && defined(__clang__) && \
|
||||||
!defined(CPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES) && \
|
!defined(CPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES) && \
|
||||||
(defined(CPPHTTPLIB_OPENSSL_SUPPORT) || \
|
(defined(CPPHTTPLIB_OPENSSL_SUPPORT) || \
|
||||||
defined(CPPHTTPLIB_MBEDTLS_SUPPORT) || \
|
defined(CPPHTTPLIB_MBEDTLS_SUPPORT) || \
|
||||||
defined(CPPHTTPLIB_WOLFSSL_SUPPORT))
|
defined(CPPHTTPLIB_WOLFSSL_SUPPORT))
|
||||||
|
#if TARGET_OS_OSX
|
||||||
#ifndef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
#ifndef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
||||||
#define CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
#define CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && \
|
||||||
|
defined(__APPLE__) && !TARGET_OS_OSX
|
||||||
|
#error \
|
||||||
|
"CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN is only supported on macOS. On iOS/tvOS/watchOS, supply a CA bundle via set_ca_cert_path()."
|
||||||
|
#endif
|
||||||
|
|
||||||
// On Windows, enable Schannel certificate verification by default
|
// On Windows, enable Schannel certificate verification by default
|
||||||
// unless the user explicitly opts out.
|
// unless the user explicitly opts out.
|
||||||
@@ -382,7 +392,7 @@ using socket_t = int;
|
|||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
||||||
#if TARGET_OS_MAC
|
#if TARGET_OS_OSX
|
||||||
#include <Security/Security.h>
|
#include <Security/Security.h>
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@@ -430,7 +440,7 @@ using socket_t = int;
|
|||||||
#endif
|
#endif
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
||||||
#if TARGET_OS_MAC
|
#if TARGET_OS_OSX
|
||||||
#include <Security/Security.h>
|
#include <Security/Security.h>
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@@ -473,7 +483,7 @@ using socket_t = int;
|
|||||||
#endif
|
#endif
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
||||||
#if TARGET_OS_MAC
|
#if TARGET_OS_OSX
|
||||||
#include <Security/Security.h>
|
#include <Security/Security.h>
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@@ -16143,9 +16153,18 @@ inline bool enumerate_windows_system_certs(Callback cb) {
|
|||||||
template <typename Callback>
|
template <typename Callback>
|
||||||
inline bool enumerate_macos_keychain_certs(Callback cb) {
|
inline bool enumerate_macos_keychain_certs(Callback cb) {
|
||||||
bool loaded = false;
|
bool loaded = false;
|
||||||
CFArrayRef certs = nullptr;
|
const SecTrustSettingsDomain domains[] = {
|
||||||
OSStatus status = SecTrustCopyAnchorCertificates(&certs);
|
kSecTrustSettingsDomainSystem,
|
||||||
if (status == errSecSuccess && certs) {
|
kSecTrustSettingsDomainAdmin,
|
||||||
|
kSecTrustSettingsDomainUser,
|
||||||
|
};
|
||||||
|
for (auto domain : domains) {
|
||||||
|
CFArrayRef certs = nullptr;
|
||||||
|
OSStatus status = SecTrustSettingsCopyCertificates(domain, &certs);
|
||||||
|
if (status != errSecSuccess || !certs) {
|
||||||
|
if (certs) CFRelease(certs);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
CFIndex count = CFArrayGetCount(certs);
|
CFIndex count = CFArrayGetCount(certs);
|
||||||
for (CFIndex i = 0; i < count; i++) {
|
for (CFIndex i = 0; i < count; i++) {
|
||||||
SecCertificateRef cert =
|
SecCertificateRef cert =
|
||||||
@@ -16508,28 +16527,36 @@ inline bool load_system_certs(ctx_t ctx) {
|
|||||||
auto store = SSL_CTX_get_cert_store(ssl_ctx);
|
auto store = SSL_CTX_get_cert_store(ssl_ctx);
|
||||||
if (!store) return false;
|
if (!store) return false;
|
||||||
|
|
||||||
CFArrayRef certs = nullptr;
|
|
||||||
if (SecTrustCopyAnchorCertificates(&certs) != errSecSuccess || !certs) {
|
|
||||||
return SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool loaded_any = false;
|
bool loaded_any = false;
|
||||||
auto count = CFArrayGetCount(certs);
|
const SecTrustSettingsDomain domains[] = {
|
||||||
for (CFIndex i = 0; i < count; i++) {
|
kSecTrustSettingsDomainSystem,
|
||||||
auto cert = reinterpret_cast<SecCertificateRef>(
|
kSecTrustSettingsDomainAdmin,
|
||||||
const_cast<void *>(CFArrayGetValueAtIndex(certs, i)));
|
kSecTrustSettingsDomainUser,
|
||||||
CFDataRef der = SecCertificateCopyData(cert);
|
};
|
||||||
if (der) {
|
for (auto domain : domains) {
|
||||||
const unsigned char *data = CFDataGetBytePtr(der);
|
CFArrayRef certs = nullptr;
|
||||||
auto x509 = d2i_X509(nullptr, &data, CFDataGetLength(der));
|
if (SecTrustSettingsCopyCertificates(domain, &certs) != errSecSuccess ||
|
||||||
if (x509) {
|
!certs) {
|
||||||
if (X509_STORE_add_cert(store, x509) == 1) { loaded_any = true; }
|
if (certs) CFRelease(certs);
|
||||||
X509_free(x509);
|
continue;
|
||||||
}
|
|
||||||
CFRelease(der);
|
|
||||||
}
|
}
|
||||||
|
auto count = CFArrayGetCount(certs);
|
||||||
|
for (CFIndex i = 0; i < count; i++) {
|
||||||
|
auto cert = reinterpret_cast<SecCertificateRef>(
|
||||||
|
const_cast<void *>(CFArrayGetValueAtIndex(certs, i)));
|
||||||
|
CFDataRef der = SecCertificateCopyData(cert);
|
||||||
|
if (der) {
|
||||||
|
const unsigned char *data = CFDataGetBytePtr(der);
|
||||||
|
auto x509 = d2i_X509(nullptr, &data, CFDataGetLength(der));
|
||||||
|
if (x509) {
|
||||||
|
if (X509_STORE_add_cert(store, x509) == 1) { loaded_any = true; }
|
||||||
|
X509_free(x509);
|
||||||
|
}
|
||||||
|
CFRelease(der);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CFRelease(certs);
|
||||||
}
|
}
|
||||||
CFRelease(certs);
|
|
||||||
return loaded_any || SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
|
return loaded_any || SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
|
||||||
#else
|
#else
|
||||||
return SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
|
return SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
|
||||||
|
|||||||
Reference in new issue
Block a user