| Index: ios/web/net/crw_cert_verification_controller.mm
|
| diff --git a/ios/web/net/crw_cert_verification_controller.mm b/ios/web/net/crw_cert_verification_controller.mm
|
| index c5dca87c376a89361b8837eaa5b24990958ae5b2..716c31e3f7e74b7c351ef5979e03c6f9c4299d06 100644
|
| --- a/ios/web/net/crw_cert_verification_controller.mm
|
| +++ b/ios/web/net/crw_cert_verification_controller.mm
|
| @@ -12,6 +12,7 @@
|
| #include "base/threading/worker_pool.h"
|
| #include "ios/web/net/cert_verifier_block_adapter.h"
|
| #include "ios/web/public/browser_state.h"
|
| +#include "ios/web/public/certificate_policy_cache.h"
|
| #include "ios/web/public/web_thread.h"
|
| #import "ios/web/web_state/wk_web_view_security_util.h"
|
| #include "net/cert/cert_verify_result.h"
|
| @@ -67,6 +68,9 @@ class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> {
|
|
|
| // URLRequestContextGetter for obtaining net layer objects.
|
| net::URLRequestContextGetter* _contextGetter;
|
| +
|
| + // Used to remember user exceptions to invalid certs.
|
| + scoped_refptr<web::CertificatePolicyCache> _certPolicyCache;
|
| }
|
|
|
| // Cert verification flags. Must be used on IO Thread.
|
| @@ -78,10 +82,11 @@ class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> {
|
| // Verifies the given |cert| for the given |host| using |net::CertVerifier| and
|
| // calls |completionHandler| on completion. This method can be called on any
|
| // thread. |completionHandler| cannot be null and will be called asynchronously
|
| -// on IO thread.
|
| +// on IO thread or synchronously on current thread if IO task can't start (in
|
| +// this case |dispatched| argument will be NO).
|
| - (void)verifyCert:(const scoped_refptr<net::X509Certificate>&)cert
|
| forHost:(NSString*)host
|
| - completionHandler:(void (^)(net::CertVerifyResult, int))completionHandler;
|
| + completionHandler:(void (^)(net::CertVerifyResult, BOOL dispatched))handler;
|
|
|
| // Verifies the given |trust| using SecTrustRef API. |completionHandler| cannot
|
| // be null and will be either called asynchronously on Worker thread or
|
| @@ -90,6 +95,14 @@ class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> {
|
| - (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust
|
| completionHandler:(void (^)(SecTrustResultType, BOOL dispatched))handler;
|
|
|
| +// Returns cert accept policy for the given SecTrust result. |trustResult| must
|
| +// not be for a valid cert. Must be called on IO thread.
|
| +- (web::CertAcceptPolicy)
|
| + loadPolicyForBadTrustResult:(SecTrustResultType)trustResult
|
| + certVerifierResult:(net::CertVerifyResult)certVerifierResult
|
| + serverTrust:(SecTrustRef)trust
|
| + host:(NSString*)host;
|
| +
|
| @end
|
|
|
| @implementation CRWCertVerificationController
|
| @@ -115,14 +128,17 @@ class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> {
|
| if (self) {
|
| _contextGetter = browserState->GetRequestContext();
|
| DCHECK(_contextGetter);
|
| + _certPolicyCache =
|
| + web::BrowserState::GetCertificatePolicyCache(browserState);
|
| +
|
| [self createCertVerifier];
|
| }
|
| return self;
|
| }
|
|
|
| -- (void)decidePolicyForCert:(const scoped_refptr<net::X509Certificate>&)cert
|
| - host:(NSString*)host
|
| - completionHandler:(web::PolicyDecisionHandler)completionHandler {
|
| +- (void)decideLoadPolicyForTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust
|
| + host:(NSString*)host
|
| + completionHandler:(web::PolicyDecisionHandler)completionHandler {
|
| DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
|
| // completionHandler of |verifyCert:forHost:completionHandler:| is called on
|
| // IO thread and then bounces back to UI thread. As a result all objects
|
| @@ -132,22 +148,55 @@ class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> {
|
| // released on background thread and |BlockHolder| ensures that.
|
| __block scoped_refptr<BlockHolder<web::PolicyDecisionHandler>> handlerHolder(
|
| new BlockHolder<web::PolicyDecisionHandler>(completionHandler));
|
| - [self verifyCert:cert
|
| - forHost:host
|
| - completionHandler:^(net::CertVerifyResult result, int error) {
|
| - web::CertAcceptPolicy policy =
|
| - web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR;
|
| - if (error == net::OK) {
|
| - policy = web::CERT_ACCEPT_POLICY_ALLOW;
|
| - } else if (net::IsCertStatusError(result.cert_status)) {
|
| - policy = net::IsCertStatusMinorError(result.cert_status)
|
| - ? web::CERT_ACCEPT_POLICY_ALLOW
|
| - : web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR;
|
| + [self verifyTrust:trust
|
| + completionHandler:^(SecTrustResultType trustResult, BOOL dispatched) {
|
| + if (!dispatched) {
|
| + // Cert verification task did not start.
|
| + dispatch_async(dispatch_get_main_queue(), ^{
|
| + handlerHolder->call(web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR,
|
| + net::CertStatus());
|
| + });
|
| + return;
|
| }
|
|
|
| - dispatch_async(dispatch_get_main_queue(), ^{
|
| - handlerHolder->call(policy, result.cert_status);
|
| - });
|
| + if (web::GetSecurityStyleFromTrustResult(trustResult) ==
|
| + web::SECURITY_STYLE_AUTHENTICATED) {
|
| + // SecTrust API considers this cert as valid.
|
| + dispatch_async(dispatch_get_main_queue(), ^{
|
| + handlerHolder->call(web::CERT_ACCEPT_POLICY_ALLOW,
|
| + net::CertStatus());
|
| + });
|
| + return;
|
| + }
|
| +
|
| + // SecTrust API considers this cert as invalid. Check the reason and
|
| + // whether or not user has decided to proceed with this bad cert.
|
| + scoped_refptr<net::X509Certificate> cert(
|
| + web::CreateCertFromTrust(trust));
|
| + [self verifyCert:cert
|
| + forHost:host
|
| + completionHandler:^(net::CertVerifyResult certVerifierResult,
|
| + BOOL dispatched) {
|
| + if (!dispatched) {
|
| + // Cert verification task did not start.
|
| + dispatch_async(dispatch_get_main_queue(), ^{
|
| + handlerHolder->call(
|
| + web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR,
|
| + net::CertStatus());
|
| + });
|
| + return;
|
| + }
|
| +
|
| + web::CertAcceptPolicy policy =
|
| + [self loadPolicyForBadTrustResult:trustResult
|
| + certVerifierResult:certVerifierResult
|
| + serverTrust:trust.get()
|
| + host:host];
|
| +
|
| + dispatch_async(dispatch_get_main_queue(), ^{
|
| + handlerHolder->call(policy, certVerifierResult.cert_status);
|
| + });
|
| + }];
|
| }];
|
| }
|
|
|
| @@ -191,7 +240,8 @@ class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> {
|
| web::CreateCertFromTrust(trust));
|
| [self verifyCert:cert
|
| forHost:host
|
| - completionHandler:^(net::CertVerifyResult certVerifierResult, int) {
|
| + completionHandler:^(net::CertVerifyResult certVerifierResult,
|
| + BOOL) {
|
| dispatch_async(dispatch_get_main_queue(), ^{
|
| handlerHolder->call(securityStyle,
|
| certVerifierResult.cert_status);
|
| @@ -200,6 +250,26 @@ class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> {
|
| }];
|
| }
|
|
|
| +- (void)allowCert:(scoped_refptr<net::X509Certificate>)cert
|
| + forHost:(NSString*)host
|
| + status:(net::CertStatus)status {
|
| + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
|
| + // Store user decisions with the leaf cert, ignoring any intermediates.
|
| + // This is because WKWebView returns the verified certificate chain in
|
| + // |webView:didReceiveAuthenticationChallenge:completionHandler:|,
|
| + // but the server-supplied chain in
|
| + // |webView:didFailProvisionalNavigation:withError:|.
|
| + if (!cert->GetIntermediateCertificates().empty()) {
|
| + cert = net::X509Certificate::CreateFromHandle(
|
| + cert->os_cert_handle(), net::X509Certificate::OSCertHandles());
|
| + }
|
| + DCHECK(cert->GetIntermediateCertificates().empty());
|
| + web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{
|
| + _certPolicyCache->AllowCertForHost(
|
| + cert.get(), base::SysNSStringToUTF8(host), status);
|
| + }));
|
| +}
|
| +
|
| - (void)shutDown {
|
| DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
|
| web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{
|
| @@ -235,10 +305,10 @@ class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> {
|
|
|
| - (void)verifyCert:(const scoped_refptr<net::X509Certificate>&)cert
|
| forHost:(NSString*)host
|
| - completionHandler:(void (^)(net::CertVerifyResult, int))completionHandler {
|
| + completionHandler:(void (^)(net::CertVerifyResult, BOOL))completionHandler {
|
| DCHECK(completionHandler);
|
| __block scoped_refptr<net::X509Certificate> blockCert = cert;
|
| - web::WebThread::PostTask(
|
| + bool dispatched = web::WebThread::PostTask(
|
| web::WebThread::IO, FROM_HERE, base::BindBlock(^{
|
| // WeakNSObject does not work across different threads, hence this block
|
| // retains self.
|
| @@ -252,8 +322,14 @@ class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> {
|
| params.flags = self.certVerifyFlags;
|
| params.crl_set = net::SSLConfigService::GetCRLSet();
|
| // OCSP response is not provided by iOS API.
|
| - _certVerifier->Verify(params, completionHandler);
|
| + _certVerifier->Verify(params, ^(net::CertVerifyResult result, int) {
|
| + completionHandler(result, YES);
|
| + });
|
| }));
|
| +
|
| + if (!dispatched) {
|
| + completionHandler(net::CertVerifyResult(), NO);
|
| + }
|
| }
|
|
|
| - (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust
|
| @@ -274,4 +350,34 @@ class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> {
|
| }
|
| }
|
|
|
| +- (web::CertAcceptPolicy)
|
| + loadPolicyForBadTrustResult:(SecTrustResultType)trustResult
|
| + certVerifierResult:(net::CertVerifyResult)certVerifierResult
|
| + serverTrust:(SecTrustRef)trust
|
| + host:(NSString*)host {
|
| + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
|
| + DCHECK_NE(web::SECURITY_STYLE_AUTHENTICATED,
|
| + web::GetSecurityStyleFromTrustResult(trustResult));
|
| +
|
| + if (trustResult != kSecTrustResultRecoverableTrustFailure ||
|
| + SecTrustGetCertificateCount(trust) == 0) {
|
| + // Trust result is not recoverable or leaf cert is missing.
|
| + return web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR;
|
| + }
|
| +
|
| + // Check if user has decided to proceed with this bad cert.
|
| + scoped_refptr<net::X509Certificate> leafCert =
|
| + net::X509Certificate::CreateFromHandle(
|
| + SecTrustGetCertificateAtIndex(trust, 0),
|
| + net::X509Certificate::OSCertHandles());
|
| +
|
| + web::CertPolicy::Judgment judgment = _certPolicyCache->QueryPolicy(
|
| + leafCert.get(), base::SysNSStringToUTF8(host),
|
| + certVerifierResult.cert_status);
|
| +
|
| + return (judgment == web::CertPolicy::ALLOWED)
|
| + ? web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_ACCEPTED_BY_USER
|
| + : web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_UNDECIDED_BY_USER;
|
| +}
|
| +
|
| @end
|
|
|