Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(386)

Unified Diff: ios/web/web_state/ui/crw_wk_web_view_web_controller.mm

Issue 1322193003: WKWebView(iOS9): correctly update SSL status for current navigation item (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@reland_cert_verification
Patch Set: Updated comments Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 e5c88664c2057d26c56a89ea4e4b249886d6e8fb..0395f183220c8884c99d2c0a312ac878ee442212 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"
@@ -82,6 +83,14 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
} // namespace
+#if !defined(__IPHONE_9_0) || __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0
+// Predeclare iOS9 API so calls are compiled on iOS8 bots.
+// TODO(eugenebut): Clean this up (crbug.com/523365).
+@interface WKWebView (CRWIOS9API)
+@property(nonatomic, readonly, copy) NSArray* certificateChain;
+@end
+#endif
+
@interface CRWWKWebViewWebController () <WKNavigationDelegate,
WKScriptMessageHandler,
WKUIDelegate> {
@@ -250,12 +259,28 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
// Clears all activity indicator tasks for this web controller.
- (void)clearActivityIndicatorTasks;
+// Updates |style| and |certStatus| for Navigation Item wich has given
davidben 2015/10/12 23:21:50 Perhaps: // Updates |security_style| and |cert_st
Eugene But (OOO till 7-30) 2015/10/13 20:40:33 Done.
+// |navigationItemID|, |host| and represents given |certChain|.
+- (void)updateSSLStatusForNavigationItemWithID:(int)navigationItemID
+ certChain:(NSArray*)certChain
+ 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.
+- (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.
- (void)updateSSLStatusForCurrentNavigationItem;
#endif
+// Reports "WebController.WKWebViewHasCertForSecureConnection" UMA.
+- (void)reportHasCertForSecureConnectionUMAWithValue:(bool)value;
+
// Registers load request with empty referrer and link or client redirect
// transition based on user interaction state.
- (void)registerLoadRequest:(const GURL&)url;
@@ -864,12 +889,76 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
clearNetworkTasksForGroup:[self activityIndicatorGroupID]];
}
+- (void)updateSSLStatusForNavigationItemWithID:(int)navigationItemID
+ certChain:(NSArray*)chain
davidben 2015/10/12 23:21:50 Here you use chain but in the prototype, it's cert
Eugene But (OOO till 7-30) 2015/10/13 20:40:33 Changed predeclaration. Longer argument name makes
+ 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 matche is necessary.
davidben 2015/10/12 23:21:50 matche -> match
Eugene But (OOO till 7-30) 2015/10/13 20:40:33 Done.
+ 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)
@@ -877,36 +966,63 @@ WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
web::SSLStatus previousSSLStatus = item->GetSSL();
web::SSLStatus& SSLStatus = item->GetSSL();
davidben 2015/10/12 23:21:50 I don't think the C++ style guide allows non-const
Eugene But (OOO till 7-30) 2015/10/13 20:40:33 C++ Style Guide does not allow non-const Reference
davidben 2015/10/15 17:46:12 Hrm, me neither. Interesting. I don't think I've e
Eugene But (OOO till 7-30) 2015/10/15 22:59:34 I realize that non-const ref led to come confusion
- 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;
+
+ // Starting from iOS9 WKWebView blocks active mixed content, so if
+ // |hasOnlySecureContent| returns NO it means passive content.
+ // On iOS8 there is no way to determine if web view has active mixed content.
+ SSLStatus.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 (base::ios::IsRunningOnIOS9OrLater() &&
+ item->GetURL().SchemeIsCryptographic()) {
+ NSArray* chain = [_wkWebView certificateChain];
+ cert = web::CreateCertFromChain(chain);
+ if (cert) {
+ int oldCertID = SSLStatus.cert_id;
+ SSLStatus.cert_id = web::CertStore::GetInstance()->StoreCert(
+ cert.get(), self.certGroupID);
+ // Only recompute the SSLStatus information if the certificate has since
+ // changed.
+ if (oldCertID != SSLStatus.cert_id ||
+ item->GetURL().host() != _documentURL.host()) {
davidben 2015/10/12 23:21:50 Does this check even do anything? item->GetURL() i
davidben 2015/10/12 23:25:55 Right, a related problem here is: is it possible f
Eugene But (OOO till 7-30) 2015/10/13 20:40:33 I check host, so we can recalculate SSL status aft
Eugene But (OOO till 7-30) 2015/10/13 20:40:33 _documentURL.host() can not change w/o calling upd
davidben 2015/10/15 17:46:12 If they're already synchronized, how can this chec
Eugene But (OOO till 7-30) 2015/10/15 22:59:34 Yes, the update flow is: 1.) webView:didCommitNavi
+ NSString* host = base::SysUTF8ToNSString(_documentURL.host());
+ [self scheduleSSLStatusUpdateUsingCertChain:chain host:host];
davidben 2015/10/12 23:21:50 Because this is asynchronous (and unless we decide
Eugene But (OOO till 7-30) 2015/10/13 20:40:33 I think that clearing cert_status and using web::S
+ [self reportHasCertForSecureConnectionUMAWithValue:true];
}
}
- } else {
- SSLStatus.security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
+ }
+
+ if (!cert) {
SSLStatus.cert_id = 0;
+ if (!item->GetURL().SchemeIsCryptographic()) {
+ // HTTP or other non-secure connection.
+ SSLStatus.security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
+ } else if (base::ios::IsRunningOnIOS9OrLater()) {
+ // HTTPS, iOS9 and no certificate (this use-case has not been observed).
+ [self reportHasCertForSecureConnectionUMAWithValue:false];
davidben 2015/10/12 23:21:50 This line will also run every time updateSSLStatus
Eugene But (OOO till 7-30) 2015/10/13 20:40:33 Done.
+ SSLStatus.security_style = web::SECURITY_STYLE_UNKNOWN;
+ } else {
+ // HTTPS, iOS8.
+ // iOS8 cannot load unauthenticated HTTPS content.
+ SSLStatus.security_style = web::SECURITY_STYLE_AUTHENTICATED;
+ }
}
if (!previousSSLStatus.Equals(SSLStatus)) {
[self didUpdateSSLStatusForCurrentNavigationItem];
}
}
+
#endif // !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
+- (void)reportHasCertForSecureConnectionUMAWithValue:(bool)value {
+ UMA_HISTOGRAM_BOOLEAN("WebController.WKWebViewHasCertForSecureConnection",
+ value);
+}
+
- (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
@@ -1389,6 +1505,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

Powered by Google App Engine
This is Rietveld 408576698