Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(589)

Unified Diff: ios/web/net/crw_cert_verification_controller.mm

Issue 1357773002: WKWebView: Implemented recoverable SSL interstitials. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@lock_coloring
Patch Set: Addressed unit tests review comments Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
« no previous file with comments | « ios/web/net/crw_cert_verification_controller.h ('k') | ios/web/net/crw_cert_verification_controller_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698