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

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: Self review 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
275 // Reports "WebController.WKWebViewHasCertForSecureConnection" UMA.
276 - (void)reportHasCertForSecureConnectionUMAWithValue:(bool)value;
277
259 // Registers load request with empty referrer and link or client redirect 278 // Registers load request with empty referrer and link or client redirect
260 // transition based on user interaction state. 279 // transition based on user interaction state.
261 - (void)registerLoadRequest:(const GURL&)url; 280 - (void)registerLoadRequest:(const GURL&)url;
262 281
263 // Called when a non-document-changing URL change occurs. Updates the 282 // Called when a non-document-changing URL change occurs. Updates the
264 // _documentURL, and informs the superclass of the change. 283 // _documentURL, and informs the superclass of the change.
265 - (void)URLDidChangeWithoutDocumentChange:(const GURL&)URL; 284 - (void)URLDidChangeWithoutDocumentChange:(const GURL&)URL;
266 285
267 // Returns new autoreleased instance of WKUserContentController which has 286 // Returns new autoreleased instance of WKUserContentController which has
268 // early page script. 287 // early page script.
(...skipping 588 matching lines...) Expand 10 before | Expand all | Expand 10 after
857 - (void)addActivityIndicatorTask { 876 - (void)addActivityIndicatorTask {
858 [[CRWNetworkActivityIndicatorManager sharedInstance] 877 [[CRWNetworkActivityIndicatorManager sharedInstance]
859 startNetworkTaskForGroup:[self activityIndicatorGroupID]]; 878 startNetworkTaskForGroup:[self activityIndicatorGroupID]];
860 } 879 }
861 880
862 - (void)clearActivityIndicatorTasks { 881 - (void)clearActivityIndicatorTasks {
863 [[CRWNetworkActivityIndicatorManager sharedInstance] 882 [[CRWNetworkActivityIndicatorManager sharedInstance]
864 clearNetworkTasksForGroup:[self activityIndicatorGroupID]]; 883 clearNetworkTasksForGroup:[self activityIndicatorGroupID]];
865 } 884 }
866 885
886 - (void)updateSSLStatusForNavigationItemWithID:(int)navigationItemID
887 certChain:(NSArray*)chain
888 host:(NSString*)host
889 withSecurityStyle:(web::SecurityStyle)style
890 certStatus:(net::CertStatus)certStatus {
891 web::NavigationManager* navigationManager =
892 self.webStateImpl->GetNavigationManager();
893
894 // The searched item almost always be the last one, so walk backward rather
895 // than forward.
896 for (int i = navigationManager->GetEntryCount() - 1; 0 <= i; i--) {
897 web::NavigationItem* item = navigationManager->GetItemAtIndex(i);
898 if (item->GetUniqueID() != navigationItemID)
899 continue;
900
901 // NavigationItem's UniqueID is preserved even after redirects, so
902 // checking that cert and URL match is necessary.
903 scoped_refptr<net::X509Certificate> cert(web::CreateCertFromChain(chain));
904 int certID =
905 web::CertStore::GetInstance()->StoreCert(cert.get(), self.certGroupID);
906 std::string GURLHost = base::SysNSStringToUTF8(host);
907 web::SSLStatus& SSLStatus = item->GetSSL();
908 if (SSLStatus.cert_id == certID && item->GetURL().host() == GURLHost) {
909 web::SSLStatus previousSSLStatus = item->GetSSL();
910 SSLStatus.cert_status = certStatus;
911 SSLStatus.security_style = style;
912 if (navigationManager->GetCurrentEntryIndex() == i &&
913 !previousSSLStatus.Equals(SSLStatus)) {
914 [self didUpdateSSLStatusForCurrentNavigationItem];
915 }
916 }
917 return;
918 }
919 }
920
921 - (void)scheduleSSLStatusUpdateUsingCertChain:(NSArray*)chain
922 host:(NSString*)host {
923 // Real SSL status is unknown, reset cert status and security style. Keep
924 // cert_id, which works as a guard to prevent re-entry into this method.
davidben 2015/10/15 17:46:12 I don't understand what this comment is saying. By
Eugene But (OOO till 7-30) 2015/10/15 22:59:34 Moved the code.
925 web::NavigationItem* item =
926 self.webStateImpl->GetNavigationManager()->GetLastCommittedItem();
927 item->GetSSL().cert_status = net::CertStatus();
928 item->GetSSL().security_style = web::SECURITY_STYLE_UNKNOWN;
929
930 // Use Navigation Item's unique ID to locate requested item after
931 // obtaining cert status asynchronously.
932 int itemID = item->GetUniqueID();
933
934 base::WeakNSObject<CRWWKWebViewWebController> weakSelf(self);
935 void (^SSLStatusResponse)(web::SecurityStyle, net::CertStatus) =
936 ^(web::SecurityStyle style, net::CertStatus certStatus) {
937 base::scoped_nsobject<CRWWKWebViewWebController> strongSelf(
938 [weakSelf retain]);
939 if (!strongSelf || [strongSelf isBeingDestroyed]) {
940 return;
941 }
942 [strongSelf updateSSLStatusForNavigationItemWithID:itemID
943 certChain:chain
944 host:host
945 withSecurityStyle:style
946 certStatus:certStatus];
947 };
948
949 [_certVerificationController
950 querySSLStatusForTrust:web::CreateServerTrustFromChain(chain, host)
951 host:host
952 completionHandler:SSLStatusResponse];
953 }
954
867 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) 955 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
956
868 - (void)updateSSLStatusForCurrentNavigationItem { 957 - (void)updateSSLStatusForCurrentNavigationItem {
869 if ([self isBeingDestroyed]) 958 if ([self isBeingDestroyed])
870 return; 959 return;
871 960
872 DCHECK(self.webStateImpl);
873 web::NavigationItem* item = 961 web::NavigationItem* item =
874 self.webStateImpl->GetNavigationManagerImpl().GetLastCommittedItem(); 962 self.webStateImpl->GetNavigationManagerImpl().GetLastCommittedItem();
875 if (!item) 963 if (!item)
876 return; 964 return;
877 965
878 web::SSLStatus previousSSLStatus = item->GetSSL(); 966 web::SSLStatus previousSSLStatus = item->GetSSL();
879 web::SSLStatus& SSLStatus = item->GetSSL(); 967 web::SSLStatus& SSLStatus = item->GetSSL();
968
969 // Starting from iOS9 WKWebView blocks active mixed content, so if
970 // |hasOnlySecureContent| returns NO it means passive content.
971 SSLStatus.content_status = [_wkWebView hasOnlySecureContent]
972 ? web::SSLStatus::NORMAL_CONTENT
973 : web::SSLStatus::DISPLAYED_INSECURE_CONTENT;
974
975 // Try updating SSLStatus for current NavigationItem asynchronously.
976 scoped_refptr<net::X509Certificate> cert;
880 if (item->GetURL().SchemeIsCryptographic()) { 977 if (item->GetURL().SchemeIsCryptographic()) {
881 // TODO(eugenebut): Do not set security style to authenticated once 978 NSArray* chain = [_wkWebView certificateChain];
882 // proceeding with bad ssl cert is implemented. 979 cert = web::CreateCertFromChain(chain);
883 SSLStatus.security_style = web::SECURITY_STYLE_AUTHENTICATED; 980 if (cert) {
884 SSLStatus.content_status = [_wkWebView hasOnlySecureContent] 981 int oldCertID = SSLStatus.cert_id;
885 ? web::SSLStatus::NORMAL_CONTENT 982 SSLStatus.cert_id = web::CertStore::GetInstance()->StoreCert(
886 : web::SSLStatus::DISPLAYED_INSECURE_CONTENT; 983 cert.get(), self.certGroupID);
887 984 // Only recompute the SSLStatus information if the certificate or host has
888 if (base::ios::IsRunningOnIOS9OrLater()) { 985 // since changed. Host can be changed in case of redirect.
889 scoped_refptr<net::X509Certificate> cert(web::CreateCertFromChain( 986 if (oldCertID != SSLStatus.cert_id ||
890 [_wkWebView performSelector:@selector(certificateChain)])); 987 item->GetURL().host() != _documentURL.host()) {
891 if (cert) { 988 NSString* host = base::SysUTF8ToNSString(_documentURL.host());
892 SSLStatus.cert_id = web::CertStore::GetInstance()->StoreCert( 989 [self scheduleSSLStatusUpdateUsingCertChain:chain host:host];
893 cert.get(), self.certGroupID); 990 [self reportHasCertForSecureConnectionUMAWithValue:true];
894 } else {
895 SSLStatus.security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
896 SSLStatus.cert_id = 0;
897 } 991 }
898 } 992 }
899 } else { 993 }
900 SSLStatus.security_style = web::SECURITY_STYLE_UNAUTHENTICATED; 994
995 if (!cert) {
901 SSLStatus.cert_id = 0; 996 SSLStatus.cert_id = 0;
997 if (!item->GetURL().SchemeIsCryptographic()) {
998 // HTTP or other non-secure connection.
999 SSLStatus.security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
1000 } else {
1001 // HTTPS, no certificate (this use-case has not been observed).
1002 SSLStatus.security_style = web::SECURITY_STYLE_UNKNOWN;
1003 if (!previousSSLStatus.Equals(SSLStatus)) {
1004 [self reportHasCertForSecureConnectionUMAWithValue:false];
davidben 2015/10/15 17:46:12 If all that changed was hasSecureContent, this wil
Eugene But (OOO till 7-30) 2015/10/15 22:59:34 That's a good suggestion. Done.
1005 }
1006 }
902 } 1007 }
903 1008
904 if (!previousSSLStatus.Equals(SSLStatus)) { 1009 if (!previousSSLStatus.Equals(SSLStatus)) {
905 [self didUpdateSSLStatusForCurrentNavigationItem]; 1010 [self didUpdateSSLStatusForCurrentNavigationItem];
906 } 1011 }
907 } 1012 }
1013
908 #endif // !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) 1014 #endif // !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
909 1015
1016 - (void)reportHasCertForSecureConnectionUMAWithValue:(bool)value {
1017 UMA_HISTOGRAM_BOOLEAN("WebController.WKWebViewHasCertForSecureConnection",
1018 value);
1019 }
1020
910 - (void)registerLoadRequest:(const GURL&)url { 1021 - (void)registerLoadRequest:(const GURL&)url {
911 // If load request is registered via WKWebViewWebController, assume transition 1022 // If load request is registered via WKWebViewWebController, assume transition
912 // is link or client redirect as other transitions will already be registered 1023 // is link or client redirect as other transitions will already be registered
913 // by web controller or delegates. 1024 // by web controller or delegates.
914 // TODO(stuartmorgan): Remove guesswork and replace with information from 1025 // TODO(stuartmorgan): Remove guesswork and replace with information from
915 // decidePolicyForNavigationAction:. 1026 // decidePolicyForNavigationAction:.
916 ui::PageTransition transition = self.userInteractionRegistered 1027 ui::PageTransition transition = self.userInteractionRegistered
917 ? ui::PAGE_TRANSITION_LINK 1028 ? ui::PAGE_TRANSITION_LINK
918 : ui::PAGE_TRANSITION_CLIENT_REDIRECT; 1029 : ui::PAGE_TRANSITION_CLIENT_REDIRECT;
919 // The referrer is not known yet, and will be updated later. 1030 // The referrer is not known yet, and will be updated later.
920 const web::Referrer emptyReferrer; 1031 const web::Referrer emptyReferrer;
921 [self registerLoadRequest:url referrer:emptyReferrer transition:transition]; 1032 [self registerLoadRequest:url referrer:emptyReferrer transition:transition];
922 } 1033 }
923 1034
924 - (void)URLDidChangeWithoutDocumentChange:(const GURL&)newURL { 1035 - (void)URLDidChangeWithoutDocumentChange:(const GURL&)newURL {
925 DCHECK(newURL == net::GURLWithNSURL([_wkWebView URL])); 1036 DCHECK(newURL == net::GURLWithNSURL([_wkWebView URL]));
926 _documentURL = newURL; 1037 _documentURL = newURL;
davidben 2015/10/15 17:46:12 This should probably also have a DCHECK that _docu
Eugene But (OOO till 7-30) 2015/10/15 22:59:34 Yes it should. Done.
927 // If called during window.history.pushState or window.history.replaceState 1038 // If called during window.history.pushState or window.history.replaceState
928 // JavaScript evaluation, only update the document URL. This callback does not 1039 // 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 1040 // 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 1041 // navigation entry for this page change. Web controller will sync with
931 // history changes when a window.history.didPushState or 1042 // history changes when a window.history.didPushState or
932 // window.history.didReplaceState message is received, which should happen in 1043 // window.history.didReplaceState message is received, which should happen in
933 // the next runloop. 1044 // the next runloop.
934 if (!_changingHistoryState) { 1045 if (!_changingHistoryState) {
935 [self registerLoadRequest:_documentURL]; 1046 [self registerLoadRequest:_documentURL];
936 [self didStartLoadingURL:_documentURL updateHistory:YES]; 1047 [self didStartLoadingURL:_documentURL updateHistory:YES];
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after
1091 // TODO(eugenebut): use WKWebView progress even if Chrome net stack is enabled. 1202 // TODO(eugenebut): use WKWebView progress even if Chrome net stack is enabled.
1092 - (void)webViewEstimatedProgressDidChange { 1203 - (void)webViewEstimatedProgressDidChange {
1093 if ([self.delegate respondsToSelector: 1204 if ([self.delegate respondsToSelector:
1094 @selector(webController:didUpdateProgress:)]) { 1205 @selector(webController:didUpdateProgress:)]) {
1095 [self.delegate webController:self 1206 [self.delegate webController:self
1096 didUpdateProgress:[_wkWebView estimatedProgress]]; 1207 didUpdateProgress:[_wkWebView estimatedProgress]];
1097 } 1208 }
1098 } 1209 }
1099 1210
1100 - (void)webViewSecurityFeaturesDidChange { 1211 - (void)webViewSecurityFeaturesDidChange {
1101 if (self.loadPhase == web::LOAD_REQUESTED) { 1212 if (self.loadPhase == web::LOAD_REQUESTED) {
davidben 2015/10/15 17:46:12 Existing issue (probably ought to be separate), bu
stuartmorgan 2015/10/15 22:02:27 IIRC it's because we get told about changes relate
Eugene But (OOO till 7-30) 2015/10/15 22:59:34 WKWebView API is weird. certificateChain, URL, or
davidben 2015/10/16 00:15:20 Gotcha. Sigh. :-) I guess the situation is what it
1102 // Do not update SSL Status for pending load. It will be updated in 1213 // Do not update SSL Status for pending load. It will be updated in
1103 // |webView:didCommitNavigation:| callback. 1214 // |webView:didCommitNavigation:| callback.
1104 return; 1215 return;
1105 } 1216 }
1106 [self updateSSLStatusForCurrentNavigationItem]; 1217 [self updateSSLStatusForCurrentNavigationItem];
1107 } 1218 }
1108 1219
1109 #endif // !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) 1220 #endif // !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
1110 1221
1111 - (void)webViewLoadingStateDidChange { 1222 - (void)webViewLoadingStateDidChange {
(...skipping 270 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
« no previous file with comments | « ios/web/net/crw_cert_verification_controller_unittest.mm ('k') | ios/web/web_state/wk_web_view_security_util.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698