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

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 origin/master 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/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 b5c930d4e547e2030eb5a5ecc6d87ddbca0cd79d..78649e610c41cc5af2fec0d183328289b58ace6c 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
@@ -20,6 +20,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"
@@ -49,6 +50,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"];
@@ -154,6 +164,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.
@@ -570,6 +587,7 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
- (void)abortWebLoad {
[_wkWebView stopLoading];
+ _certVerificationErrors.reset();
}
- (void)resetLoadState {
@@ -856,19 +874,48 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
- (void)handleSSLCertError:(NSError*)error {
DCHECK(web::IsWKWebViewSSLCertError(error));
- net::SSLInfo sslInfo;
- web::GetSSLInfoFromWKWebViewSSLCertError(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::GetSSLInfoFromWKWebViewSSLCertError(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),
stuartmorgan 2015/10/08 16:52:39 Why |if| rather than a DCHECK? I thought the advan
Eugene But (OOO till 7-30) 2015/10/09 16:32:36 I don't want to assert that this leaf cert has bee
stuartmorgan 2015/10/09 21:30:00 Yes, UMA sounds good. If this is anything but incr
Eugene But (OOO till 7-30) 2015/10/12 18:19:40 Added TODO
+ &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
+ allowCert:leafCert
+ forHost:host
+ status:SSLStatus.cert_status];
+ [self loadCurrentURL];
stuartmorgan 2015/10/08 16:52:39 There is (currently; it needs to move into NM) spe
Eugene But (OOO till 7-30) 2015/10/09 16:32:36 Are you sure? How is this case different form UIWe
+ }
+ }];
}
#endif // #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
@@ -1420,11 +1467,13 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
[self handleLoadError:error inMainFrame:YES];
[self discardPendingNavigationTypeForMainFrame];
+ _certVerificationErrors.reset();
}
- (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]);
@@ -1461,6 +1510,7 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
withError:(NSError *)error {
[self handleLoadError:WKWebViewErrorWithSource(error, NAVIGATION)
inMainFrame:YES];
+ _certVerificationErrors.reset();
}
- (void)webView:(WKWebView *)webView
@@ -1475,15 +1525,53 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
}
SecTrustRef trust = challenge.protectionSpace.serverTrust;
- scoped_refptr<net::X509Certificate> cert = web::CreateCertFromTrust(trust);
+ base::WeakNSObject<CRWWKWebViewWebController> weakSelf(self);
+ id handler = ^(web::CertAcceptPolicy policy, net::CertStatus status) {
+ base::scoped_nsobject<CRWWKWebViewWebController> strongSelf(
+ [weakSelf retain]);
+ if (!strongSelf) {
+ return;
+ }
stuartmorgan 2015/10/08 16:52:39 Per my note in the other CL, I'd prefer everything
Eugene But (OOO till 7-30) 2015/10/09 16:32:36 Done.
+
+ if (policy == web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_ACCEPTED_BY_USER) {
+ // cert is invalid, but user agreed to proceed.
+ completionHandler(NSURLSessionAuthChallengeUseCredential,
+ [NSURLCredential credentialForTrust:trust]);
+ return;
+ }
+
+ 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;
+ strongSelf.get()->_certVerificationErrors.Set(
+ leafCert, base::SysNSStringToUTF8(challenge.protectionSpace.host),
+ {is_recoverable, status});
+ }
+ }
+ }
+ completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
+ };
+
+ base::ScopedCFTypeRef<SecTrustRef> scopedTrust(trust,
+ base::scoped_policy::RETAIN);
[_certVerificationController
- decidePolicyForCert:cert
- host:challenge.protectionSpace.host
- completionHandler:^(web::CertAcceptPolicy policy,
- net::CertStatus status) {
- completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace,
- nil);
- }];
+ decideLoadPolicyForTrust:scopedTrust
+ host:challenge.protectionSpace.host
+ completionHandler:handler];
}
- (void)webViewWebContentProcessDidTerminate:(WKWebView*)webView {

Powered by Google App Engine
This is Rietveld 408576698