Chromium Code Reviews| 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 { |