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