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

Side by Side 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: Addressed David's review 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 unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "ios/web/web_state/ui/crw_wk_web_view_web_controller.h" 5 #import "ios/web/web_state/ui/crw_wk_web_view_web_controller.h"
6 6
7 #import <WebKit/WebKit.h> 7 #import <WebKit/WebKit.h>
8 8
9 #include "base/ios/ios_util.h" 9 #include "base/ios/ios_util.h"
10 #include "base/ios/weak_nsobject.h" 10 #include "base/ios/weak_nsobject.h"
11 #include "base/json/json_reader.h" 11 #include "base/json/json_reader.h"
12 #import "base/mac/scoped_nsobject.h" 12 #import "base/mac/scoped_nsobject.h"
13 #include "base/macros.h" 13 #include "base/macros.h"
14 #include "base/metrics/histogram_macros.h"
14 #include "base/strings/sys_string_conversions.h" 15 #include "base/strings/sys_string_conversions.h"
15 #include "base/values.h" 16 #include "base/values.h"
16 #import "ios/net/http_response_headers_util.h" 17 #import "ios/net/http_response_headers_util.h"
17 #import "ios/web/crw_network_activity_indicator_manager.h" 18 #import "ios/web/crw_network_activity_indicator_manager.h"
18 #import "ios/web/navigation/crw_session_controller.h" 19 #import "ios/web/navigation/crw_session_controller.h"
19 #import "ios/web/navigation/crw_session_entry.h" 20 #import "ios/web/navigation/crw_session_entry.h"
20 #include "ios/web/navigation/navigation_item_impl.h" 21 #include "ios/web/navigation/navigation_item_impl.h"
21 #include "ios/web/navigation/web_load_params.h" 22 #include "ios/web/navigation/web_load_params.h"
22 #import "ios/web/net/crw_cert_verification_controller.h" 23 #import "ios/web/net/crw_cert_verification_controller.h"
23 #include "ios/web/public/cert_store.h" 24 #include "ios/web/public/cert_store.h"
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 // Called when a load ends in an SSL error and certificate chain. 244 // Called when a load ends in an SSL error and certificate chain.
244 - (void)handleSSLCertError:(NSError*)error; 245 - (void)handleSSLCertError:(NSError*)error;
245 #endif 246 #endif
246 247
247 // Adds an activity indicator tasks for this web controller. 248 // Adds an activity indicator tasks for this web controller.
248 - (void)addActivityIndicatorTask; 249 - (void)addActivityIndicatorTask;
249 250
250 // Clears all activity indicator tasks for this web controller. 251 // Clears all activity indicator tasks for this web controller.
251 - (void)clearActivityIndicatorTasks; 252 - (void)clearActivityIndicatorTasks;
252 253
254 // Updates |security_style| and |cert_status| for the NavigationItem with ID
255 // |navigationItemID|, if URL and certificate chain still match |host| and
256 // |certChain|.
257 - (void)updateSSLStatusForNavigationItemWithID:(int)navigationItemID
258 certChain:(NSArray*)chain
259 host:(NSString*)host
260 withSecurityStyle:(web::SecurityStyle)style
261 certStatus:(net::CertStatus)certStatus;
262
263 // Asynchronously obtains SSL status from given |certChain| and |host| and
264 // updates current navigation item. Before scheduling update changes SSLStatus'
265 // cert_status and security_style to default.
266 - (void)scheduleSSLStatusUpdateUsingCertChain:(NSArray*)chain
267 host:(NSString*)host;
268
253 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) 269 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
254 // Updates SSL status for the current navigation item based on the information 270 // Updates SSL status for the current navigation item based on the information
255 // provided by web view. 271 // provided by web view.
256 - (void)updateSSLStatusForCurrentNavigationItem; 272 - (void)updateSSLStatusForCurrentNavigationItem;
257 #endif 273 #endif
258 274
259 // Registers load request with empty referrer and link or client redirect 275 // Registers load request with empty referrer and link or client redirect
260 // transition based on user interaction state. 276 // transition based on user interaction state.
261 - (void)registerLoadRequest:(const GURL&)url; 277 - (void)registerLoadRequest:(const GURL&)url;
262 278
(...skipping 594 matching lines...) Expand 10 before | Expand all | Expand 10 after
857 - (void)addActivityIndicatorTask { 873 - (void)addActivityIndicatorTask {
858 [[CRWNetworkActivityIndicatorManager sharedInstance] 874 [[CRWNetworkActivityIndicatorManager sharedInstance]
859 startNetworkTaskForGroup:[self activityIndicatorGroupID]]; 875 startNetworkTaskForGroup:[self activityIndicatorGroupID]];
860 } 876 }
861 877
862 - (void)clearActivityIndicatorTasks { 878 - (void)clearActivityIndicatorTasks {
863 [[CRWNetworkActivityIndicatorManager sharedInstance] 879 [[CRWNetworkActivityIndicatorManager sharedInstance]
864 clearNetworkTasksForGroup:[self activityIndicatorGroupID]]; 880 clearNetworkTasksForGroup:[self activityIndicatorGroupID]];
865 } 881 }
866 882
883 - (void)updateSSLStatusForNavigationItemWithID:(int)navigationItemID
884 certChain:(NSArray*)chain
885 host:(NSString*)host
886 withSecurityStyle:(web::SecurityStyle)style
887 certStatus:(net::CertStatus)certStatus {
888 web::NavigationManager* navigationManager =
889 self.webStateImpl->GetNavigationManager();
890
891 // The searched item almost always be the last one, so walk backward rather
892 // than forward.
893 for (int i = navigationManager->GetEntryCount() - 1; 0 <= i; i--) {
894 web::NavigationItem* item = navigationManager->GetItemAtIndex(i);
895 if (item->GetUniqueID() != navigationItemID)
896 continue;
897
898 // NavigationItem's UniqueID is preserved even after redirects, so
899 // checking that cert and URL match is necessary.
900 scoped_refptr<net::X509Certificate> cert(web::CreateCertFromChain(chain));
901 int certID =
902 web::CertStore::GetInstance()->StoreCert(cert.get(), self.certGroupID);
903 std::string GURLHost = base::SysNSStringToUTF8(host);
904 web::SSLStatus& SSLStatus = item->GetSSL();
905 if (SSLStatus.cert_id == certID && item->GetURL().host() == GURLHost) {
906 web::SSLStatus previousSSLStatus = item->GetSSL();
907 SSLStatus.cert_status = certStatus;
908 SSLStatus.security_style = style;
909 if (navigationManager->GetCurrentEntryIndex() == i &&
910 !previousSSLStatus.Equals(SSLStatus)) {
911 [self didUpdateSSLStatusForCurrentNavigationItem];
912 }
913 }
914 return;
915 }
916 }
917
918 - (void)scheduleSSLStatusUpdateUsingCertChain:(NSArray*)chain
919 host:(NSString*)host {
920 // Use Navigation Item's unique ID to locate requested item after
921 // obtaining cert status asynchronously.
922 int itemID = self.webStateImpl->GetNavigationManager()
923 ->GetLastCommittedItem()
924 ->GetUniqueID();
925
926 base::WeakNSObject<CRWWKWebViewWebController> weakSelf(self);
927 void (^SSLStatusResponse)(web::SecurityStyle, net::CertStatus) =
928 ^(web::SecurityStyle style, net::CertStatus certStatus) {
929 base::scoped_nsobject<CRWWKWebViewWebController> strongSelf(
930 [weakSelf retain]);
931 if (!strongSelf || [strongSelf isBeingDestroyed]) {
932 return;
933 }
934 [strongSelf updateSSLStatusForNavigationItemWithID:itemID
935 certChain:chain
936 host:host
937 withSecurityStyle:style
938 certStatus:certStatus];
939 };
940
941 [_certVerificationController
942 querySSLStatusForTrust:web::CreateServerTrustFromChain(chain, host)
943 host:host
944 completionHandler:SSLStatusResponse];
945 }
946
867 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) 947 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
948
868 - (void)updateSSLStatusForCurrentNavigationItem { 949 - (void)updateSSLStatusForCurrentNavigationItem {
869 if ([self isBeingDestroyed]) 950 if ([self isBeingDestroyed])
870 return; 951 return;
871 952
872 DCHECK(self.webStateImpl);
873 web::NavigationItem* item = 953 web::NavigationItem* item =
874 self.webStateImpl->GetNavigationManagerImpl().GetLastCommittedItem(); 954 self.webStateImpl->GetNavigationManagerImpl().GetLastCommittedItem();
875 if (!item) 955 if (!item)
876 return; 956 return;
877 957
878 web::SSLStatus previousSSLStatus = item->GetSSL(); 958 web::SSLStatus previousSSLStatus = item->GetSSL();
879 web::SSLStatus& SSLStatus = item->GetSSL(); 959
960 // Starting from iOS9 WKWebView blocks active mixed content, so if
961 // |hasOnlySecureContent| returns NO it means passive content.
962 item->GetSSL().content_status =
963 [_wkWebView hasOnlySecureContent]
964 ? web::SSLStatus::NORMAL_CONTENT
965 : web::SSLStatus::DISPLAYED_INSECURE_CONTENT;
966
967 // Try updating SSLStatus for current NavigationItem asynchronously.
968 scoped_refptr<net::X509Certificate> cert;
880 if (item->GetURL().SchemeIsCryptographic()) { 969 if (item->GetURL().SchemeIsCryptographic()) {
881 // TODO(eugenebut): Do not set security style to authenticated once 970 NSArray* chain = [_wkWebView certificateChain];
882 // proceeding with bad ssl cert is implemented. 971 cert = web::CreateCertFromChain(chain);
883 SSLStatus.security_style = web::SECURITY_STYLE_AUTHENTICATED; 972 if (cert) {
884 SSLStatus.content_status = [_wkWebView hasOnlySecureContent] 973 int oldCertID = item->GetSSL().cert_id;
885 ? web::SSLStatus::NORMAL_CONTENT 974 std::string oldHost = item->GetSSL().cert_status_host;
886 : web::SSLStatus::DISPLAYED_INSECURE_CONTENT; 975 item->GetSSL().cert_id = web::CertStore::GetInstance()->StoreCert(
976 cert.get(), self.certGroupID);
977 item->GetSSL().cert_status_host = _documentURL.host();
978 // Only recompute the SSLStatus information if the certificate or host has
979 // since changed. Host can be changed in case of redirect.
980 if (oldCertID != item->GetSSL().cert_id ||
981 oldHost != item->GetSSL().cert_status_host) {
982 // Real SSL status is unknown, reset cert status and security style.
983 // they will be asynchronously updated in
davidben 2015/10/16 00:15:20 they -> They
Eugene But (OOO till 7-30) 2015/10/16 02:25:35 Done.
984 // |scheduleSSLStatusUpdateUsingCertChain|.
985 item->GetSSL().cert_status = net::CertStatus();
986 item->GetSSL().security_style = web::SECURITY_STYLE_UNKNOWN;
887 987
888 if (base::ios::IsRunningOnIOS9OrLater()) { 988 NSString* host = base::SysUTF8ToNSString(_documentURL.host());
889 scoped_refptr<net::X509Certificate> cert(web::CreateCertFromChain( 989 [self scheduleSSLStatusUpdateUsingCertChain:chain host:host];
890 [_wkWebView performSelector:@selector(certificateChain)]));
891 if (cert) {
892 SSLStatus.cert_id = web::CertStore::GetInstance()->StoreCert(
893 cert.get(), self.certGroupID);
894 } else {
895 SSLStatus.security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
896 SSLStatus.cert_id = 0;
897 } 990 }
898 } 991 }
899 } else {
900 SSLStatus.security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
901 SSLStatus.cert_id = 0;
902 } 992 }
903 993
904 if (!previousSSLStatus.Equals(SSLStatus)) { 994 if (!cert) {
995 item->GetSSL().cert_id = 0;
996 if (!item->GetURL().SchemeIsCryptographic()) {
997 // HTTP or other non-secure connection.
998 item->GetSSL().security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
999 } else {
1000 // HTTPS, no certificate (this use-case has not been observed).
1001 item->GetSSL().security_style = web::SECURITY_STYLE_UNKNOWN;
1002 }
1003 }
1004
1005 if (!previousSSLStatus.Equals(item->GetSSL())) {
905 [self didUpdateSSLStatusForCurrentNavigationItem]; 1006 [self didUpdateSSLStatusForCurrentNavigationItem];
906 } 1007 }
907 } 1008 }
1009
908 #endif // !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) 1010 #endif // !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
909 1011
910 - (void)registerLoadRequest:(const GURL&)url { 1012 - (void)registerLoadRequest:(const GURL&)url {
911 // If load request is registered via WKWebViewWebController, assume transition 1013 // If load request is registered via WKWebViewWebController, assume transition
912 // is link or client redirect as other transitions will already be registered 1014 // is link or client redirect as other transitions will already be registered
913 // by web controller or delegates. 1015 // by web controller or delegates.
914 // TODO(stuartmorgan): Remove guesswork and replace with information from 1016 // TODO(stuartmorgan): Remove guesswork and replace with information from
915 // decidePolicyForNavigationAction:. 1017 // decidePolicyForNavigationAction:.
916 ui::PageTransition transition = self.userInteractionRegistered 1018 ui::PageTransition transition = self.userInteractionRegistered
917 ? ui::PAGE_TRANSITION_LINK 1019 ? ui::PAGE_TRANSITION_LINK
918 : ui::PAGE_TRANSITION_CLIENT_REDIRECT; 1020 : ui::PAGE_TRANSITION_CLIENT_REDIRECT;
919 // The referrer is not known yet, and will be updated later. 1021 // The referrer is not known yet, and will be updated later.
920 const web::Referrer emptyReferrer; 1022 const web::Referrer emptyReferrer;
921 [self registerLoadRequest:url referrer:emptyReferrer transition:transition]; 1023 [self registerLoadRequest:url referrer:emptyReferrer transition:transition];
922 } 1024 }
923 1025
924 - (void)URLDidChangeWithoutDocumentChange:(const GURL&)newURL { 1026 - (void)URLDidChangeWithoutDocumentChange:(const GURL&)newURL {
925 DCHECK(newURL == net::GURLWithNSURL([_wkWebView URL])); 1027 DCHECK(newURL == net::GURLWithNSURL([_wkWebView URL]));
1028 DCHECK_EQ(_documentURL.host(), newURL.host());
926 _documentURL = newURL; 1029 _documentURL = newURL;
927 // If called during window.history.pushState or window.history.replaceState 1030 // If called during window.history.pushState or window.history.replaceState
928 // JavaScript evaluation, only update the document URL. This callback does not 1031 // JavaScript evaluation, only update the document URL. This callback does not
929 // have any information about the state object and cannot create (or edit) the 1032 // have any information about the state object and cannot create (or edit) the
930 // navigation entry for this page change. Web controller will sync with 1033 // navigation entry for this page change. Web controller will sync with
931 // history changes when a window.history.didPushState or 1034 // history changes when a window.history.didPushState or
932 // window.history.didReplaceState message is received, which should happen in 1035 // window.history.didReplaceState message is received, which should happen in
933 // the next runloop. 1036 // the next runloop.
934 if (!_changingHistoryState) { 1037 if (!_changingHistoryState) {
935 [self registerLoadRequest:_documentURL]; 1038 [self registerLoadRequest:_documentURL];
(...skipping 413 matching lines...) Expand 10 before | Expand all | Expand 10 after
1349 // This is the point where the document's URL has actually changed. 1452 // This is the point where the document's URL has actually changed.
1350 _documentURL = net::GURLWithNSURL([_wkWebView URL]); 1453 _documentURL = net::GURLWithNSURL([_wkWebView URL]);
1351 DCHECK(_documentURL == self.lastRegisteredRequestURL); 1454 DCHECK(_documentURL == self.lastRegisteredRequestURL);
1352 [self webPageChanged]; 1455 [self webPageChanged];
1353 1456
1354 [self updateCurrentBackForwardListItemHolder]; 1457 [self updateCurrentBackForwardListItemHolder];
1355 1458
1356 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) 1459 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
1357 [self updateSSLStatusForCurrentNavigationItem]; 1460 [self updateSSLStatusForCurrentNavigationItem];
1358 #endif 1461 #endif
1462
1463 // Report cases where SSL cert is missing for a secure connection.
1464 if (_documentURL.SchemeIsCryptographic()) {
1465 scoped_refptr<net::X509Certificate> cert =
1466 web::CreateCertFromChain([_wkWebView certificateChain]);
1467 UMA_HISTOGRAM_BOOLEAN("WebController.WKWebViewHasCertForSecureConnection",
1468 cert);
1469 }
1359 } 1470 }
1360 1471
1361 - (void)webView:(WKWebView *)webView 1472 - (void)webView:(WKWebView *)webView
1362 didFinishNavigation:(WKNavigation *)navigation { 1473 didFinishNavigation:(WKNavigation *)navigation {
1363 DCHECK(!self.isHalted); 1474 DCHECK(!self.isHalted);
1364 // Trigger JavaScript driven post-document-load-completion tasks. 1475 // Trigger JavaScript driven post-document-load-completion tasks.
1365 // TODO(jyquinn): Investigate using WKUserScriptInjectionTimeAtDocumentEnd to 1476 // TODO(jyquinn): Investigate using WKUserScriptInjectionTimeAtDocumentEnd to
1366 // inject this material at the appropriate time rather than invoking here. 1477 // inject this material at the appropriate time rather than invoking here.
1367 web::EvaluateJavaScript(webView, 1478 web::EvaluateJavaScript(webView,
1368 @"__gCrWeb.didFinishNavigation()", nil); 1479 @"__gCrWeb.didFinishNavigation()", nil);
(...skipping 13 matching lines...) Expand all
1382 (void (^)(NSURLSessionAuthChallengeDisposition disposition, 1493 (void (^)(NSURLSessionAuthChallengeDisposition disposition,
1383 NSURLCredential *credential))completionHandler { 1494 NSURLCredential *credential))completionHandler {
1384 if (![challenge.protectionSpace.authenticationMethod 1495 if (![challenge.protectionSpace.authenticationMethod
1385 isEqual:NSURLAuthenticationMethodServerTrust]) { 1496 isEqual:NSURLAuthenticationMethodServerTrust]) {
1386 completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); 1497 completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
1387 return; 1498 return;
1388 } 1499 }
1389 1500
1390 SecTrustRef trust = challenge.protectionSpace.serverTrust; 1501 SecTrustRef trust = challenge.protectionSpace.serverTrust;
1391 scoped_refptr<net::X509Certificate> cert = web::CreateCertFromTrust(trust); 1502 scoped_refptr<net::X509Certificate> cert = web::CreateCertFromTrust(trust);
1503 // TODO(eugenebut): pass SecTrustRef instead of cert.
1392 [_certVerificationController 1504 [_certVerificationController
1393 decidePolicyForCert:cert 1505 decidePolicyForCert:cert
1394 host:challenge.protectionSpace.host 1506 host:challenge.protectionSpace.host
1395 completionHandler:^(web::CertAcceptPolicy policy, 1507 completionHandler:^(web::CertAcceptPolicy policy,
1396 net::CertStatus status) { 1508 net::CertStatus status) {
1397 completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, 1509 completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace,
1398 nil); 1510 nil);
1399 }]; 1511 }];
1400 } 1512 }
1401 1513
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
1488 placeholderText:defaultText 1600 placeholderText:defaultText
1489 requestURL: 1601 requestURL:
1490 net::GURLWithNSURL(frame.request.URL) 1602 net::GURLWithNSURL(frame.request.URL)
1491 completionHandler:completionHandler]; 1603 completionHandler:completionHandler];
1492 } else if (completionHandler) { 1604 } else if (completionHandler) {
1493 completionHandler(nil); 1605 completionHandler(nil);
1494 } 1606 }
1495 } 1607 }
1496 1608
1497 @end 1609 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698