mirror of
https://github.com/yhirose/cpp-httplib
synced 2024-11-21 06:26:02 -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>:crypt32>
|
||||
$<$<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.
|
||||
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::common>
|
||||
$<$<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, "cryptui.lib")
|
||||
#endif
|
||||
#endif //_WIN32
|
||||
#elif defined(__APPLE__) // _WIN32
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Security/Security.h>
|
||||
#endif // __APPLE__
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
|
@ -4388,15 +4391,15 @@ inline std::string SHA_512(const std::string &s) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#ifdef _WIN32
|
||||
// NOTE: This code came up with the following stackoverflow post:
|
||||
// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
|
||||
inline bool load_system_certs_on_windows(X509_STORE *store) {
|
||||
auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
|
||||
|
||||
if (!hStore) { return false; }
|
||||
|
||||
auto result = false;
|
||||
PCCERT_CONTEXT pContext = NULL;
|
||||
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
|
||||
nullptr) {
|
||||
|
@ -4407,16 +4410,107 @@ inline bool load_system_certs_on_windows(X509_STORE *store) {
|
|||
if (x509) {
|
||||
X509_STORE_add_cert(store, x509);
|
||||
X509_free(x509);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
CertFreeCertificateContext(pContext);
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#ifdef _WIN32
|
||||
class WSInit {
|
||||
public:
|
||||
WSInit() {
|
||||
|
@ -7842,11 +7936,14 @@ inline bool SSLClient::load_certs() {
|
|||
ret = false;
|
||||
}
|
||||
} else {
|
||||
auto loaded = false;
|
||||
#ifdef _WIN32
|
||||
detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
|
||||
#else
|
||||
SSL_CTX_set_default_verify_paths(ctx_);
|
||||
loaded =
|
||||
detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
|
||||
#elif defined(__APPLE__)
|
||||
loaded = detail::load_system_certs_on_apple(SSL_CTX_get_cert_store(ctx_));
|
||||
#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()
|
||||
deps += openssl_dep
|
||||
args += '-DCPPHTTPLIB_OPENSSL_SUPPORT'
|
||||
if host_machine.system() == 'darwin'
|
||||
deps += dependency('appleframeworks', modules: ['CoreFoundation', 'Security'])
|
||||
endif
|
||||
endif
|
||||
|
||||
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_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
|
||||
|
||||
BROTLI_DIR = $(PREFIX)/opt/brotli
|
||||
|
|
Loading…
Reference in a new issue