| 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 db9dc926aef7a0b74b3184f8e0e91e47587a689d..726c4770e6ed33293bcce2a3fec08142abbc13fc 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
|
| @@ -11,6 +11,7 @@
|
| #include "base/json/json_reader.h"
|
| #import "base/mac/scoped_nsobject.h"
|
| #include "base/macros.h"
|
| +#include "base/metrics/histogram_macros.h"
|
| #include "base/strings/sys_string_conversions.h"
|
| #include "base/values.h"
|
| #import "ios/net/http_response_headers_util.h"
|
| @@ -248,6 +249,21 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
|
| // Clears all activity indicator tasks for this web controller.
|
| - (void)clearActivityIndicatorTasks;
|
|
|
| +// Updates |security_style| and |cert_status| for the NavigationItem with ID
|
| +// |navigationItemID|, if URL and certificate chain still match |host| and
|
| +// |certChain|.
|
| +- (void)updateSSLStatusForNavigationItemWithID:(int)navigationItemID
|
| + certChain:(NSArray*)chain
|
| + host:(NSString*)host
|
| + withSecurityStyle:(web::SecurityStyle)style
|
| + certStatus:(net::CertStatus)certStatus;
|
| +
|
| +// Asynchronously obtains SSL status from given |certChain| and |host| and
|
| +// updates current navigation item. Before scheduling update changes SSLStatus'
|
| +// cert_status and security_style to default.
|
| +- (void)scheduleSSLStatusUpdateUsingCertChain:(NSArray*)chain
|
| + host:(NSString*)host;
|
| +
|
| #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
|
| // Updates SSL status for the current navigation item based on the information
|
| // provided by web view.
|
| @@ -867,47 +883,133 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
|
| clearNetworkTasksForGroup:[self activityIndicatorGroupID]];
|
| }
|
|
|
| +- (void)updateSSLStatusForNavigationItemWithID:(int)navigationItemID
|
| + certChain:(NSArray*)chain
|
| + host:(NSString*)host
|
| + withSecurityStyle:(web::SecurityStyle)style
|
| + certStatus:(net::CertStatus)certStatus {
|
| + web::NavigationManager* navigationManager =
|
| + self.webStateImpl->GetNavigationManager();
|
| +
|
| + // The searched item almost always be the last one, so walk backward rather
|
| + // than forward.
|
| + for (int i = navigationManager->GetEntryCount() - 1; 0 <= i; i--) {
|
| + web::NavigationItem* item = navigationManager->GetItemAtIndex(i);
|
| + if (item->GetUniqueID() != navigationItemID)
|
| + continue;
|
| +
|
| + // NavigationItem's UniqueID is preserved even after redirects, so
|
| + // checking that cert and URL match is necessary.
|
| + scoped_refptr<net::X509Certificate> cert(web::CreateCertFromChain(chain));
|
| + int certID =
|
| + web::CertStore::GetInstance()->StoreCert(cert.get(), self.certGroupID);
|
| + std::string GURLHost = base::SysNSStringToUTF8(host);
|
| + web::SSLStatus& SSLStatus = item->GetSSL();
|
| + if (SSLStatus.cert_id == certID && item->GetURL().host() == GURLHost) {
|
| + web::SSLStatus previousSSLStatus = item->GetSSL();
|
| + SSLStatus.cert_status = certStatus;
|
| + SSLStatus.security_style = style;
|
| + if (navigationManager->GetCurrentEntryIndex() == i &&
|
| + !previousSSLStatus.Equals(SSLStatus)) {
|
| + [self didUpdateSSLStatusForCurrentNavigationItem];
|
| + }
|
| + }
|
| + return;
|
| + }
|
| +}
|
| +
|
| +- (void)scheduleSSLStatusUpdateUsingCertChain:(NSArray*)chain
|
| + host:(NSString*)host {
|
| + // Use Navigation Item's unique ID to locate requested item after
|
| + // obtaining cert status asynchronously.
|
| + int itemID = self.webStateImpl->GetNavigationManager()
|
| + ->GetLastCommittedItem()
|
| + ->GetUniqueID();
|
| +
|
| + base::WeakNSObject<CRWWKWebViewWebController> weakSelf(self);
|
| + void (^SSLStatusResponse)(web::SecurityStyle, net::CertStatus) =
|
| + ^(web::SecurityStyle style, net::CertStatus certStatus) {
|
| + base::scoped_nsobject<CRWWKWebViewWebController> strongSelf(
|
| + [weakSelf retain]);
|
| + if (!strongSelf || [strongSelf isBeingDestroyed]) {
|
| + return;
|
| + }
|
| + [strongSelf updateSSLStatusForNavigationItemWithID:itemID
|
| + certChain:chain
|
| + host:host
|
| + withSecurityStyle:style
|
| + certStatus:certStatus];
|
| + };
|
| +
|
| + [_certVerificationController
|
| + querySSLStatusForTrust:web::CreateServerTrustFromChain(chain, host)
|
| + host:host
|
| + completionHandler:SSLStatusResponse];
|
| +}
|
| +
|
| #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
|
| +
|
| - (void)updateSSLStatusForCurrentNavigationItem {
|
| if ([self isBeingDestroyed])
|
| return;
|
|
|
| - DCHECK(self.webStateImpl);
|
| web::NavigationItem* item =
|
| self.webStateImpl->GetNavigationManagerImpl().GetLastCommittedItem();
|
| if (!item)
|
| return;
|
|
|
| web::SSLStatus previousSSLStatus = item->GetSSL();
|
| - web::SSLStatus& SSLStatus = item->GetSSL();
|
| +
|
| + // Starting from iOS9 WKWebView blocks active mixed content, so if
|
| + // |hasOnlySecureContent| returns NO it means passive content.
|
| + item->GetSSL().content_status =
|
| + [_wkWebView hasOnlySecureContent]
|
| + ? web::SSLStatus::NORMAL_CONTENT
|
| + : web::SSLStatus::DISPLAYED_INSECURE_CONTENT;
|
| +
|
| + // Try updating SSLStatus for current NavigationItem asynchronously.
|
| + scoped_refptr<net::X509Certificate> cert;
|
| if (item->GetURL().SchemeIsCryptographic()) {
|
| - // TODO(eugenebut): Do not set security style to authenticated once
|
| - // proceeding with bad ssl cert is implemented.
|
| - SSLStatus.security_style = web::SECURITY_STYLE_AUTHENTICATED;
|
| - SSLStatus.content_status = [_wkWebView hasOnlySecureContent]
|
| - ? web::SSLStatus::NORMAL_CONTENT
|
| - : web::SSLStatus::DISPLAYED_INSECURE_CONTENT;
|
| -
|
| - if (base::ios::IsRunningOnIOS9OrLater()) {
|
| - scoped_refptr<net::X509Certificate> cert(web::CreateCertFromChain(
|
| - [_wkWebView performSelector:@selector(certificateChain)]));
|
| - if (cert) {
|
| - SSLStatus.cert_id = web::CertStore::GetInstance()->StoreCert(
|
| - cert.get(), self.certGroupID);
|
| - } else {
|
| - SSLStatus.security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
|
| - SSLStatus.cert_id = 0;
|
| + NSArray* chain = [_wkWebView certificateChain];
|
| + cert = web::CreateCertFromChain(chain);
|
| + if (cert) {
|
| + int oldCertID = item->GetSSL().cert_id;
|
| + std::string oldHost = item->GetSSL().cert_status_host;
|
| + item->GetSSL().cert_id = web::CertStore::GetInstance()->StoreCert(
|
| + cert.get(), self.certGroupID);
|
| + item->GetSSL().cert_status_host = _documentURL.host();
|
| + // Only recompute the SSLStatus information if the certificate or host has
|
| + // since changed. Host can be changed in case of redirect.
|
| + if (oldCertID != item->GetSSL().cert_id ||
|
| + oldHost != item->GetSSL().cert_status_host) {
|
| + // Real SSL status is unknown, reset cert status and security style.
|
| + // They will be asynchronously updated in
|
| + // |scheduleSSLStatusUpdateUsingCertChain|.
|
| + item->GetSSL().cert_status = net::CertStatus();
|
| + item->GetSSL().security_style = web::SECURITY_STYLE_UNKNOWN;
|
| +
|
| + NSString* host = base::SysUTF8ToNSString(_documentURL.host());
|
| + [self scheduleSSLStatusUpdateUsingCertChain:chain host:host];
|
| }
|
| }
|
| - } else {
|
| - SSLStatus.security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
|
| - SSLStatus.cert_id = 0;
|
| }
|
|
|
| - if (!previousSSLStatus.Equals(SSLStatus)) {
|
| + if (!cert) {
|
| + item->GetSSL().cert_id = 0;
|
| + if (!item->GetURL().SchemeIsCryptographic()) {
|
| + // HTTP or other non-secure connection.
|
| + item->GetSSL().security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
|
| + } else {
|
| + // HTTPS, no certificate (this use-case has not been observed).
|
| + item->GetSSL().security_style = web::SECURITY_STYLE_UNKNOWN;
|
| + }
|
| + }
|
| +
|
| + if (!previousSSLStatus.Equals(item->GetSSL())) {
|
| [self didUpdateSSLStatusForCurrentNavigationItem];
|
| }
|
| }
|
| +
|
| #endif // !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
|
|
|
| - (void)registerLoadRequest:(const GURL&)url {
|
| @@ -926,6 +1028,7 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
|
|
|
| - (void)URLDidChangeWithoutDocumentChange:(const GURL&)newURL {
|
| DCHECK(newURL == net::GURLWithNSURL([_wkWebView URL]));
|
| + DCHECK_EQ(_documentURL.host(), newURL.host());
|
| _documentURL = newURL;
|
| // If called during window.history.pushState or window.history.replaceState
|
| // JavaScript evaluation, only update the document URL. This callback does not
|
| @@ -1347,6 +1450,14 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
|
| #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
|
| [self updateSSLStatusForCurrentNavigationItem];
|
| #endif
|
| +
|
| + // Report cases where SSL cert is missing for a secure connection.
|
| + if (_documentURL.SchemeIsCryptographic()) {
|
| + scoped_refptr<net::X509Certificate> cert =
|
| + web::CreateCertFromChain([_wkWebView certificateChain]);
|
| + UMA_HISTOGRAM_BOOLEAN("WebController.WKWebViewHasCertForSecureConnection",
|
| + cert);
|
| + }
|
| }
|
|
|
| - (void)webView:(WKWebView *)webView
|
| @@ -1380,6 +1491,7 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
|
|
|
| SecTrustRef trust = challenge.protectionSpace.serverTrust;
|
| scoped_refptr<net::X509Certificate> cert = web::CreateCertFromTrust(trust);
|
| + // TODO(eugenebut): pass SecTrustRef instead of cert.
|
| [_certVerificationController
|
| decidePolicyForCert:cert
|
| host:challenge.protectionSpace.host
|
|
|