mirror of
https://github.com/yhirose/cpp-httplib
synced 2024-11-21 14:29:10 -07:00
Support loading system certs from Keychein on MacOS (#1474)
* Support loading system certs from Keychein on MacOS * review improvements: add deps to meson.build and improve conditional expressions in cmake * fix tabs * fix tabs * review improvements * fix after review * additionally load root certs from the system root keychain * cmake fix * fix * small refactoring * small refactoring --------- Co-authored-by: Sergey Kazmin <sergey.kazmin@kaspersky.com>
This commit is contained in:
parent
88f6245c84
commit
6d963fbe8d
4 changed files with 115 additions and 6 deletions
|
@ -206,6 +206,8 @@ target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||||
$<$<PLATFORM_ID:Windows>:ws2_32>
|
$<$<PLATFORM_ID:Windows>:ws2_32>
|
||||||
$<$<PLATFORM_ID:Windows>:crypt32>
|
$<$<PLATFORM_ID:Windows>:crypt32>
|
||||||
$<$<PLATFORM_ID:Windows>:cryptui>
|
$<$<PLATFORM_ID:Windows>:cryptui>
|
||||||
|
# Needed for API from MacOS Security framework
|
||||||
|
"$<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>>:-framework CoreFoundation -framework Security>"
|
||||||
# Can't put multiple targets in a single generator expression or it bugs out.
|
# Can't put multiple targets in a single generator expression or it bugs out.
|
||||||
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::common>
|
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::common>
|
||||||
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::encoder>
|
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::encoder>
|
||||||
|
|
109
httplib.h
109
httplib.h
|
@ -239,7 +239,10 @@ using socket_t = int;
|
||||||
#pragma comment(lib, "crypt32.lib")
|
#pragma comment(lib, "crypt32.lib")
|
||||||
#pragma comment(lib, "cryptui.lib")
|
#pragma comment(lib, "cryptui.lib")
|
||||||
#endif
|
#endif
|
||||||
#endif //_WIN32
|
#elif defined(__APPLE__) // _WIN32
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
#include <Security/Security.h>
|
||||||
|
#endif // __APPLE__
|
||||||
|
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
|
@ -4388,15 +4391,15 @@ inline std::string SHA_512(const std::string &s) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
|
#ifdef _WIN32
|
||||||
// NOTE: This code came up with the following stackoverflow post:
|
// NOTE: This code came up with the following stackoverflow post:
|
||||||
// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
|
// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
|
||||||
inline bool load_system_certs_on_windows(X509_STORE *store) {
|
inline bool load_system_certs_on_windows(X509_STORE *store) {
|
||||||
auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
|
auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
|
||||||
|
|
||||||
if (!hStore) { return false; }
|
if (!hStore) { return false; }
|
||||||
|
|
||||||
|
auto result = false;
|
||||||
PCCERT_CONTEXT pContext = NULL;
|
PCCERT_CONTEXT pContext = NULL;
|
||||||
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
|
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
|
||||||
nullptr) {
|
nullptr) {
|
||||||
|
@ -4407,16 +4410,107 @@ inline bool load_system_certs_on_windows(X509_STORE *store) {
|
||||||
if (x509) {
|
if (x509) {
|
||||||
X509_STORE_add_cert(store, x509);
|
X509_STORE_add_cert(store, x509);
|
||||||
X509_free(x509);
|
X509_free(x509);
|
||||||
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CertFreeCertificateContext(pContext);
|
CertFreeCertificateContext(pContext);
|
||||||
CertCloseStore(hStore, 0);
|
CertCloseStore(hStore, 0);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
template <typename T>
|
||||||
|
using CFObjectPtr =
|
||||||
|
std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
|
||||||
|
|
||||||
|
inline void cf_object_ptr_deleter(CFTypeRef obj) {
|
||||||
|
if (obj) { CFRelease(obj); }
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
|
||||||
|
CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
|
||||||
|
CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll,
|
||||||
|
kCFBooleanTrue};
|
||||||
|
|
||||||
|
CFObjectPtr<CFDictionaryRef> query(
|
||||||
|
CFDictionaryCreate(nullptr, reinterpret_cast<const void **>(keys), values,
|
||||||
|
sizeof(keys) / sizeof(keys[0]),
|
||||||
|
&kCFTypeDictionaryKeyCallBacks,
|
||||||
|
&kCFTypeDictionaryValueCallBacks),
|
||||||
|
cf_object_ptr_deleter);
|
||||||
|
|
||||||
|
if (!query) { return false; }
|
||||||
|
|
||||||
|
CFTypeRef security_items = nullptr;
|
||||||
|
if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
|
||||||
|
CFArrayGetTypeID() != CFGetTypeID(security_items)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
certs.reset(reinterpret_cast<CFArrayRef>(security_items));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
|
||||||
|
CFArrayRef root_security_items = nullptr;
|
||||||
|
if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
certs.reset(root_security_items);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {
|
||||||
|
auto result = false;
|
||||||
|
for (int i = 0; i < CFArrayGetCount(certs); ++i) {
|
||||||
|
const auto cert = reinterpret_cast<const __SecCertificate *>(
|
||||||
|
CFArrayGetValueAtIndex(certs, i));
|
||||||
|
|
||||||
|
if (SecCertificateGetTypeID() != CFGetTypeID(cert)) { continue; }
|
||||||
|
|
||||||
|
CFDataRef cert_data = nullptr;
|
||||||
|
if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) !=
|
||||||
|
errSecSuccess) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
|
||||||
|
|
||||||
|
auto encoded_cert = static_cast<const unsigned char *>(
|
||||||
|
CFDataGetBytePtr(cert_data_ptr.get()));
|
||||||
|
|
||||||
|
auto x509 =
|
||||||
|
d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
|
||||||
|
|
||||||
|
if (x509) {
|
||||||
|
X509_STORE_add_cert(store, x509);
|
||||||
|
X509_free(x509);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool load_system_certs_on_apple(X509_STORE *store) {
|
||||||
|
auto result = false;
|
||||||
|
CFObjectPtr<CFArrayRef> certs(nullptr, cf_object_ptr_deleter);
|
||||||
|
if (retrieve_certs_from_keychain(certs) && certs) {
|
||||||
|
result = add_certs_to_x509_store(certs.get(), store);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retrieve_root_certs_from_keychain(certs) && certs) {
|
||||||
|
result = add_certs_to_x509_store(certs.get(), store) || result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
class WSInit {
|
class WSInit {
|
||||||
public:
|
public:
|
||||||
WSInit() {
|
WSInit() {
|
||||||
|
@ -7842,11 +7936,14 @@ inline bool SSLClient::load_certs() {
|
||||||
ret = false;
|
ret = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
auto loaded = false;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
|
loaded =
|
||||||
#else
|
detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
|
||||||
SSL_CTX_set_default_verify_paths(ctx_);
|
#elif defined(__APPLE__)
|
||||||
|
loaded = detail::load_system_certs_on_apple(SSL_CTX_get_cert_store(ctx_));
|
||||||
#endif
|
#endif
|
||||||
|
if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,9 @@ openssl_dep = dependency('openssl', version: '>=1.1.1', required: get_option('cp
|
||||||
if openssl_dep.found()
|
if openssl_dep.found()
|
||||||
deps += openssl_dep
|
deps += openssl_dep
|
||||||
args += '-DCPPHTTPLIB_OPENSSL_SUPPORT'
|
args += '-DCPPHTTPLIB_OPENSSL_SUPPORT'
|
||||||
|
if host_machine.system() == 'darwin'
|
||||||
|
deps += dependency('appleframeworks', modules: ['CoreFoundation', 'Security'])
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
zlib_dep = dependency('zlib', required: get_option('cpp-httplib_zlib'))
|
zlib_dep = dependency('zlib', required: get_option('cpp-httplib_zlib'))
|
||||||
|
|
|
@ -8,6 +8,13 @@ OPENSSL_DIR = $(PREFIX)/opt/openssl@1.1
|
||||||
#OPENSSL_DIR = $(PREFIX)/opt/openssl@3
|
#OPENSSL_DIR = $(PREFIX)/opt/openssl@3
|
||||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||||
|
|
||||||
|
ifneq ($(OS), Windows_NT)
|
||||||
|
UNAME_S := $(shell uname -s)
|
||||||
|
ifeq ($(UNAME_S), Darwin)
|
||||||
|
OPENSSL_SUPPORT += -framework CoreFoundation -framework Security
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||||
|
|
||||||
BROTLI_DIR = $(PREFIX)/opt/brotli
|
BROTLI_DIR = $(PREFIX)/opt/brotli
|
||||||
|
|
Loading…
Reference in a new issue