|
@@ -19592,15 +19592,75 @@ inline bool enumerate_windows_system_certs(Callback cb) {
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
|
#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
|
|
-// Enumerate macOS Keychain certificates and call callback with DER data
|
|
|
|
|
|
|
+// The trust a certificate's Trust Settings give it for SSL server
|
|
|
|
|
+// verification (SecTrustSettings.h). An empty array trusts it as a root.
|
|
|
|
|
+// Otherwise the first usage-constraints dictionary that applies decides: one
|
|
|
|
|
+// naming a policy other than SSL, a host name or an application does not
|
|
|
|
|
+// apply, and a result left out means TrustRoot. Keychain Access writes "Never
|
|
|
|
|
+// Trust" as kSecTrustSettingsResultDeny.
|
|
|
|
|
+inline SecTrustSettingsResult
|
|
|
|
|
+macos_ssl_trust_settings_result(CFArrayRef trust_settings) {
|
|
|
|
|
+ auto count = CFArrayGetCount(trust_settings);
|
|
|
|
|
+ if (count == 0) { return kSecTrustSettingsResultTrustRoot; }
|
|
|
|
|
+
|
|
|
|
|
+ for (CFIndex i = 0; i < count; i++) {
|
|
|
|
|
+ auto value = CFArrayGetValueAtIndex(trust_settings, i);
|
|
|
|
|
+ if (!value || CFGetTypeID(value) != CFDictionaryGetTypeID()) { continue; }
|
|
|
|
|
+ auto constraint = static_cast<CFDictionaryRef>(value);
|
|
|
|
|
+
|
|
|
|
|
+ auto policy_value =
|
|
|
|
|
+ CFDictionaryGetValue(constraint, kSecTrustSettingsPolicy);
|
|
|
|
|
+ if (policy_value) {
|
|
|
|
|
+ auto policy = static_cast<SecPolicyRef>(const_cast<void *>(policy_value));
|
|
|
|
|
+ auto is_ssl = false;
|
|
|
|
|
+ if (auto properties = SecPolicyCopyProperties(policy)) {
|
|
|
|
|
+ auto oid = CFDictionaryGetValue(properties, kSecPolicyOid);
|
|
|
|
|
+ is_ssl = oid && CFEqual(oid, kSecPolicyAppleSSL);
|
|
|
|
|
+ CFRelease(properties);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!is_ssl) { continue; }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (CFDictionaryContainsKey(constraint, kSecTrustSettingsPolicyString) ||
|
|
|
|
|
+ CFDictionaryContainsKey(constraint, kSecTrustSettingsApplication)) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ auto result = kSecTrustSettingsResultTrustRoot;
|
|
|
|
|
+ auto number = static_cast<CFNumberRef>(
|
|
|
|
|
+ CFDictionaryGetValue(constraint, kSecTrustSettingsResult));
|
|
|
|
|
+ if (number) {
|
|
|
|
|
+ SInt32 number_value = 0;
|
|
|
|
|
+ if (!CFNumberGetValue(number, kCFNumberSInt32Type, &number_value)) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ result = static_cast<SecTrustSettingsResult>(number_value);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (result == kSecTrustSettingsResultTrustRoot ||
|
|
|
|
|
+ result == kSecTrustSettingsResultTrustAsRoot ||
|
|
|
|
|
+ result == kSecTrustSettingsResultDeny) {
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return kSecTrustSettingsResultUnspecified;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Enumerate the macOS Keychain certificates trusted for SSL and call callback
|
|
|
|
|
+// with DER data. A certificate with Trust Settings is not trusted just by
|
|
|
|
|
+// having them: the settings say whether it is, and a per-user setting
|
|
|
|
|
+// overrides the admin domain's, which overrides the system domain's (whose
|
|
|
|
|
+// roots are all trusted). So a system root the user set to "Never Trust" is
|
|
|
|
|
+// left out, as is one trusted only for S/MIME or code signing.
|
|
|
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;
|
|
|
|
|
const SecTrustSettingsDomain domains[] = {
|
|
const SecTrustSettingsDomain domains[] = {
|
|
|
- kSecTrustSettingsDomainSystem,
|
|
|
|
|
- kSecTrustSettingsDomainAdmin,
|
|
|
|
|
kSecTrustSettingsDomainUser,
|
|
kSecTrustSettingsDomainUser,
|
|
|
|
|
+ kSecTrustSettingsDomainAdmin,
|
|
|
|
|
+ kSecTrustSettingsDomainSystem,
|
|
|
};
|
|
};
|
|
|
|
|
+ std::set<std::string> decided;
|
|
|
|
|
+ std::vector<std::string> trusted;
|
|
|
for (auto domain : domains) {
|
|
for (auto domain : domains) {
|
|
|
CFArrayRef certs = nullptr;
|
|
CFArrayRef certs = nullptr;
|
|
|
OSStatus status = SecTrustSettingsCopyCertificates(domain, &certs);
|
|
OSStatus status = SecTrustSettingsCopyCertificates(domain, &certs);
|
|
@@ -19613,16 +19673,43 @@ inline bool enumerate_macos_keychain_certs(Callback cb) {
|
|
|
SecCertificateRef cert =
|
|
SecCertificateRef cert =
|
|
|
(SecCertificateRef)CFArrayGetValueAtIndex(certs, i);
|
|
(SecCertificateRef)CFArrayGetValueAtIndex(certs, i);
|
|
|
CFDataRef data = SecCertificateCopyData(cert);
|
|
CFDataRef data = SecCertificateCopyData(cert);
|
|
|
- if (data) {
|
|
|
|
|
- if (cb(CFDataGetBytePtr(data),
|
|
|
|
|
- static_cast<size_t>(CFDataGetLength(data)))) {
|
|
|
|
|
- loaded = true;
|
|
|
|
|
|
|
+ if (!data) { continue; }
|
|
|
|
|
+ std::string der(reinterpret_cast<const char *>(CFDataGetBytePtr(data)),
|
|
|
|
|
+ static_cast<size_t>(CFDataGetLength(data)));
|
|
|
|
|
+ CFRelease(data);
|
|
|
|
|
+
|
|
|
|
|
+ // Decided by a domain that overrides this one
|
|
|
|
|
+ if (decided.find(der) != decided.end()) { continue; }
|
|
|
|
|
+
|
|
|
|
|
+ auto result = kSecTrustSettingsResultTrustRoot;
|
|
|
|
|
+ if (domain != kSecTrustSettingsDomainSystem) {
|
|
|
|
|
+ result = kSecTrustSettingsResultUnspecified;
|
|
|
|
|
+ CFArrayRef trust_settings = nullptr;
|
|
|
|
|
+ if (SecTrustSettingsCopyTrustSettings(cert, domain, &trust_settings) ==
|
|
|
|
|
+ errSecSuccess &&
|
|
|
|
|
+ trust_settings) {
|
|
|
|
|
+ result = macos_ssl_trust_settings_result(trust_settings);
|
|
|
}
|
|
}
|
|
|
- CFRelease(data);
|
|
|
|
|
|
|
+ if (trust_settings) CFRelease(trust_settings);
|
|
|
|
|
+ }
|
|
|
|
|
+ // Neither trusted nor distrusted here; a domain below may decide
|
|
|
|
|
+ if (result == kSecTrustSettingsResultUnspecified) { continue; }
|
|
|
|
|
+
|
|
|
|
|
+ decided.insert(der);
|
|
|
|
|
+ if (result == kSecTrustSettingsResultTrustRoot ||
|
|
|
|
|
+ result == kSecTrustSettingsResultTrustAsRoot) {
|
|
|
|
|
+ trusted.push_back(std::move(der));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
CFRelease(certs);
|
|
CFRelease(certs);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ bool loaded = false;
|
|
|
|
|
+ for (const auto &der : trusted) {
|
|
|
|
|
+ if (cb(reinterpret_cast<const unsigned char *>(der.data()), der.size())) {
|
|
|
|
|
+ loaded = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
return loaded;
|
|
return loaded;
|
|
|
}
|
|
}
|
|
|
#endif
|
|
#endif
|
|
@@ -20056,36 +20143,14 @@ 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;
|
|
|
|
|
|
|
|
- bool loaded_any = false;
|
|
|
|
|
- const SecTrustSettingsDomain domains[] = {
|
|
|
|
|
- kSecTrustSettingsDomainSystem,
|
|
|
|
|
- kSecTrustSettingsDomainAdmin,
|
|
|
|
|
- kSecTrustSettingsDomainUser,
|
|
|
|
|
- };
|
|
|
|
|
- for (auto domain : domains) {
|
|
|
|
|
- CFArrayRef certs = nullptr;
|
|
|
|
|
- if (SecTrustSettingsCopyCertificates(domain, &certs) != errSecSuccess ||
|
|
|
|
|
- !certs) {
|
|
|
|
|
- if (certs) CFRelease(certs);
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
- 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);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ auto loaded_any = impl::enumerate_macos_keychain_certs(
|
|
|
|
|
+ [&](const unsigned char *data, size_t len) {
|
|
|
|
|
+ auto x509 = d2i_X509(nullptr, &data, static_cast<long>(len));
|
|
|
|
|
+ if (!x509) { return false; }
|
|
|
|
|
+ auto added = X509_STORE_add_cert(store, x509) == 1;
|
|
|
|
|
+ X509_free(x509);
|
|
|
|
|
+ return added;
|
|
|
|
|
+ });
|
|
|
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;
|