| 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 2ca1bd42aac4ae2fef4fdb64ade3f7b0f1bb3ebd..ba4a08bfaaa3747c2bd7c7f92010e773b0db0099 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
|
| @@ -6,6 +6,7 @@
|
|
|
| #import <WebKit/WebKit.h>
|
|
|
| +#include "base/containers/mru_cache.h"
|
| #include "base/ios/ios_util.h"
|
| #include "base/ios/weak_nsobject.h"
|
| #include "base/json/json_reader.h"
|
| @@ -20,6 +21,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_host_pair.h"
|
| #import "ios/web/net/crw_cert_verification_controller.h"
|
| #include "ios/web/public/cert_store.h"
|
| #include "ios/web/public/navigation_item.h"
|
| @@ -43,12 +45,33 @@
|
| #import "ios/web/web_state/web_view_internal_creation_util.h"
|
| #import "ios/web/web_state/wk_web_view_security_util.h"
|
| #import "ios/web/webui/crw_web_ui_manager.h"
|
| -#include "net/cert/x509_certificate.h"
|
| #import "net/base/mac/url_conversions.h"
|
| +#include "net/cert/x509_certificate.h"
|
| #include "net/ssl/ssl_info.h"
|
| #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 {
|
| + CertVerificationError(BOOL is_recoverable, net::CertStatus status)
|
| + : is_recoverable(is_recoverable), status(status) {}
|
| +
|
| + BOOL is_recoverable;
|
| + net::CertStatus status;
|
| +};
|
| +
|
| +// Type of Cache object for storing cert verification errors.
|
| +typedef base::MRUCache<web::CertHostPair, CertVerificationError>
|
| + CertVerificationErrorsCacheType;
|
| +
|
| +// Maximum number of errors to store in cert verification errors cache.
|
| +// Cache holds errors only for pending navigations, so the actual number of
|
| +// stored errors is not expected to be high.
|
| +const CertVerificationErrorsCacheType::size_type kMaxCertErrorsCount = 100;
|
| +
|
| // Extracts Referer value from WKNavigationAction request header.
|
| NSString* GetRefererFromNavigationAction(WKNavigationAction* action) {
|
| return [action.request valueForHTTPHeaderField:@"Referer"];
|
| @@ -144,6 +167,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.
|
| + scoped_ptr<CertVerificationErrorsCacheType> _certVerificationErrors;
|
| }
|
|
|
| // Response's MIME type of the last known navigation.
|
| @@ -271,6 +301,14 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
|
| - (void)updateSSLStatusForCurrentNavigationItem;
|
| #endif
|
|
|
| +// Used in webView:didReceiveAuthenticationChallenge:completionHandler: to reply
|
| +// with NSURLSessionAuthChallengeDisposition and credentials.
|
| +- (void)processAuthChallenge:(NSURLAuthenticationChallenge*)challenge
|
| + forCertAcceptPolicy:(web::CertAcceptPolicy)policy
|
| + certStatus:(net::CertStatus)certStatus
|
| + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition,
|
| + NSURLCredential*))completionHandler;
|
| +
|
| // Registers load request with empty referrer and link or client redirect
|
| // transition based on user interaction state.
|
| - (void)registerLoadRequest:(const GURL&)url;
|
| @@ -320,6 +358,8 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
|
| if (self) {
|
| _certVerificationController.reset([[CRWCertVerificationController alloc]
|
| initWithBrowserState:browserState]);
|
| + _certVerificationErrors.reset(
|
| + new CertVerificationErrorsCacheType(kMaxCertErrorsCount));
|
| }
|
| return self;
|
| }
|
| @@ -566,6 +606,7 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
|
|
|
| - (void)abortWebLoad {
|
| [_wkWebView stopLoading];
|
| + _certVerificationErrors->Clear();
|
| }
|
|
|
| - (void)resetLoadState {
|
| @@ -864,19 +905,54 @@ 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);
|
| + net::SSLInfo info;
|
| + web::GetSSLInfoFromWKWebViewSSLCertError(error, &info);
|
| +
|
| + web::SSLStatus status;
|
| + status.security_style = web::SECURITY_STYLE_AUTHENTICATION_BROKEN;
|
| + status.cert_status = info.cert_status;
|
| + status.cert_id = web::CertStore::GetInstance()->StoreCert(
|
| + info.cert.get(), self.certGroupID);
|
| +
|
| + // Retrieve verification results from _certVerificationErrors cache to avoid
|
| + // unnecessary recalculations. Verification results are cached for the leaf
|
| + // cert, because the cert chain in |didReceiveAuthenticationChallenge:| is
|
| + // the OS constructed chain, while |chain| is the chain from the server.
|
| + NSArray* chain = error.userInfo[web::kNSErrorPeerCertificateChainKey];
|
| + NSString* host = [error.userInfo[web::kNSErrorFailingURLKey] host];
|
| + scoped_refptr<net::X509Certificate> leafCert;
|
| + BOOL recoverable = NO;
|
| + if (chain.count && host.length) {
|
| + // The complete cert chain may not be available, so the leaf cert is used
|
| + // as a key to retrieve _certVerificationErrors, as well as for storing the
|
| + // cert decision.
|
| + leafCert = web::CreateCertFromChain(@[ chain.firstObject ]);
|
| + if (leafCert) {
|
| + auto error = _certVerificationErrors->Get(
|
| + {leafCert, base::SysNSStringToUTF8(host)});
|
| + if (error != _certVerificationErrors->end()) {
|
| + status.cert_status = error->second.status;
|
| + recoverable = error->second.is_recoverable;
|
| + } else {
|
| + // TODO(eugenebut): Report UMA with cache size (crbug.com/541736).
|
| + }
|
| + }
|
| + }
|
|
|
| - [self.delegate presentSSLError:sslInfo
|
| - forSSLStatus:sslStatus
|
| - recoverable:NO
|
| - callback:nullptr];
|
| + // Present SSL interstitial.
|
| + [self.delegate presentSSLError:info
|
| + forSSLStatus:status
|
| + recoverable:recoverable
|
| + callback:^(BOOL proceed) {
|
| + if (proceed) {
|
| + // The interstitial will be removed during reload.
|
| + [_certVerificationController
|
| + allowCert:leafCert
|
| + forHost:host
|
| + status:status.cert_status];
|
| + [self loadCurrentURL];
|
| + }
|
| + }];
|
| }
|
| #endif // #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
|
|
|
| @@ -1019,6 +1095,44 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
|
|
|
| #endif // !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
|
|
|
| +- (void)processAuthChallenge:(NSURLAuthenticationChallenge*)challenge
|
| + forCertAcceptPolicy:(web::CertAcceptPolicy)policy
|
| + certStatus:(net::CertStatus)certStatus
|
| + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition,
|
| + NSURLCredential*))completionHandler {
|
| + SecTrustRef trust = challenge.protectionSpace.serverTrust;
|
| + if (policy == web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_ACCEPTED_BY_USER) {
|
| + // Cert is invalid, but user agreed to proceed, override default behavior.
|
| + completionHandler(NSURLSessionAuthChallengeUseCredential,
|
| + [NSURLCredential credentialForTrust:trust]);
|
| + return;
|
| + }
|
| +
|
| + if (policy != web::CERT_ACCEPT_POLICY_ALLOW &&
|
| + SecTrustGetCertificateCount(trust)) {
|
| + // The cert is invalid and the user has not agreed to proceed. Cache the
|
| + // cert verification result in |_certVerificationErrors|, so that it can
|
| + // later be reused inside |didFailProvisionalNavigation:|.
|
| + // The leaf cert is used as the key, because the chain provided by
|
| + // |didFailProvisionalNavigation:| will differ (it is the server-supplied
|
| + // chain), thus if intermediates were considered, the keys would mismatch.
|
| + scoped_refptr<net::X509Certificate> leafCert =
|
| + net::X509Certificate::CreateFromHandle(
|
| + SecTrustGetCertificateAtIndex(trust, 0),
|
| + net::X509Certificate::OSCertHandles());
|
| + if (leafCert) {
|
| + BOOL is_recoverable =
|
| + policy == web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_UNDECIDED_BY_USER;
|
| + std::string host =
|
| + base::SysNSStringToUTF8(challenge.protectionSpace.host);
|
| + _certVerificationErrors->Put(
|
| + web::CertHostPair(leafCert, host),
|
| + CertVerificationError(is_recoverable, certStatus));
|
| + }
|
| + }
|
| + completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
|
| +}
|
| +
|
| - (void)registerLoadRequest:(const GURL&)url {
|
| // If load request is registered via WKWebViewWebController, assume transition
|
| // is link or client redirect as other transitions will already be registered
|
| @@ -1431,11 +1545,13 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
|
| [self handleLoadError:error inMainFrame:YES];
|
|
|
| [self discardPendingNavigationTypeForMainFrame];
|
| + _certVerificationErrors->Clear();
|
| }
|
|
|
| - (void)webView:(WKWebView *)webView
|
| didCommitNavigation:(WKNavigation *)navigation {
|
| DCHECK_EQ(_wkWebView, webView);
|
| + _certVerificationErrors->Clear();
|
| // 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]);
|
| @@ -1481,13 +1597,14 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
|
| withError:(NSError *)error {
|
| [self handleLoadError:WKWebViewErrorWithSource(error, NAVIGATION)
|
| inMainFrame:YES];
|
| + _certVerificationErrors->Clear();
|
| }
|
|
|
| -- (void)webView:(WKWebView *)webView
|
| - didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
|
| +- (void)webView:(WKWebView*)webView
|
| + didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge
|
| completionHandler:
|
| - (void (^)(NSURLSessionAuthChallengeDisposition disposition,
|
| - NSURLCredential *credential))completionHandler {
|
| + (void (^)(NSURLSessionAuthChallengeDisposition,
|
| + NSURLCredential*))completionHandler {
|
| if (![challenge.protectionSpace.authenticationMethod
|
| isEqual:NSURLAuthenticationMethodServerTrust]) {
|
| completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
|
| @@ -1495,19 +1612,25 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
|
| }
|
|
|
| SecTrustRef trust = challenge.protectionSpace.serverTrust;
|
| - scoped_refptr<net::X509Certificate> cert = web::CreateCertFromTrust(trust);
|
| - // TODO(eugenebut): pass SecTrustRef instead of cert.
|
| + base::ScopedCFTypeRef<SecTrustRef> scopedTrust(trust,
|
| + base::scoped_policy::RETAIN);
|
| + base::WeakNSObject<CRWWKWebViewWebController> weakSelf(self);
|
| [_certVerificationController
|
| - decidePolicyForCert:cert
|
| - host:challenge.protectionSpace.host
|
| - completionHandler:^(web::CertAcceptPolicy policy,
|
| - net::CertStatus status) {
|
| - completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace,
|
| - nil);
|
| - }];
|
| + decideLoadPolicyForTrust:scopedTrust
|
| + host:challenge.protectionSpace.host
|
| + completionHandler:^(web::CertAcceptPolicy policy,
|
| + net::CertStatus status) {
|
| + base::scoped_nsobject<CRWWKWebViewWebController> strongSelf(
|
| + [weakSelf retain]);
|
| + [strongSelf processAuthChallenge:challenge
|
| + forCertAcceptPolicy:policy
|
| + certStatus:status
|
| + completionHandler:completionHandler];
|
| + }];
|
| }
|
|
|
| - (void)webViewWebContentProcessDidTerminate:(WKWebView*)webView {
|
| + _certVerificationErrors->Clear();
|
| [self webViewWebProcessDidCrash];
|
| }
|
|
|
|
|