Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Bugfixes:

Features:

- kotlin/c++: add option to support platform provided certificates validation interfaces on Android. (:issue `#2144 <2144>`)
- api: add option to support platform provided certificates validation interfaces on iOS and Android. (:issue `#2144 <2144>`)
- api: Add a ``setPerTryIdleTimeoutSeconds()`` method to C++ EngineBuilder.
- kotlin: add a way to tell Envoy Mobile to respect system proxy settings by calling an ``enableProxying(true)`` method on the engine builder. (:issue:`#2416 <2416>`)
- kotlin: add a ``enableSkipDNSLookupForProxiedRequests(true)`` knob for controlling whether Envoy waits on DNS response in the dynamic forward proxy filter for proxied requests. (:issue:`#2602 <2602>`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ void PlatformBridgeCertValidator::PendingValidation::postVerifyResultAndCleanUp(
if (weak_alive_indicator.expired()) {
return;
}
ENVOY_LOG(trace, "Get validation result for {} from platform", host_name_);
ENVOY_LOG(trace, "Got validation result for {} from platform", host_name_);
parent_.validation_threads_[thread_id].join();
parent_.validation_threads_.erase(thread_id);
if (error_counter.has_value()) {
Expand All @@ -158,7 +158,9 @@ void PlatformBridgeCertValidator::PendingValidation::postVerifyResultAndCleanUp(
"Finished platform cert validation for {}, post result callback to network thread",
host_name_);

parent_.platform_validator_->release_validator();
if (parent_.platform_validator_->release_validator) {
parent_.platform_validator_->release_validator();
}
}

} // namespace Tls
Expand Down
20 changes: 20 additions & 0 deletions library/common/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,23 @@ envoy_cc_library(
"@envoy//source/common/network:socket_interface_lib",
],
)

cc_library(
name = "apple_platform_cert_verifier",
srcs = select({
"@envoy//bazel:apple": ["apple_platform_cert_verifier.cc"],
"//conditions:default": [],
}),
hdrs = select({
"@envoy//bazel:apple": ["apple_platform_cert_verifier.h"],
"//conditions:default": [],
}),
deps = select({
"@envoy//bazel:apple": [
"//library/common/extensions/cert_validator/platform_bridge:c_types_lib",
"//library/common:envoy_main_interface_lib",
"@envoy//bazel:boringssl",
],
"//conditions:default": [],
}),
)
120 changes: 120 additions & 0 deletions library/common/network/apple_platform_cert_verifier.cc
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
16 changes: 16 additions & 0 deletions library/common/network/apple_platform_cert_verifier.h
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
1 change: 1 addition & 0 deletions library/objective-c/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ objc_library(
":envoy_objc_bridge_lib",
"//library/common:envoy_main_interface_lib",
"//library/common/api:c_types",
"//library/common/network:apple_platform_cert_verifier",
],
)

Expand Down
8 changes: 6 additions & 2 deletions library/objective-c/EnvoyConfiguration.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ - (instancetype)initWithAdminInterfaceEnabled:(BOOL)adminInterfaceEnabled
enableInterfaceBinding:(BOOL)enableInterfaceBinding
enableDrainPostDnsRefresh:(BOOL)enableDrainPostDnsRefresh
enforceTrustChainVerification:(BOOL)enforceTrustChainVerification
enablePlatformCertificateValidation:(BOOL)enablePlatformCertificateValidation
forceIPv6:(BOOL)forceIPv6
h2ConnectionKeepaliveIdleIntervalMilliseconds:
(UInt32)h2ConnectionKeepaliveIdleIntervalMilliseconds
Expand Down Expand Up @@ -64,6 +65,7 @@ - (instancetype)initWithAdminInterfaceEnabled:(BOOL)adminInterfaceEnabled
self.enableInterfaceBinding = enableInterfaceBinding;
self.enableDrainPostDnsRefresh = enableDrainPostDnsRefresh;
self.enforceTrustChainVerification = enforceTrustChainVerification;
self.enablePlatformCertificateValidation = enablePlatformCertificateValidation;
self.forceIPv6 = forceIPv6;
self.h2ConnectionKeepaliveIdleIntervalMilliseconds =
h2ConnectionKeepaliveIdleIntervalMilliseconds;
Expand Down Expand Up @@ -187,8 +189,10 @@ - (nullable NSString *)resolveTemplate:(NSString *)templateYAML {
[definitions
appendFormat:@"- &stats_flush_interval %lus\n", (unsigned long)self.statsFlushSeconds];

NSString *cert_validator_template =
[[NSString alloc] initWithUTF8String:default_cert_validation_context_template];
NSString *cert_validator_template = self.enablePlatformCertificateValidation
? @(platform_cert_validation_context_template)
: @(default_cert_validation_context_template);

[definitions appendFormat:@"%@\n", cert_validator_template];

NSMutableArray *stat_sinks_config = [self.statsSinks mutableCopy];
Expand Down
2 changes: 2 additions & 0 deletions library/objective-c/EnvoyEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ extern const int kEnvoyFilterResumeStatusResumeIteration;
@property (nonatomic, assign) BOOL enableInterfaceBinding;
@property (nonatomic, assign) BOOL enableDrainPostDnsRefresh;
@property (nonatomic, assign) BOOL enforceTrustChainVerification;
@property (nonatomic, assign) BOOL enablePlatformCertificateValidation;
@property (nonatomic, assign) BOOL forceIPv6;
@property (nonatomic, assign) UInt32 h2ConnectionKeepaliveIdleIntervalMilliseconds;
@property (nonatomic, assign) UInt32 h2ConnectionKeepaliveTimeoutSeconds;
Expand Down Expand Up @@ -398,6 +399,7 @@ extern const int kEnvoyFilterResumeStatusResumeIteration;
enableInterfaceBinding:(BOOL)enableInterfaceBinding
enableDrainPostDnsRefresh:(BOOL)enableDrainPostDnsRefresh
enforceTrustChainVerification:(BOOL)enforceTrustChainVerification
enablePlatformCertificateValidation:(BOOL)enablePlatformCertificateValidation
forceIPv6:(BOOL)forceIPv6
h2ConnectionKeepaliveIdleIntervalMilliseconds:
(UInt32)h2ConnectionKeepaliveIdleIntervalMilliseconds
Expand Down
5 changes: 5 additions & 0 deletions library/objective-c/EnvoyEngineImpl.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "library/common/api/c_types.h"

#import "library/common/main_interface.h"
#import "library/common/network/apple_platform_cert_verifier.h"
#import "library/common/types/c_types.h"
#import "library/common/extensions/key_value/platform/c_types.h"

Expand Down Expand Up @@ -555,6 +556,10 @@ - (int)runWithTemplate:(NSString *)yaml
[self registerKeyValueStore:name keyValueStore:config.keyValueStores[name]];
}

if (config.enablePlatformCertificateValidation) {
register_apple_platform_cert_verifier();
}

return [self runWithConfigYAML:resolvedYAML logLevel:logLevel];
}

Expand Down
14 changes: 14 additions & 0 deletions library/swift/EngineBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this default to false until it's been vetted in a production experiment?

Suggested change
private var enablePlatformCertificateValidation: Bool = true
private var enablePlatformCertificateValidation: Bool = false

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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
Expand Down Expand Up @@ -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.
///
Expand Down Expand Up @@ -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,
Expand Down
16 changes: 16 additions & 0 deletions test/swift/EngineBuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,7 @@ final class EngineBuilderTests: XCTestCase {
enableInterfaceBinding: true,
enableDrainPostDnsRefresh: false,
enforceTrustChainVerification: false,
enablePlatformCertificateValidation: false,
forceIPv6: false,
h2ConnectionKeepaliveIdleIntervalMilliseconds: 1,
h2ConnectionKeepaliveTimeoutSeconds: 333,
Expand Down Expand Up @@ -506,6 +507,12 @@ final class EngineBuilderTests: XCTestCase {
XCTAssertTrue(resolvedYAML.contains("&dns_multiple_addresses true"))
XCTAssertTrue(resolvedYAML.contains("&enable_interface_binding true"))
XCTAssertTrue(resolvedYAML.contains("&trust_chain_verification ACCEPT_UNTRUSTED"))
XCTAssertTrue(resolvedYAML.contains("""
&validation_context
trusted_ca:
inline_string: *tls_root_certs
"""
))
XCTAssertTrue(resolvedYAML.contains("&enable_drain_post_dns_refresh false"))

// HTTP/2
Expand Down Expand Up @@ -558,6 +565,7 @@ final class EngineBuilderTests: XCTestCase {
enableInterfaceBinding: false,
enableDrainPostDnsRefresh: true,
enforceTrustChainVerification: true,
enablePlatformCertificateValidation: true,
forceIPv6: true,
h2ConnectionKeepaliveIdleIntervalMilliseconds: 1,
h2ConnectionKeepaliveTimeoutSeconds: 333,
Expand Down Expand Up @@ -586,6 +594,13 @@ final class EngineBuilderTests: XCTestCase {
XCTAssertTrue(resolvedYAML.contains("&dns_multiple_addresses false"))
XCTAssertTrue(resolvedYAML.contains("&enable_interface_binding false"))
XCTAssertTrue(resolvedYAML.contains("&trust_chain_verification VERIFY_TRUST_CHAIN"))
XCTAssertTrue(resolvedYAML.contains(
"""
&validation_context
custom_validator_config:
name: "envoy_mobile.cert_validator.platform_bridge_cert_validator"
"""
))
XCTAssertTrue(resolvedYAML.contains("&h2_delay_keepalive_timeout false"))
XCTAssertTrue(resolvedYAML.contains("&enable_drain_post_dns_refresh true"))

Expand All @@ -611,6 +626,7 @@ final class EngineBuilderTests: XCTestCase {
enableInterfaceBinding: false,
enableDrainPostDnsRefresh: false,
enforceTrustChainVerification: true,
enablePlatformCertificateValidation: true,
forceIPv6: true,
h2ConnectionKeepaliveIdleIntervalMilliseconds: 222,
h2ConnectionKeepaliveTimeoutSeconds: 333,
Expand Down
1 change: 1 addition & 0 deletions test/swift/apps/experimental/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ final class ViewController: UITableViewController {
.h2ExtendKeepaliveTimeout(true)
.enableAdminInterface()
.enableInterfaceBinding(true)
.enablePlatformCertificateValidation(true)
.addNativeFilter(
name: "envoy.filters.http.buffer",
typedConfig: """
Expand Down