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 6c0dd0beb4b8a5be59d34a9928ec5e056ed6af91..55a34c1127bfeb938941194bc732332b7416e0d9 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. |
@@ -114,14 +121,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,23 +141,48 @@ 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) { |
+ 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; |
+ } |
- dispatch_async(dispatch_get_main_queue(), ^{ |
- handlerHolder->call(policy, result.cert_status); |
- }); |
- }]; |
+ // 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; |
+ if (trustResult == kSecTrustResultRecoverableTrustFailure && |
+ SecTrustGetCertificateCount(trust)) { |
+ // Check if user has decided to proceed with this bad cert. |
+ scoped_refptr<net::X509Certificate> leafCert = |
stuartmorgan
2015/10/08 16:52:39
Fix the indentation of this statement. (Is this a
Eugene But (OOO till 7-30)
2015/10/09 16:32:36
Fixed. This probably was not clang format bug.
|
+ 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)querySSLStatusForTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust |
@@ -194,6 +229,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(^{ |