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 a2ac38e4ee041708f60d2fd585574a1a700946d0..557626a6595923ba446ea825c7a6d148cdcc830b 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. |
@@ -113,14 +120,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:(SecTrustRef)serverTrust |
+ 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 |
@@ -130,23 +140,52 @@ 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; |
- } |
+ web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{ |
+ base::ScopedCFTypeRef<SecTrustRef> trust( |
+ serverTrust, base::scoped_policy::RETAIN); |
+ [self verifyTrust:trust completionHandler:^(SecTrustResultType result) { |
+ if (result == kSecTrustResultProceed || |
+ result == kSecTrustResultUnspecified) { |
+ // SecTrust API considers this cert as valid. |
dispatch_async(dispatch_get_main_queue(), ^{ |
- handlerHolder->call(policy, result.cert_status); |
+ 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, int) { |
+ web::CertAcceptPolicy policy = |
+ web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR; |
+ scoped_refptr<net::X509Certificate> leafCert; |
+ if (result == kSecTrustResultRecoverableTrustFailure && |
+ SecTrustGetCertificateCount(trust)) { |
+ // Check if user has decided to proceed with this bad cert. |
+ leafCert = web::CreateCertFromChain(@[ |
+ static_cast<id>(SecTrustGetCertificateAtIndex(trust, 0)) |
+ ]); |
+ |
+ web::CertPolicy::Judgment judgment = |
+ _certPolicyCache->QueryPolicy( |
+ leafCert.get(), base::SysNSStringToUTF8(host), |
+ certVerifierResult.cert_status); |
+ |
+ policy = (judgment == web::CertPolicy::ALLOWED) |
+ ? CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_ACCEPTED_BY_USER |
+ : CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_NOT_ACCEPTED_BY_USER; |
+ } |
+ |
+ dispatch_async(dispatch_get_main_queue(), ^{ |
+ handlerHolder->call(policy, certVerifierResult.cert_status); |
+ }); |
+ }]; |
+ }]; |
+ })); |
} |
- (void)querySSLStatusForCertChain:(NSArray*)certChain |
@@ -160,36 +199,53 @@ class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> { |
// released on background thread and |BlockHolder| ensures that. |
__block scoped_refptr<BlockHolder<web::StatusQueryHandler>> handlerHolder( |
new BlockHolder<web::StatusQueryHandler>(completionHandler)); |
- scoped_refptr<net::X509Certificate> cert(web::CreateCertFromChain(certChain)); |
- // Retrieve the net::CertStatus to allow Chromium-specific policies to be |
- // enacted. This is called for all certificates (valid and invalid alike), |
- // since the net::CertVerifier may reject or downgrade certificates that |
- // SecTrust accepts. |
- [self verifyCert:cert |
- forHost:host |
- completionHandler:^(net::CertVerifyResult certVerifierResult, int) { |
- [self verifyTrust:web::CreateServerTrustFromChain(certChain, host) |
- completionHandler:^(SecTrustResultType trustResult) { |
- // If SecTrust has accepted a certificate, clear any error |
- // statuses returned by net::CertVerifier, since the CertVerifier |
- // may not have access to the same trust information |
- // (e.g. custom-installed roots). Note, this also disables |
- // net::CertVerifier's ability to reject certificates, such as via |
- // CRLSets. However, copy any supplemental status bits over, since |
- // those may be used as part of policy logic, such as deprecating |
- // SHA-1 or requiring Certificate Transparency. |
- web::SecurityStyle security_style = |
- web::GetSecurityStyleFromTrustResult(trustResult); |
- net::CertStatus cert_status = certVerifierResult.cert_status; |
- if (security_style == web::SECURITY_STYLE_AUTHENTICATED) { |
- cert_status &= ~net::CERT_STATUS_ALL_ERRORS; |
- } |
- |
- dispatch_async(dispatch_get_main_queue(), ^{ |
- handlerHolder->call(security_style, cert_status); |
- }); |
- }]; |
- }]; |
+ web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{ |
+ scoped_refptr<net::X509Certificate> cert( |
+ web::CreateCertFromChain(certChain)); |
+ // Retrieve the net::CertStatus to allow Chromium-specific policies to |
+ // be enacted. This is called for all certificates (valid and invalid |
+ // alike), since the net::CertVerifier may reject or downgrade certificates |
+ // that SecTrust accepts. |
+ [self verifyCert:cert |
+ forHost:host |
+ completionHandler:^(net::CertVerifyResult certVerifierResult, int) { |
+ [self verifyTrust:web::CreateServerTrustFromChain(certChain, host) |
+ completionHandler:^(SecTrustResultType trustResult) { |
+ // If SecTrust has accepted a certificate, clear any error |
+ // statuses returned by net::CertVerifier, since the |
+ // CertVerifier may not have access to the same trust |
+ // information (e.g. custom-installed roots). Note, this also |
+ // disables net::CertVerifier's ability to reject certificates, |
+ // such as via CRLSets. However, copy any supplemental status |
+ // bits over, since those may be used as part of policy logic, |
+ // such as deprecating SHA-1 or requiring Certificate |
+ // Transparency. |
+ web::SecurityStyle security_style = |
+ web::GetSecurityStyleFromTrustResult(trustResult); |
+ net::CertStatus cert_status = |
+ certVerifierResult.cert_status; |
+ if (security_style == web::SECURITY_STYLE_AUTHENTICATED) { |
+ cert_status &= ~net::CERT_STATUS_ALL_ERRORS; |
+ } |
+ |
+ dispatch_async(dispatch_get_main_queue(), ^{ |
+ handlerHolder->call(security_style, cert_status); |
+ }); |
+ }]; |
+ }]; |
+ })); |
+} |
+ |
+- (void)allowCert:(scoped_refptr<net::X509Certificate>)cert |
+ forHost:(NSString*)host |
+ status:(net::CertStatus)status { |
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
+ DCHECK(cert); |
+ 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 { |
@@ -228,24 +284,19 @@ class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> { |
- (void)verifyCert:(const scoped_refptr<net::X509Certificate>&)cert |
forHost:(NSString*)host |
completionHandler:(void (^)(net::CertVerifyResult, int))completionHandler { |
+ DCHECK(web::WebThread::CurrentlyOn(web::WebThread::IO)); |
DCHECK(completionHandler); |
- __block scoped_refptr<net::X509Certificate> blockCert = cert; |
- web::WebThread::PostTask( |
- web::WebThread::IO, FROM_HERE, base::BindBlock(^{ |
- // WeakNSObject does not work across different threads, hence this block |
- // retains self. |
- if (!_certVerifier) { |
- completionHandler(net::CertVerifyResult(), net::ERR_FAILED); |
- return; |
- } |
- |
- web::CertVerifierBlockAdapter::Params params( |
- blockCert.Pass(), base::SysNSStringToUTF8(host)); |
- params.flags = self.certVerifyFlags; |
- params.crl_set = net::SSLConfigService::GetCRLSet(); |
- // OCSP response is not provided by iOS API. |
- _certVerifier->Verify(params, completionHandler); |
- })); |
+ if (!_certVerifier) { |
+ completionHandler(net::CertVerifyResult(), net::ERR_FAILED); |
+ return; |
+ } |
+ |
+ web::CertVerifierBlockAdapter::Params params(cert, |
+ base::SysNSStringToUTF8(host)); |
+ params.flags = self.certVerifyFlags; |
+ params.crl_set = net::SSLConfigService::GetCRLSet(); |
+ // OCSP response is not provided by iOS API. |
+ _certVerifier->Verify(params, completionHandler); |
} |
- (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust |