Chromium Code Reviews| 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 ad478ae55b1a963ceb49532c69bc97479bd75d99..72f1f02978708c0424dffa51a89ec4c5032b4550 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" |
| @@ -60,6 +61,9 @@ class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> { |
| } // namespace |
| +using web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_ACCEPTED_BY_USER; |
| +using web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_NOT_ACCEPTED_BY_USER; |
| + |
| @interface CRWCertVerificationController () { |
| // Cert verification object which wraps |net::CertVerifier|. Must be created, |
| // used and destroyed on IO Thread. |
| @@ -67,6 +71,9 @@ class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> { |
| // URLRequestContextGetter for obtaining net layer objects. |
| net::URLRequestContextGetter* _contextGetter; |
| + |
| + // Used to remember decisions about how to handle problematic certs. |
| + scoped_refptr<web::CertificatePolicyCache> _certPolicyCache; |
| } |
| // Cert verification flags. Must be used on IO Thread. |
| @@ -77,10 +84,11 @@ class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> { |
| // Verifies the given |cert| for the given |host| using |net::CertVerifier| and |
| // calls |completionHandler| on completion. |completionHandler| cannot be null |
| -// and will be called asynchronously on IO thread. |
| +// and will be called asynchronously on IO thread or synchronously on current |
| +// thread if IO task can't start (in this case |success| 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 success))handler; |
| // Verifies the given |trust| using SecTrustRef API. |completionHandler| cannot |
| // be null and will be either called asynchronously on Worker thread or |
| @@ -114,14 +122,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 |
| @@ -131,22 +142,71 @@ 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 success) { |
| + if (!success) { |
| + 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 sucess) { |
|
stuartmorgan
2015/10/09 21:30:00
Consider pulling everything in here into a helper
Eugene But (OOO till 7-30)
2015/10/12 18:19:40
Done.
|
| + if (!sucess) { |
| + dispatch_async(dispatch_get_main_queue(), ^{ |
| + handlerHolder->call( |
| + web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR, |
| + net::CertStatus()); |
| + }); |
| + return; |
| + } |
| + |
| + web::CertAcceptPolicy policy = |
| + web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR; |
| + if (trustResult == kSecTrustResultRecoverableTrustFailure && |
| + SecTrustGetCertificateCount(trust)) { |
| + // Check if user has decided to proceed with this bad cert. |
| + scoped_refptr<net::X509Certificate> leafCert = |
| + web::CreateCertFromChain(@[ |
| + static_cast<id>(SecTrustGetCertificateAtIndex(trust, 0)) |
| + ]); |
| + |
| + web::CertPolicy::Judgment judgment = |
| + _certPolicyCache->QueryPolicy( |
| + leafCert.get(), base::SysNSStringToUTF8(host), |
| + certVerifierResult.cert_status); |
| + |
| + if (judgment == web::CertPolicy::ALLOWED) { |
| + policy = |
| + CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_ACCEPTED_BY_USER; |
| + } else { |
| + policy = |
| + CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_NOT_ACCEPTED_BY_USER; |
| + } |
| + } |
| + |
| + dispatch_async(dispatch_get_main_queue(), ^{ |
| + handlerHolder->call(policy, certVerifierResult.cert_status); |
| + }); |
| + }]; |
| }]; |
| } |
| @@ -188,7 +248,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); |
| @@ -197,6 +258,24 @@ 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 decision only for leaf server cert, and drop any intermediates. |
| + // This is a workaround for WKWebView behavior, where different delegate |
| + // methods receive different certificate chains for the same server cert. |
| + if (cert->GetIntermediateCertificates().size() > 0) { |
| + cert = net::X509Certificate::CreateFromHandle( |
| + cert->os_cert_handle(), net::X509Certificate::OSCertHandles()); |
| + } |
| + DCHECK_EQ(0U, cert->GetIntermediateCertificates().size()); |
| + 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(^{ |
| @@ -232,10 +311,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 started = web::WebThread::PostTask( |
| web::WebThread::IO, FROM_HERE, base::BindBlock(^{ |
| // WeakNSObject does not work across different threads, hence this block |
| // retains self. |
| @@ -249,8 +328,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 (!started) { |
| + completionHandler(net::CertVerifyResult(), NO); |
| + } |
| } |
| - (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust |
| @@ -258,7 +343,7 @@ class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> { |
| DCHECK(completionHandler); |
| // SecTrustEvaluate performs trust evaluation synchronously, possibly making |
| // network requests. The UI thread should not be blocked by that operation. |
| - bool result = base::WorkerPool::PostTask(FROM_HERE, base::BindBlock(^{ |
| + bool started = base::WorkerPool::PostTask(FROM_HERE, base::BindBlock(^{ |
| SecTrustResultType trustResult = kSecTrustResultInvalid; |
| if (SecTrustEvaluate(trust.get(), &trustResult) != errSecSuccess) { |
| trustResult = kSecTrustResultInvalid; |
| @@ -266,7 +351,7 @@ class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> { |
| completionHandler(trustResult, YES); |
| }), false /* task_is_slow */); |
| - if (!result) { |
| + if (!started) { |
| completionHandler(kSecTrustResultInvalid, NO); |
| } |
| } |