-
Notifications
You must be signed in to change notification settings - Fork 84
Implement an iOS platform certificate verifier. #2638
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6ad9ea6
e002ba1
8a9bff4
32449b9
bf3b101
e44f436
138e0a6
2aef3cd
30bf561
951dd14
65cd2b4
37cdc26
f7be8db
3bb3082
4f4fe2d
5c79ed9
0775abd
f977bc2
aa258c9
55efac9
56c01b3
afb2154
b883c10
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| #include "library/common/network/apple_platform_cert_verifier.h" | ||
|
|
||
| #include <CoreFoundation/CFArray.h> | ||
| #include <CoreFoundation/CoreFoundation.h> | ||
| #include <Security/SecCertificate.h> | ||
| #include <Security/SecPolicy.h> | ||
| #include <Security/SecTrust.h> | ||
|
|
||
| #include "library/common/extensions/cert_validator/platform_bridge/c_types.h" | ||
| #include "library/common/main_interface.h" | ||
| #include "openssl/ssl.h" | ||
|
|
||
| // NOLINT(namespace-envoy) | ||
|
|
||
| // Returns a new CFMutableArrayRef containing a series of SecPolicyRefs to be | ||
| // added to a SecTrustRef used to validate a certificate for an SSL server, | ||
| // or NULL on failure. | ||
| CFMutableArrayRef CreateTrustPolicies() { | ||
| CFMutableArrayRef policies = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); | ||
| if (!policies) { | ||
| return NULL; | ||
| } | ||
|
|
||
| SecPolicyRef ssl_policy = SecPolicyCreateBasicX509(); | ||
| CFArrayAppendValue(policies, ssl_policy); | ||
| CFRelease(ssl_policy); | ||
|
|
||
| ssl_policy = SecPolicyCreateSSL(true, NULL); | ||
| CFArrayAppendValue(policies, ssl_policy); | ||
| CFRelease(ssl_policy); | ||
|
|
||
| return policies; | ||
| } | ||
|
|
||
| // Returns a new CFMutableArrayRef containing the specified certificates | ||
| // in the form expected by Security.framework and Keychain Services, or | ||
| // NULL on failure. | ||
| CFMutableArrayRef CreateSecCertificateArray(const envoy_data* certs, uint8_t num_certs) { | ||
| CFMutableArrayRef cert_array = | ||
| CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); | ||
|
|
||
| if (!cert_array) { | ||
| return NULL; | ||
| } | ||
|
|
||
| for (uint8_t i = 0; i < num_certs; ++i) { | ||
| CFDataRef cert_data = CFDataCreate(kCFAllocatorDefault, certs[i].bytes, certs[i].length); | ||
| if (!cert_data) { | ||
| CFRelease(cert_array); | ||
| return NULL; | ||
| } | ||
| SecCertificateRef sec_cert = SecCertificateCreateWithData(NULL, cert_data); | ||
| if (!sec_cert) { | ||
| CFRelease(cert_array); | ||
| return NULL; | ||
| } | ||
| CFArrayAppendValue(cert_array, sec_cert); | ||
| CFRelease(cert_data); | ||
| } | ||
| return cert_array; | ||
| } | ||
|
|
||
| // Helper to create a envoy_cert_validation_result. | ||
| envoy_cert_validation_result make_result(envoy_status_t status, uint8_t tls_alert, | ||
| const char* error_details) { | ||
| envoy_cert_validation_result result; | ||
| result.result = status; | ||
| result.tls_alert = tls_alert; | ||
| result.error_details = error_details; | ||
| return result; | ||
| } | ||
|
|
||
| static envoy_cert_validation_result verify_cert(const envoy_data* certs, uint8_t num_certs, | ||
| const char* hostname) { | ||
| CFArrayRef trust_policies = CreateTrustPolicies(); | ||
| if (!trust_policies) { | ||
| return make_result(ENVOY_FAILURE, SSL_AD_CERTIFICATE_UNKNOWN, | ||
| "validation couldn't be conducted."); | ||
| } | ||
|
|
||
| CFMutableArrayRef cert_array = CreateSecCertificateArray(certs, num_certs); | ||
| if (!cert_array) { | ||
| return make_result(ENVOY_FAILURE, SSL_AD_CERTIFICATE_UNKNOWN, | ||
| "validation couldn't be conducted."); | ||
| } | ||
|
|
||
| SecTrustRef trust = NULL; | ||
| OSStatus status = SecTrustCreateWithCertificates(cert_array, trust_policies, &trust); | ||
| if (status) { | ||
| return make_result(ENVOY_FAILURE, SSL_AD_CERTIFICATE_UNKNOWN, | ||
| "validation couldn't be conducted."); | ||
| } | ||
|
|
||
| CFErrorRef error; | ||
| bool verified = SecTrustEvaluateWithError(trust, &error); | ||
|
|
||
| CFRelease(cert_array); | ||
| CFRelease(trust); | ||
|
|
||
| if (!verified) { | ||
| return make_result(ENVOY_FAILURE, SSL_AD_CERTIFICATE_UNKNOWN, | ||
| "validation couldn't be conducted."); | ||
| } | ||
| return make_result(ENVOY_SUCCESS, 0, ""); | ||
| } | ||
|
|
||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|
|
||
| void register_apple_platform_cert_verifier() { | ||
| envoy_cert_validator* api = (envoy_cert_validator*)safe_malloc(sizeof(envoy_cert_validator)); | ||
| api->validate_cert = verify_cert; | ||
| api->release_validator = NULL; | ||
| register_platform_api("platform_cert_validator", api); | ||
| } | ||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| #pragma once | ||
|
|
||
| // NOLINT(namespace-envoy) | ||
|
|
||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|
|
||
| /** | ||
| * Registers the Apple platform cert verifier API. | ||
| */ | ||
| void register_apple_platform_cert_verifier(); | ||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -27,6 +27,7 @@ open class EngineBuilder: NSObject { | |||||
| private var enableBrotli: Bool = false | ||||||
| private var enableInterfaceBinding: Bool = false | ||||||
| private var enforceTrustChainVerification: Bool = true | ||||||
| private var enablePlatformCertificateValidation: Bool = true | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this default to
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, definitely! This was a hack to make sure this new code would be run though CI to help make sure it works. Do you have a suggestion for how to write a test for this code? Or alternatively, a suggestion for some place I could explicitly enable in, maybe in the experimental app? |
||||||
| private var enableDrainPostDnsRefresh: Bool = false | ||||||
| private var forceIPv6: Bool = false | ||||||
| private var h2ConnectionKeepaliveIdleIntervalMilliseconds: UInt32 = 1 | ||||||
|
|
@@ -242,6 +243,18 @@ open class EngineBuilder: NSObject { | |||||
| return self | ||||||
| } | ||||||
|
|
||||||
| /// Specify whether to use the platform certificate verifier. | ||||||
| /// | ||||||
| /// - parameter enablePlatformCertificateValidation: whether to use the platform verifier. | ||||||
| /// | ||||||
| /// - returns: This builder. | ||||||
| @discardableResult | ||||||
| public func enablePlatformCertificateValidation( | ||||||
| _ enablePlatformCertificateValidation: Bool) -> Self { | ||||||
| self.enablePlatformCertificateValidation = enablePlatformCertificateValidation | ||||||
| return self | ||||||
| } | ||||||
|
|
||||||
| /// Specify whether to remap IPv4 addresses to the IPv6 space and always force connections | ||||||
| /// to use IPv6. Note this is an experimental option and should be enabled with caution. | ||||||
| /// | ||||||
|
|
@@ -519,6 +532,7 @@ open class EngineBuilder: NSObject { | |||||
| enableInterfaceBinding: self.enableInterfaceBinding, | ||||||
| enableDrainPostDnsRefresh: self.enableDrainPostDnsRefresh, | ||||||
| enforceTrustChainVerification: self.enforceTrustChainVerification, | ||||||
| enablePlatformCertificateValidation: self.enablePlatformCertificateValidation, | ||||||
| forceIPv6: self.forceIPv6, | ||||||
| h2ConnectionKeepaliveIdleIntervalMilliseconds: | ||||||
| self.h2ConnectionKeepaliveIdleIntervalMilliseconds, | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.