blob: a524461157a22f0619445af7e6d2dfb405ab9fc6 [file] [log] [blame]
// Copyright (c) 2012 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.
#include "ios/chrome/browser/ui/omnibox/page_info_model.h"
#include <stdint.h>
#include <string>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/i18n/time_formatting.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "components/keyed_service/core/service_access_type.h"
#include "components/ssl_errors/error_info.h"
#include "components/strings/grit/components_chromium_strings.h"
#include "components/strings/grit/components_google_chrome_strings.h"
#include "components/strings/grit/components_strings.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/chrome/browser/chrome_url_constants.h"
#include "ios/chrome/browser/ui/omnibox/page_info_model_observer.h"
#include "ios/chrome/grit/ios_chromium_strings.h"
#include "ios/chrome/grit/ios_strings.h"
#include "ios/chrome/grit/ios_theme_resources.h"
#include "ios/web/public/ssl_status.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/x509_certificate.h"
#include "net/ssl/ssl_cipher_suite_names.h"
#include "net/ssl/ssl_connection_status_flags.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
// TODO(crbug.com/227827) Merge 178763: PageInfoModel has been removed in
// upstream; check if we should use PageInfoModel.
PageInfoModel::PageInfoModel(ios::ChromeBrowserState* browser_state,
const GURL& url,
const web::SSLStatus& ssl,
PageInfoModelObserver* observer)
: observer_(observer) {
if (url.SchemeIs(kChromeUIScheme)) {
if (url.host() == kChromeUIOfflineHost) {
sections_.push_back(SectionInfo(
ICON_STATE_OFFLINE_PAGE,
l10n_util::GetStringUTF16(IDS_IOS_PAGE_INFO_OFFLINE_TITLE),
l10n_util::GetStringUTF16(IDS_IOS_PAGE_INFO_OFFLINE_PAGE),
SECTION_INFO_INTERNAL_PAGE, BUTTON_RELOAD));
} else {
sections_.push_back(
SectionInfo(ICON_STATE_INTERNAL_PAGE, base::string16(),
l10n_util::GetStringUTF16(IDS_PAGE_INFO_INTERNAL_PAGE),
SECTION_INFO_INTERNAL_PAGE, BUTTON_NONE));
}
return;
}
SectionStateIcon icon_id = ICON_STATE_OK;
base::string16 headline;
base::string16 description;
// Identity section.
base::string16 subject_name(base::UTF8ToUTF16(url.host()));
bool empty_subject_name = false;
if (subject_name.empty()) {
subject_name.assign(
l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY));
empty_subject_name = true;
}
bool is_cert_present = !!ssl.certificate;
bool is_major_cert_error = net::IsCertStatusError(ssl.cert_status) &&
!net::IsCertStatusMinorError(ssl.cert_status);
// It is possible to have |SECURITY_STYLE_AUTHENTICATION_BROKEN| and non-error
// |cert_status| for WKWebView because |security_style| and |cert_status| are
// calculated using different API, which may lead to different cert
// verification results.
if (is_cert_present && !is_major_cert_error &&
ssl.security_style != web::SECURITY_STYLE_AUTHENTICATION_BROKEN) {
// There are no major errors. Check for minor errors.
if (net::IsCertStatusMinorError(ssl.cert_status)) {
base::string16 issuer_name(
base::UTF8ToUTF16(ssl.certificate->issuer().GetDisplayName()));
if (issuer_name.empty()) {
issuer_name.assign(l10n_util::GetStringUTF16(
IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY));
}
description.assign(l10n_util::GetStringFUTF16(
IDS_IOS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY, issuer_name));
description += base::ASCIIToUTF16("\n\n");
if (ssl.cert_status & net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION) {
description += l10n_util::GetStringUTF16(
IDS_PAGE_INFO_SECURITY_TAB_UNABLE_TO_CHECK_REVOCATION);
} else if (ssl.cert_status & net::CERT_STATUS_NO_REVOCATION_MECHANISM) {
description += l10n_util::GetStringUTF16(
IDS_PAGE_INFO_SECURITY_TAB_NO_REVOCATION_MECHANISM);
} else {
NOTREACHED() << "Need to specify string for this warning";
}
icon_id = ICON_STATE_INFO;
} else {
// OK HTTPS page.
DCHECK(!(ssl.cert_status & net::CERT_STATUS_IS_EV))
<< "Extended Validation should be disabled";
if (empty_subject_name)
headline.clear(); // Don't display any title.
else
headline.assign(subject_name);
base::string16 issuer_name(
base::UTF8ToUTF16(ssl.certificate->issuer().GetDisplayName()));
if (issuer_name.empty()) {
issuer_name.assign(l10n_util::GetStringUTF16(
IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY));
}
description.assign(l10n_util::GetStringFUTF16(
IDS_IOS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY, issuer_name));
}
if (ssl.cert_status & net::CERT_STATUS_SHA1_SIGNATURE_PRESENT) {
icon_id = ICON_STATE_INFO;
description +=
base::UTF8ToUTF16("\n\n") +
l10n_util::GetStringUTF16(
IDS_PAGE_INFO_SECURITY_TAB_DEPRECATED_SIGNATURE_ALGORITHM);
}
} else {
// HTTP or HTTPS with errors (not warnings).
description.assign(l10n_util::GetStringUTF16(
IDS_PAGE_INFO_SECURITY_TAB_INSECURE_IDENTITY));
icon_id = ssl.security_style == web::SECURITY_STYLE_UNAUTHENTICATED
? ICON_NONE
: ICON_STATE_ERROR;
const base::string16 bullet = base::UTF8ToUTF16("\n • ");
std::vector<ssl_errors::ErrorInfo> errors;
ssl_errors::ErrorInfo::GetErrorsForCertStatus(
ssl.certificate, ssl.cert_status, url, &errors);
for (size_t i = 0; i < errors.size(); ++i) {
description += bullet;
description += errors[i].short_description();
}
if (ssl.cert_status & net::CERT_STATUS_NON_UNIQUE_NAME) {
description += base::ASCIIToUTF16("\n\n");
description +=
l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_NON_UNIQUE_NAME);
}
}
sections_.push_back(SectionInfo(icon_id, headline, description,
SECTION_INFO_IDENTITY,
BUTTON_SHOW_SECURITY_HELP));
// Connection section.
icon_id = ICON_STATE_OK;
headline.clear();
description.clear();
if (!ssl.certificate) {
// Not HTTPS.
icon_id = ssl.security_style == web::SECURITY_STYLE_UNAUTHENTICATED
? ICON_NONE
: ICON_STATE_ERROR;
description.assign(l10n_util::GetStringFUTF16(
IDS_PAGE_INFO_SECURITY_TAB_NOT_ENCRYPTED_CONNECTION_TEXT,
subject_name));
} else if (ssl.security_bits < 0) {
if (ssl.content_status == web::SSLStatus::DISPLAYED_INSECURE_CONTENT) {
DCHECK(description.empty());
// For WKWebView security_bits flag is always -1, and description is empty
// because ciphersuite is unknown. On iOS9 WKWebView blocks active
// mixed content, so warning should be about page look, not about page
// behavior.
icon_id = ICON_NONE;
description.assign(l10n_util::GetStringUTF16(
IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_INSECURE_CONTENT_WARNING));
} else {
// Security strength is unknown. Say nothing.
icon_id = ICON_STATE_ERROR;
}
} else if (ssl.security_bits == 0) {
DCHECK_NE(ssl.security_style, web::SECURITY_STYLE_UNAUTHENTICATED);
icon_id = ICON_STATE_ERROR;
description.assign(l10n_util::GetStringFUTF16(
IDS_PAGE_INFO_SECURITY_TAB_NOT_ENCRYPTED_CONNECTION_TEXT,
subject_name));
} else {
if (net::SSLConnectionStatusToVersion(ssl.connection_status) >=
net::SSL_CONNECTION_VERSION_TLS1_2 &&
(net::OBSOLETE_SSL_NONE ==
net::ObsoleteSSLStatus(
net::SSLConnectionStatusToCipherSuite(ssl.connection_status)))) {
description.assign(l10n_util::GetStringFUTF16(
IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_CONNECTION_TEXT, subject_name));
} else {
description.assign(l10n_util::GetStringFUTF16(
IDS_PAGE_INFO_SECURITY_TAB_WEAK_ENCRYPTION_CONNECTION_TEXT,
subject_name));
}
if (ssl.content_status) {
bool ran_insecure_content = false; // Always false on iOS.
icon_id = ran_insecure_content ? ICON_STATE_ERROR : ICON_NONE;
description.assign(l10n_util::GetStringFUTF16(
IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_SENTENCE_LINK, description,
l10n_util::GetStringUTF16(
ran_insecure_content
? IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_INSECURE_CONTENT_ERROR
: IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_INSECURE_CONTENT_WARNING)));
}
}
uint16_t cipher_suite =
net::SSLConnectionStatusToCipherSuite(ssl.connection_status);
if (ssl.security_bits > 0 && cipher_suite) {
int ssl_version = net::SSLConnectionStatusToVersion(ssl.connection_status);
const char* ssl_version_str;
net::SSLVersionToString(&ssl_version_str, ssl_version);
description += base::ASCIIToUTF16("\n\n");
description +=
l10n_util::GetStringFUTF16(IDS_PAGE_INFO_SECURITY_TAB_SSL_VERSION,
base::ASCIIToUTF16(ssl_version_str));
bool no_renegotiation =
(ssl.connection_status &
net::SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION) != 0;
const char *key_exchange, *cipher, *mac;
bool is_aead;
bool is_tls13;
net::SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, &is_aead,
&is_tls13, cipher_suite);
description += base::ASCIIToUTF16("\n\n");
if (is_aead) {
description += l10n_util::GetStringFUTF16(
IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTION_DETAILS_AEAD,
base::ASCIIToUTF16(cipher), base::ASCIIToUTF16(key_exchange));
} else {
description += l10n_util::GetStringFUTF16(
IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTION_DETAILS,
base::ASCIIToUTF16(cipher), base::ASCIIToUTF16(mac),
base::ASCIIToUTF16(key_exchange));
}
if (no_renegotiation) {
description += base::ASCIIToUTF16("\n\n");
description += l10n_util::GetStringUTF16(
IDS_PAGE_INFO_SECURITY_TAB_RENEGOTIATION_MESSAGE);
}
}
if (!description.empty()) {
sections_.push_back(SectionInfo(icon_id, headline, description,
SECTION_INFO_CONNECTION,
BUTTON_SHOW_SECURITY_HELP));
}
if (ssl.certificate) {
certificate_label_ =
l10n_util::GetStringUTF16(IDS_PAGEINFO_CERT_INFO_BUTTON);
}
}
PageInfoModel::~PageInfoModel() {}
int PageInfoModel::GetSectionCount() {
return sections_.size();
}
PageInfoModel::SectionInfo PageInfoModel::GetSectionInfo(int index) {
DCHECK(index < static_cast<int>(sections_.size()));
return sections_[index];
}
gfx::Image* PageInfoModel::GetIconImage(SectionStateIcon icon_id) {
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
switch (icon_id) {
case ICON_NONE:
case ICON_STATE_INTERNAL_PAGE:
return nullptr;
case ICON_STATE_OK:
return &rb.GetNativeImageNamed(IDR_IOS_PAGEINFO_GOOD);
case ICON_STATE_ERROR:
return &rb.GetNativeImageNamed(IDR_IOS_PAGEINFO_BAD);
case ICON_STATE_INFO:
return &rb.GetNativeImageNamed(IDR_IOS_PAGEINFO_INFO);
case ICON_STATE_OFFLINE_PAGE:
return &rb.GetNativeImageNamed(IDR_IOS_OMNIBOX_OFFLINE);
}
}
base::string16 PageInfoModel::GetCertificateLabel() const {
return certificate_label_;
}
PageInfoModel::PageInfoModel() : observer_(NULL) {}