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 { |