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

Unified Diff: ios/web/web_state/ui/crw_wk_web_view_web_controller.mm

Issue 1357773002: WKWebView: Implemented recoverable SSL interstitials. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@lock_coloring
Patch Set: Merged with parent CL Created 5 years, 3 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/web_state/ui/crw_wk_web_view_web_controller.mm
diff --git a/ios/web/web_state/ui/crw_wk_web_view_web_controller.mm b/ios/web/web_state/ui/crw_wk_web_view_web_controller.mm
index 3fa37486e966124d6400afbd3b69a440c5e7b45e..393afaaea4671f1f29c208a2fd695a4218d9319a 100644
--- a/ios/web/web_state/ui/crw_wk_web_view_web_controller.mm
+++ b/ios/web/web_state/ui/crw_wk_web_view_web_controller.mm
@@ -19,6 +19,7 @@
#import "ios/web/navigation/crw_session_entry.h"
#include "ios/web/navigation/navigation_item_impl.h"
#include "ios/web/navigation/web_load_params.h"
+#include "ios/web/net/cert_verification_cache.h"
#import "ios/web/net/crw_cert_verification_controller.h"
#include "ios/web/public/cert_store.h"
#include "ios/web/public/navigation_item.h"
@@ -48,6 +49,15 @@
#include "url/url_constants.h"
namespace {
+
+// Represents cert verification error, which happened inside
+// |webView:didReceiveAuthenticationChallenge:completionHandler:| and should
+// be checked inside |webView:didFailProvisionalNavigation:withError:|.
+struct CertVerificationError {
+ BOOL is_recoverable;
+ net::CertStatus status;
+};
+
// Extracts Referer value from WKNavigationAction request header.
NSString* GetRefererFromNavigationAction(WKNavigationAction* action) {
return [action.request valueForHTTPHeaderField:@"Referer"];
@@ -145,6 +155,13 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
// Cancelled navigations should be simply discarded without handling any
// specific error.
BOOL _pendingNavigationCancelled;
+
+ // CertVerification errors which happened inside
+ // |webView:didReceiveAuthenticationChallenge:completionHandler:|.
+ // Key is leaf-cert - host pair. This storage is used to carry calculated
+ // cert status from |didReceiveAuthenticationChallenge:| to
+ // |didFailProvisionalNavigation:| delegate method.
+ web::CertVerificationCache<CertVerificationError> _certVerificationErrors;
}
// Response's MIME type of the last known navigation.
@@ -557,6 +574,7 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
- (void)abortWebLoad {
[_wkWebView stopLoading];
+ _certVerificationErrors.reset();
}
- (void)resetLoadState {
@@ -843,19 +861,49 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
- (void)handleSSLError:(NSError*)error {
DCHECK(web::IsWKWebViewSSLError(error));
- net::SSLInfo sslInfo;
- web::GetSSLInfoFromWKWebViewSSLError(error, &sslInfo);
-
- web::SSLStatus sslStatus;
- sslStatus.security_style = web::SECURITY_STYLE_AUTHENTICATION_BROKEN;
- sslStatus.cert_status = sslInfo.cert_status;
- sslStatus.cert_id = web::CertStore::GetInstance()->StoreCert(
- sslInfo.cert.get(), self.certGroupID);
-
- [self.delegate presentSSLError:sslInfo
- forSSLStatus:sslStatus
- recoverable:NO
- callback:nullptr];
+ net::SSLInfo SSLInfo;
+ web::GetSSLInfoFromWKWebViewSSLError(error, &SSLInfo);
+
+ web::SSLStatus SSLStatus;
+ SSLStatus.security_style = web::SECURITY_STYLE_AUTHENTICATION_BROKEN;
+ SSLStatus.cert_status = SSLInfo.cert_status;
+ SSLStatus.cert_id = web::CertStore::GetInstance()->StoreCert(
+ SSLInfo.cert.get(), self.certGroupID);
+
+ NSArray* chain = error.userInfo[web::kNSErrorPeerCertificateChainKey];
+ NSString* host = [error.userInfo[web::kNSErrorFailingURLKey] host];
+ // Verification results are cached for leaf cert, because cert chain in
+ // |didReceiveAuthenticationChallenge:| maybe different from |chain|.
+ scoped_refptr<net::X509Certificate> leafCert;
+ BOOL recoverable = NO;
+ if (chain.count && host.length) {
+ // Complete cert chain may not be available inside this method, so leaf
+ // cert is used as a key to retrieve _certVerificationErrors as well as for
+ // storing cert decision.
+ leafCert = web::CreateCertFromChain(@[ [chain firstObject] ]);
+ if (leafCert) {
+ CertVerificationError error;
+ if (_certVerificationErrors.get(leafCert, base::SysNSStringToUTF8(host),
+ &error)) {
+ SSLStatus.cert_status = error.status;
+ recoverable = error.is_recoverable;
+ }
+ }
+ }
+ [self.delegate presentSSLError:SSLInfo
+ forSSLStatus:SSLStatus
+ recoverable:recoverable
+ callback:^(BOOL proceed) {
+ if (proceed) {
+ // The interstitial will be removed during reload.
+ [_certVerificationController
stuartmorgan 2015/09/23 17:36:09 Are we okay with a strong reference to self here?
Eugene But (OOO till 7-30) 2015/09/23 20:40:47 Yes, displayed interstitial outlives web controlle
+ allowCert:leafCert
+ forHost:host
+ status:SSLStatus.cert_status];
+ [self loadCurrentURL];
+ }
+ }];
+ _certVerificationErrors.reset();
}
#endif // #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
@@ -1398,6 +1446,7 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
- (void)webView:(WKWebView *)webView
didCommitNavigation:(WKNavigation *)navigation {
DCHECK_EQ(_wkWebView, webView);
+ _certVerificationErrors.reset();
// This point should closely approximate the document object change, so reset
// the list of injected scripts to those that are automatically injected.
_injectedScriptManagers.reset([[NSMutableSet alloc] init]);
@@ -1448,15 +1497,46 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
}
SecTrustRef trust = challenge.protectionSpace.serverTrust;
- scoped_refptr<net::X509Certificate> cert = web::CreateCertFromTrust(trust);
+ id handler = ^(web::CertAcceptPolicy policy, net::CertStatus status) {
+ if (policy == web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_ACCEPTED_BY_USER) {
+ // cert is invalid, but user agreed to proceed.
+ completionHandler(NSURLSessionAuthChallengeUseCredential,
+ [NSURLCredential credentialForTrust:trust]);
+ } else {
+ if (policy != web::CERT_ACCEPT_POLICY_ALLOW) {
+ // cert is invalid and user has not agreed to proceed.
+
+ // Cache cert verification result with _certVerificationErrors storage,
+ // so it can be later reused inside |didFailProvisionalNavigation:|.
+ // didFailProvisionalNavigation: does not receive full cert chain and it
+ // will not be possible to resulculate cert status there.
+ if (SecTrustGetCertificateCount(trust)) {
+ // Leaf cert (w/o any intermidiates) is used for caching.
+ // Chain inside |didFailProvisionalNavigation:| may be different and
+ // using intermidiates will result in keys mismatch.
+ scoped_refptr<net::X509Certificate> leafCert =
+ web::CreateCertFromChain(@[
+ static_cast<id>(SecTrustGetCertificateAtIndex(trust, 0))
+ ]);
+ if (leafCert) {
+ BOOL is_recoverable =
+ policy ==
+ web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_NOT_ACCEPTED_BY_USER;
+ _certVerificationErrors.set(
+ leafCert,
+ base::SysNSStringToUTF8(challenge.protectionSpace.host),
+ {is_recoverable, status});
+ }
+ }
+ }
+ completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
+ }
+ };
+
[_certVerificationController
- decidePolicyForCert:cert
- host:challenge.protectionSpace.host
- completionHandler:^(web::CertAcceptPolicy policy,
- net::CertStatus status) {
- completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace,
- nil);
- }];
+ decideLoadPolicyForTrust:trust
+ host:challenge.protectionSpace.host
+ completionHandler:handler];
stuartmorgan 2015/09/23 17:36:09 Same question here; what's the ownership/lifetime
Eugene But (OOO till 7-30) 2015/09/23 20:40:48 Good catch. CRWCertVerificationController has -clo
}
- (void)webViewWebContentProcessDidTerminate:(WKWebView*)webView {

Powered by Google App Engine
This is Rietveld 408576698