| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "ios/web/net/crw_ssl_status_updater.h" |
| |
| #import "base/mac/scoped_nsobject.h" |
| #import "base/strings/sys_string_conversions.h" |
| #include "ios/web/navigation/navigation_manager_util.h" |
| #import "ios/web/public/navigation_item.h" |
| #import "ios/web/public/navigation_manager.h" |
| #include "ios/web/public/ssl_status.h" |
| #import "ios/web/web_state/wk_web_view_security_util.h" |
| #include "net/cert/x509_certificate.h" |
| #include "url/gurl.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| using base::ScopedCFTypeRef; |
| using net::CertStatus; |
| using web::SecurityStyle; |
| |
| @interface CRWSSLStatusUpdater () { |
| // DataSource for CRWSSLStatusUpdater. |
| __weak id<CRWSSLStatusUpdaterDataSource> _dataSource; |
| } |
| |
| // Unowned pointer to web::NavigationManager. |
| @property(nonatomic, readonly) web::NavigationManager* navigationManager; |
| |
| // Updates |security_style| and |cert_status| for the NavigationItem with ID |
| // |navigationItemID|, if URL and certificate chain still match |host| and |
| // |certChain|. |
| - (void)updateSSLStatusForItemWithID:(int)navigationItemID |
| trust:(ScopedCFTypeRef<SecTrustRef>)trust |
| host:(NSString*)host |
| withSecurityStyle:(SecurityStyle)style |
| certStatus:(CertStatus)certStatus; |
| |
| // Asynchronously obtains SSL status from given |secTrust| and |host| and |
| // updates current navigation item. Before scheduling update changes SSLStatus' |
| // cert_status and security_style to default. |
| - (void)scheduleSSLStatusUpdateUsingTrust:(ScopedCFTypeRef<SecTrustRef>)trust |
| host:(NSString*)host; |
| |
| // Notifies delegate about SSLStatus change. |
| - (void)didChangeSSLStatusForNavigationItem:(web::NavigationItem*)navItem; |
| |
| @end |
| |
| @implementation CRWSSLStatusUpdater |
| @synthesize navigationManager = _navigationManager; |
| @synthesize delegate = _delegate; |
| |
| #pragma mark - Public |
| |
| - (instancetype)initWithDataSource:(id<CRWSSLStatusUpdaterDataSource>)dataSource |
| navigationManager:(web::NavigationManager*)navigationManager { |
| DCHECK(dataSource); |
| DCHECK(navigationManager); |
| if (self = [super init]) { |
| _dataSource = dataSource; |
| _navigationManager = navigationManager; |
| } |
| return self; |
| } |
| |
| - (void)updateSSLStatusForNavigationItem:(web::NavigationItem*)item |
| withCertHost:(NSString*)host |
| trust:(ScopedCFTypeRef<SecTrustRef>)trust |
| hasOnlySecureContent:(BOOL)hasOnlySecureContent { |
| web::SSLStatus previousSSLStatus = item->GetSSL(); |
| |
| // Starting from iOS9 WKWebView blocks active mixed content, so if |
| // |hasOnlySecureContent| returns NO it means passive content. |
| item->GetSSL().content_status = |
| 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()) { |
| cert = web::CreateCertFromTrust(trust); |
| if (cert) { |
| scoped_refptr<net::X509Certificate> oldCert = item->GetSSL().certificate; |
| std::string oldHost = item->GetSSL().cert_status_host; |
| item->GetSSL().certificate = cert; |
| item->GetSSL().cert_status_host = base::SysNSStringToUTF8(host); |
| // Only recompute the SSLStatus information if the certificate or host has |
| // since changed. Host can be changed in case of redirect. |
| if (!oldCert || !oldCert->Equals(cert.get()) || |
| oldHost != item->GetSSL().cert_status_host) { |
| // Real SSL status is unknown, reset cert status and security style. |
| // They will be asynchronously updated in |
| // |scheduleSSLStatusUpdateUsingTrust:host:|. |
| item->GetSSL().cert_status = CertStatus(); |
| item->GetSSL().security_style = web::SECURITY_STYLE_UNKNOWN; |
| |
| [self scheduleSSLStatusUpdateUsingTrust:std::move(trust) host:host]; |
| } |
| } |
| } |
| |
| if (!cert) { |
| item->GetSSL().certificate = nullptr; |
| 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 didChangeSSLStatusForNavigationItem:item]; |
| } |
| } |
| |
| #pragma mark - Private |
| |
| - (void)updateSSLStatusForItemWithID:(int)navigationItemID |
| trust:(ScopedCFTypeRef<SecTrustRef>)trust |
| host:(NSString*)host |
| withSecurityStyle:(SecurityStyle)style |
| certStatus:(CertStatus)certStatus { |
| web::NavigationItem* item = |
| web::GetCommittedItemWithUniqueID(_navigationManager, navigationItemID); |
| if (!item) |
| return; |
| |
| // NavigationItem's UniqueID is preserved even after redirects, so |
| // checking that cert and URL match is necessary. |
| scoped_refptr<net::X509Certificate> cert(web::CreateCertFromTrust(trust)); |
| std::string GURLHost = base::SysNSStringToUTF8(host); |
| web::SSLStatus& SSLStatus = item->GetSSL(); |
| if (item->GetURL().SchemeIsCryptographic() && !!SSLStatus.certificate && |
| SSLStatus.certificate->Equals(cert.get()) && |
| item->GetURL().host() == GURLHost) { |
| web::SSLStatus previousSSLStatus = item->GetSSL(); |
| SSLStatus.cert_status = certStatus; |
| SSLStatus.security_style = style; |
| if (!previousSSLStatus.Equals(SSLStatus)) { |
| [self didChangeSSLStatusForNavigationItem:item]; |
| } |
| } |
| } |
| |
| - (void)scheduleSSLStatusUpdateUsingTrust:(ScopedCFTypeRef<SecTrustRef>)trust |
| host:(NSString*)host { |
| // Use Navigation Item's unique ID to locate requested item after |
| // obtaining cert status asynchronously. |
| int itemID = _navigationManager->GetLastCommittedItem()->GetUniqueID(); |
| |
| DCHECK(_dataSource); |
| __weak CRWSSLStatusUpdater* weakSelf = self; |
| [_dataSource SSLStatusUpdater:self |
| querySSLStatusForTrust:trust |
| host:host |
| completionHandler:^(SecurityStyle style, CertStatus certStatus) { |
| [weakSelf updateSSLStatusForItemWithID:itemID |
| trust:std::move(trust) |
| host:host |
| withSecurityStyle:style |
| certStatus:certStatus]; |
| }]; |
| } |
| |
| - (void)didChangeSSLStatusForNavigationItem:(web::NavigationItem*)navItem { |
| if ([_delegate respondsToSelector: |
| @selector(SSLStatusUpdater:didChangeSSLStatusForNavigationItem:)]) { |
| [_delegate SSLStatusUpdater:self |
| didChangeSSLStatusForNavigationItem:navItem]; |
| } |
| } |
| |
| @end |