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

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: Updated comment 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
« no previous file with comments | « ios/web/public/ssl_status.h ('k') | ios/web/web_state/wk_web_view_security_util.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 217 matching lines...) Expand 10 before | Expand all | Expand 10 after
241 // Called when a load ends in an SSL error and certificate chain. 242 // Called when a load ends in an SSL error and certificate chain.
242 - (void)handleSSLCertError:(NSError*)error; 243 - (void)handleSSLCertError:(NSError*)error;
243 #endif 244 #endif
244 245
245 // Adds an activity indicator tasks for this web controller. 246 // Adds an activity indicator tasks for this web controller.
246 - (void)addActivityIndicatorTask; 247 - (void)addActivityIndicatorTask;
247 248
248 // Clears all activity indicator tasks for this web controller. 249 // Clears all activity indicator tasks for this web controller.
249 - (void)clearActivityIndicatorTasks; 250 - (void)clearActivityIndicatorTasks;
250 251
252 // Updates |security_style| and |cert_status| for the NavigationItem with ID
253 // |navigationItemID|, if URL and certificate chain still match |host| and
254 // |certChain|.
255 - (void)updateSSLStatusForNavigationItemWithID:(int)navigationItemID
256 certChain:(NSArray*)chain
257 host:(NSString*)host
258 withSecurityStyle:(web::SecurityStyle)style
259 certStatus:(net::CertStatus)certStatus;
260
261 // Asynchronously obtains SSL status from given |certChain| and |host| and
262 // updates current navigation item. Before scheduling update changes SSLStatus'
263 // cert_status and security_style to default.
264 - (void)scheduleSSLStatusUpdateUsingCertChain:(NSArray*)chain
265 host:(NSString*)host;
266
251 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) 267 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
252 // Updates SSL status for the current navigation item based on the information 268 // Updates SSL status for the current navigation item based on the information
253 // provided by web view. 269 // provided by web view.
254 - (void)updateSSLStatusForCurrentNavigationItem; 270 - (void)updateSSLStatusForCurrentNavigationItem;
255 #endif 271 #endif
256 272
257 // Registers load request with empty referrer and link or client redirect 273 // Registers load request with empty referrer and link or client redirect
258 // transition based on user interaction state. 274 // transition based on user interaction state.
259 - (void)registerLoadRequest:(const GURL&)url; 275 - (void)registerLoadRequest:(const GURL&)url;
260 276
(...skipping 599 matching lines...) Expand 10 before | Expand all | Expand 10 after
860 - (void)addActivityIndicatorTask { 876 - (void)addActivityIndicatorTask {
861 [[CRWNetworkActivityIndicatorManager sharedInstance] 877 [[CRWNetworkActivityIndicatorManager sharedInstance]
862 startNetworkTaskForGroup:[self activityIndicatorGroupID]]; 878 startNetworkTaskForGroup:[self activityIndicatorGroupID]];
863 } 879 }
864 880
865 - (void)clearActivityIndicatorTasks { 881 - (void)clearActivityIndicatorTasks {
866 [[CRWNetworkActivityIndicatorManager sharedInstance] 882 [[CRWNetworkActivityIndicatorManager sharedInstance]
867 clearNetworkTasksForGroup:[self activityIndicatorGroupID]]; 883 clearNetworkTasksForGroup:[self activityIndicatorGroupID]];
868 } 884 }
869 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 // Use Navigation Item's unique ID to locate requested item after
924 // obtaining cert status asynchronously.
925 int itemID = self.webStateImpl->GetNavigationManager()
926 ->GetLastCommittedItem()
927 ->GetUniqueID();
928
929 base::WeakNSObject<CRWWKWebViewWebController> weakSelf(self);
930 void (^SSLStatusResponse)(web::SecurityStyle, net::CertStatus) =
931 ^(web::SecurityStyle style, net::CertStatus certStatus) {
932 base::scoped_nsobject<CRWWKWebViewWebController> strongSelf(
933 [weakSelf retain]);
934 if (!strongSelf || [strongSelf isBeingDestroyed]) {
935 return;
936 }
937 [strongSelf updateSSLStatusForNavigationItemWithID:itemID
938 certChain:chain
939 host:host
940 withSecurityStyle:style
941 certStatus:certStatus];
942 };
943
944 [_certVerificationController
945 querySSLStatusForTrust:web::CreateServerTrustFromChain(chain, host)
946 host:host
947 completionHandler:SSLStatusResponse];
948 }
949
870 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) 950 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
951
871 - (void)updateSSLStatusForCurrentNavigationItem { 952 - (void)updateSSLStatusForCurrentNavigationItem {
872 if ([self isBeingDestroyed]) 953 if ([self isBeingDestroyed])
873 return; 954 return;
874 955
875 DCHECK(self.webStateImpl);
876 web::NavigationItem* item = 956 web::NavigationItem* item =
877 self.webStateImpl->GetNavigationManagerImpl().GetLastCommittedItem(); 957 self.webStateImpl->GetNavigationManagerImpl().GetLastCommittedItem();
878 if (!item) 958 if (!item)
879 return; 959 return;
880 960
881 web::SSLStatus previousSSLStatus = item->GetSSL(); 961 web::SSLStatus previousSSLStatus = item->GetSSL();
882 web::SSLStatus& SSLStatus = item->GetSSL(); 962
963 // Starting from iOS9 WKWebView blocks active mixed content, so if
964 // |hasOnlySecureContent| returns NO it means passive content.
965 item->GetSSL().content_status =
966 [_wkWebView hasOnlySecureContent]
967 ? web::SSLStatus::NORMAL_CONTENT
968 : web::SSLStatus::DISPLAYED_INSECURE_CONTENT;
969
970 // Try updating SSLStatus for current NavigationItem asynchronously.
971 scoped_refptr<net::X509Certificate> cert;
883 if (item->GetURL().SchemeIsCryptographic()) { 972 if (item->GetURL().SchemeIsCryptographic()) {
884 // TODO(eugenebut): Do not set security style to authenticated once 973 NSArray* chain = [_wkWebView certificateChain];
885 // proceeding with bad ssl cert is implemented. 974 cert = web::CreateCertFromChain(chain);
886 SSLStatus.security_style = web::SECURITY_STYLE_AUTHENTICATED; 975 if (cert) {
887 SSLStatus.content_status = [_wkWebView hasOnlySecureContent] 976 int oldCertID = item->GetSSL().cert_id;
888 ? web::SSLStatus::NORMAL_CONTENT 977 std::string oldHost = item->GetSSL().cert_status_host;
889 : web::SSLStatus::DISPLAYED_INSECURE_CONTENT; 978 item->GetSSL().cert_id = web::CertStore::GetInstance()->StoreCert(
979 cert.get(), self.certGroupID);
980 item->GetSSL().cert_status_host = _documentURL.host();
981 // Only recompute the SSLStatus information if the certificate or host has
982 // since changed. Host can be changed in case of redirect.
983 if (oldCertID != item->GetSSL().cert_id ||
984 oldHost != item->GetSSL().cert_status_host) {
985 // Real SSL status is unknown, reset cert status and security style.
986 // They will be asynchronously updated in
987 // |scheduleSSLStatusUpdateUsingCertChain|.
988 item->GetSSL().cert_status = net::CertStatus();
989 item->GetSSL().security_style = web::SECURITY_STYLE_UNKNOWN;
890 990
891 if (base::ios::IsRunningOnIOS9OrLater()) { 991 NSString* host = base::SysUTF8ToNSString(_documentURL.host());
892 scoped_refptr<net::X509Certificate> cert(web::CreateCertFromChain( 992 [self scheduleSSLStatusUpdateUsingCertChain:chain host:host];
893 [_wkWebView performSelector:@selector(certificateChain)]));
894 if (cert) {
895 SSLStatus.cert_id = web::CertStore::GetInstance()->StoreCert(
896 cert.get(), self.certGroupID);
897 } else {
898 SSLStatus.security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
899 SSLStatus.cert_id = 0;
900 } 993 }
901 } 994 }
902 } else {
903 SSLStatus.security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
904 SSLStatus.cert_id = 0;
905 } 995 }
906 996
907 if (!previousSSLStatus.Equals(SSLStatus)) { 997 if (!cert) {
998 item->GetSSL().cert_id = 0;
999 if (!item->GetURL().SchemeIsCryptographic()) {
1000 // HTTP or other non-secure connection.
1001 item->GetSSL().security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
1002 } else {
1003 // HTTPS, no certificate (this use-case has not been observed).
1004 item->GetSSL().security_style = web::SECURITY_STYLE_UNKNOWN;
1005 }
1006 }
1007
1008 if (!previousSSLStatus.Equals(item->GetSSL())) {
908 [self didUpdateSSLStatusForCurrentNavigationItem]; 1009 [self didUpdateSSLStatusForCurrentNavigationItem];
909 } 1010 }
910 } 1011 }
1012
911 #endif // !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) 1013 #endif // !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
912 1014
913 - (void)registerLoadRequest:(const GURL&)url { 1015 - (void)registerLoadRequest:(const GURL&)url {
914 // If load request is registered via WKWebViewWebController, assume transition 1016 // If load request is registered via WKWebViewWebController, assume transition
915 // is link or client redirect as other transitions will already be registered 1017 // is link or client redirect as other transitions will already be registered
916 // by web controller or delegates. 1018 // by web controller or delegates.
917 // TODO(stuartmorgan): Remove guesswork and replace with information from 1019 // TODO(stuartmorgan): Remove guesswork and replace with information from
918 // decidePolicyForNavigationAction:. 1020 // decidePolicyForNavigationAction:.
919 ui::PageTransition transition = self.userInteractionRegistered 1021 ui::PageTransition transition = self.userInteractionRegistered
920 ? ui::PAGE_TRANSITION_LINK 1022 ? ui::PAGE_TRANSITION_LINK
921 : ui::PAGE_TRANSITION_CLIENT_REDIRECT; 1023 : ui::PAGE_TRANSITION_CLIENT_REDIRECT;
922 // The referrer is not known yet, and will be updated later. 1024 // The referrer is not known yet, and will be updated later.
923 const web::Referrer emptyReferrer; 1025 const web::Referrer emptyReferrer;
924 [self registerLoadRequest:url referrer:emptyReferrer transition:transition]; 1026 [self registerLoadRequest:url referrer:emptyReferrer transition:transition];
925 } 1027 }
926 1028
927 - (void)URLDidChangeWithoutDocumentChange:(const GURL&)newURL { 1029 - (void)URLDidChangeWithoutDocumentChange:(const GURL&)newURL {
928 DCHECK(newURL == net::GURLWithNSURL([_wkWebView URL])); 1030 DCHECK(newURL == net::GURLWithNSURL([_wkWebView URL]));
1031 DCHECK_EQ(_documentURL.host(), newURL.host());
929 _documentURL = newURL; 1032 _documentURL = newURL;
930 // If called during window.history.pushState or window.history.replaceState 1033 // If called during window.history.pushState or window.history.replaceState
931 // JavaScript evaluation, only update the document URL. This callback does not 1034 // JavaScript evaluation, only update the document URL. This callback does not
932 // have any information about the state object and cannot create (or edit) the 1035 // have any information about the state object and cannot create (or edit) the
933 // navigation entry for this page change. Web controller will sync with 1036 // navigation entry for this page change. Web controller will sync with
934 // history changes when a window.history.didPushState or 1037 // history changes when a window.history.didPushState or
935 // window.history.didReplaceState message is received, which should happen in 1038 // window.history.didReplaceState message is received, which should happen in
936 // the next runloop. 1039 // the next runloop.
937 if (!_changingHistoryState) { 1040 if (!_changingHistoryState) {
938 [self registerLoadRequest:_documentURL]; 1041 [self registerLoadRequest:_documentURL];
(...skipping 401 matching lines...) Expand 10 before | Expand all | Expand 10 after
1340 // This is the point where the document's URL has actually changed. 1443 // This is the point where the document's URL has actually changed.
1341 _documentURL = net::GURLWithNSURL([_wkWebView URL]); 1444 _documentURL = net::GURLWithNSURL([_wkWebView URL]);
1342 DCHECK(_documentURL == self.lastRegisteredRequestURL); 1445 DCHECK(_documentURL == self.lastRegisteredRequestURL);
1343 [self webPageChanged]; 1446 [self webPageChanged];
1344 1447
1345 [self updateCurrentBackForwardListItemHolder]; 1448 [self updateCurrentBackForwardListItemHolder];
1346 1449
1347 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) 1450 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
1348 [self updateSSLStatusForCurrentNavigationItem]; 1451 [self updateSSLStatusForCurrentNavigationItem];
1349 #endif 1452 #endif
1453
1454 // Report cases where SSL cert is missing for a secure connection.
1455 if (_documentURL.SchemeIsCryptographic()) {
1456 scoped_refptr<net::X509Certificate> cert =
1457 web::CreateCertFromChain([_wkWebView certificateChain]);
1458 UMA_HISTOGRAM_BOOLEAN("WebController.WKWebViewHasCertForSecureConnection",
1459 cert);
1460 }
1350 } 1461 }
1351 1462
1352 - (void)webView:(WKWebView *)webView 1463 - (void)webView:(WKWebView *)webView
1353 didFinishNavigation:(WKNavigation *)navigation { 1464 didFinishNavigation:(WKNavigation *)navigation {
1354 DCHECK(!self.isHalted); 1465 DCHECK(!self.isHalted);
1355 // Trigger JavaScript driven post-document-load-completion tasks. 1466 // Trigger JavaScript driven post-document-load-completion tasks.
1356 // TODO(jyquinn): Investigate using WKUserScriptInjectionTimeAtDocumentEnd to 1467 // TODO(jyquinn): Investigate using WKUserScriptInjectionTimeAtDocumentEnd to
1357 // inject this material at the appropriate time rather than invoking here. 1468 // inject this material at the appropriate time rather than invoking here.
1358 web::EvaluateJavaScript(webView, 1469 web::EvaluateJavaScript(webView,
1359 @"__gCrWeb.didFinishNavigation()", nil); 1470 @"__gCrWeb.didFinishNavigation()", nil);
(...skipping 13 matching lines...) Expand all
1373 (void (^)(NSURLSessionAuthChallengeDisposition disposition, 1484 (void (^)(NSURLSessionAuthChallengeDisposition disposition,
1374 NSURLCredential *credential))completionHandler { 1485 NSURLCredential *credential))completionHandler {
1375 if (![challenge.protectionSpace.authenticationMethod 1486 if (![challenge.protectionSpace.authenticationMethod
1376 isEqual:NSURLAuthenticationMethodServerTrust]) { 1487 isEqual:NSURLAuthenticationMethodServerTrust]) {
1377 completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); 1488 completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
1378 return; 1489 return;
1379 } 1490 }
1380 1491
1381 SecTrustRef trust = challenge.protectionSpace.serverTrust; 1492 SecTrustRef trust = challenge.protectionSpace.serverTrust;
1382 scoped_refptr<net::X509Certificate> cert = web::CreateCertFromTrust(trust); 1493 scoped_refptr<net::X509Certificate> cert = web::CreateCertFromTrust(trust);
1494 // TODO(eugenebut): pass SecTrustRef instead of cert.
1383 [_certVerificationController 1495 [_certVerificationController
1384 decidePolicyForCert:cert 1496 decidePolicyForCert:cert
1385 host:challenge.protectionSpace.host 1497 host:challenge.protectionSpace.host
1386 completionHandler:^(web::CertAcceptPolicy policy, 1498 completionHandler:^(web::CertAcceptPolicy policy,
1387 net::CertStatus status) { 1499 net::CertStatus status) {
1388 completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, 1500 completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace,
1389 nil); 1501 nil);
1390 }]; 1502 }];
1391 } 1503 }
1392 1504
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
1479 placeholderText:defaultText 1591 placeholderText:defaultText
1480 requestURL: 1592 requestURL:
1481 net::GURLWithNSURL(frame.request.URL) 1593 net::GURLWithNSURL(frame.request.URL)
1482 completionHandler:completionHandler]; 1594 completionHandler:completionHandler];
1483 } else if (completionHandler) { 1595 } else if (completionHandler) {
1484 completionHandler(nil); 1596 completionHandler(nil);
1485 } 1597 }
1486 } 1598 }
1487 1599
1488 @end 1600 @end
OLDNEW
« no previous file with comments | « ios/web/public/ssl_status.h ('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