// 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 "chrome/browser/website_settings.h" #include #include #include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/browsing_data_cookie_helper.h" #include "chrome/browser/browsing_data_database_helper.h" #include "chrome/browser/browsing_data_local_storage_helper.h" #include "chrome/browser/browsing_data_indexed_db_helper.h" #include "chrome/browser/browsing_data_file_system_helper.h" #include "chrome/browser/browsing_data_server_bound_cert_helper.h" #include "chrome/browser/content_settings/content_settings_utils.h" #include "chrome/browser/content_settings/host_content_settings_map.h" #include "chrome/browser/content_settings/local_shared_objects_container.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ssl/ssl_error_info.h" #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" #include "chrome/browser/ui/website_settings_ui.h" #include "chrome/common/content_settings_pattern.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/cert_store.h" #include "content/public/common/ssl_status.h" #include "content/public/common/url_constants.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "net/base/cert_status_flags.h" #include "net/base/ssl_cipher_suite_names.h" #include "net/base/ssl_connection_status_flags.h" #include "net/base/x509_certificate.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #if defined(TOOLKIT_GTK) #include "chrome/browser/ui/gtk/website_settings_popup_gtk.h" #endif using content::BrowserThread; namespace { ContentSettingsType kPermissionType[] = { CONTENT_SETTINGS_TYPE_POPUPS, CONTENT_SETTINGS_TYPE_PLUGINS, CONTENT_SETTINGS_TYPE_NOTIFICATIONS, CONTENT_SETTINGS_TYPE_GEOLOCATION, }; size_t GetLocalStoredObjectCount( const LocalSharedObjectsContainer& local_shared_objects) { size_t count = 0; count += local_shared_objects.cookies()->GetCookieCount(); count += local_shared_objects.databases()->GetDatabaseCount(); count += local_shared_objects.file_systems()->GetFileSystemCount(); count += local_shared_objects.indexed_dbs()->GetIndexedDBCount(); count += local_shared_objects.local_storages()->GetLocalStorageCount(); count += local_shared_objects.server_bound_certs()->GetCertCount(); count += local_shared_objects.session_storages()->GetLocalStorageCount(); return count; } } // namespace WebsiteSettings::WebsiteSettings( WebsiteSettingsUI* ui, Profile* profile, TabSpecificContentSettings* tab_specific_content_settings, const GURL& url, const content::SSLStatus& ssl, content::CertStore* cert_store) : TabSpecificContentSettings::SiteDataObserver( tab_specific_content_settings), ui_(ui), site_url_(url), site_identity_status_(SITE_IDENTITY_STATUS_UNKNOWN), cert_id_(0), site_connection_status_(SITE_CONNECTION_STATUS_UNKNOWN), cert_store_(cert_store), content_settings_(profile->GetHostContentSettingsMap()) { ui_->SetPresenter(this); Init(profile, url, ssl); PresentSitePermissions(); PresentSiteData(); PresentSiteIdentity(); } WebsiteSettings::~WebsiteSettings() { } void WebsiteSettings::OnUIClosing() { ui_->SetPresenter(NULL); delete this; } void WebsiteSettings::OnSitePermissionChanged(ContentSettingsType type, ContentSetting setting) { ContentSettingsPattern primary_pattern; ContentSettingsPattern secondary_pattern; switch (type) { case CONTENT_SETTINGS_TYPE_GEOLOCATION: // TODO(markusheintz): The rule we create here should also change the // location permission for iframed content. primary_pattern = ContentSettingsPattern::FromURLNoWildcard(site_url_); secondary_pattern = ContentSettingsPattern::FromURLNoWildcard(site_url_); break; case CONTENT_SETTINGS_TYPE_NOTIFICATIONS: primary_pattern = ContentSettingsPattern::FromURLNoWildcard(site_url_); secondary_pattern = ContentSettingsPattern::Wildcard(); break; case CONTENT_SETTINGS_TYPE_PLUGINS: case CONTENT_SETTINGS_TYPE_POPUPS: primary_pattern = ContentSettingsPattern::FromURL(site_url_); secondary_pattern = ContentSettingsPattern::Wildcard(); break; default: NOTREACHED() << "ContentSettingsType " << type << "is not supported."; break; } // Permission settings are specified via rules. There exists always at least // one rule for the default setting. Get the rule that currently defines // the permission for the given permission |type|. Then test whether the // existing rule is more specific than the rule we are about to create. If // the existing rule is more specific, than change the existing rule instead // of creating a new rule that would be hidden behind the existing rule. content_settings::SettingInfo info; scoped_ptr v(content_settings_->GetWebsiteSetting( site_url_, site_url_, type, "", &info)); DCHECK(info.source == content_settings::SETTING_SOURCE_USER); ContentSettingsPattern::Relation r1 = info.primary_pattern.Compare(primary_pattern); DCHECK(r1 != ContentSettingsPattern::DISJOINT_ORDER_POST && r1 != ContentSettingsPattern::DISJOINT_ORDER_PRE); if (r1 == ContentSettingsPattern::PREDECESSOR) { primary_pattern = info.primary_pattern; } else if (r1 == ContentSettingsPattern::IDENTITY) { ContentSettingsPattern::Relation r2 = info.secondary_pattern.Compare(secondary_pattern); DCHECK(r2 != ContentSettingsPattern::DISJOINT_ORDER_POST && r2 != ContentSettingsPattern::DISJOINT_ORDER_PRE); if (r2 == ContentSettingsPattern::PREDECESSOR) secondary_pattern = info.secondary_pattern; } Value* value = NULL; if (setting != CONTENT_SETTING_DEFAULT) value = Value::CreateIntegerValue(setting); content_settings_->SetWebsiteSetting( primary_pattern, secondary_pattern, type, "", value); } void WebsiteSettings::OnSiteDataAccessed() { PresentSiteData(); } void WebsiteSettings::Init(Profile* profile, const GURL& url, const content::SSLStatus& ssl) { if (url.SchemeIs(chrome::kChromeUIScheme)) { site_identity_status_ = SITE_IDENTITY_STATUS_INTERNAL_PAGE; site_identity_details_ = l10n_util::GetStringUTF16(IDS_PAGE_INFO_INTERNAL_PAGE); site_connection_status_ = SITE_CONNECTION_STATUS_INTERNAL_PAGE; return; } scoped_refptr cert; // Identity section. string16 subject_name(UTF8ToUTF16(url.host())); if (subject_name.empty()) { subject_name.assign( l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY)); } cert_id_ = ssl.cert_id; if (ssl.cert_id && cert_store_->RetrieveCert(ssl.cert_id, &cert) && (!net::IsCertStatusError(ssl.cert_status) || net::IsCertStatusMinorError(ssl.cert_status))) { // There are no major errors. Check for minor errors. if (net::IsCertStatusMinorError(ssl.cert_status)) { site_identity_status_ = SITE_IDENTITY_STATUS_CERT_REVOCATION_UNKNOWN; string16 issuer_name(UTF8ToUTF16(cert->issuer().GetDisplayName())); if (issuer_name.empty()) { issuer_name.assign(l10n_util::GetStringUTF16( IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY)); } site_identity_details_.assign(l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY, issuer_name)); site_identity_details_ += ASCIIToUTF16("\n\n"); if (ssl.cert_status & net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION) { site_identity_details_ += l10n_util::GetStringUTF16( IDS_PAGE_INFO_SECURITY_TAB_UNABLE_TO_CHECK_REVOCATION); } else if (ssl.cert_status & net::CERT_STATUS_NO_REVOCATION_MECHANISM) { site_identity_details_ += l10n_util::GetStringUTF16( IDS_PAGE_INFO_SECURITY_TAB_NO_REVOCATION_MECHANISM); } else { NOTREACHED() << "Need to specify string for this warning"; } } else if (ssl.cert_status & net::CERT_STATUS_IS_EV) { // EV HTTPS page. site_identity_status_ = SITE_IDENTITY_STATUS_EV_CERT; DCHECK(!cert->subject().organization_names.empty()); organization_name_ = UTF8ToUTF16(cert->subject().organization_names[0]); // An EV Cert is required to have a city (localityName) and country but // state is "if any". DCHECK(!cert->subject().locality_name.empty()); DCHECK(!cert->subject().country_name.empty()); string16 locality; if (!cert->subject().state_or_province_name.empty()) { locality = l10n_util::GetStringFUTF16( IDS_PAGEINFO_ADDRESS, UTF8ToUTF16(cert->subject().locality_name), UTF8ToUTF16(cert->subject().state_or_province_name), UTF8ToUTF16(cert->subject().country_name)); } else { locality = l10n_util::GetStringFUTF16( IDS_PAGEINFO_PARTIAL_ADDRESS, UTF8ToUTF16(cert->subject().locality_name), UTF8ToUTF16(cert->subject().country_name)); } DCHECK(!cert->subject().organization_names.empty()); site_identity_details_.assign(l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY_EV, UTF8ToUTF16(cert->subject().organization_names[0]), locality, UTF8ToUTF16(cert->issuer().GetDisplayName()))); } else if (ssl.cert_status & net::CERT_STATUS_IS_DNSSEC) { // DNSSEC authenticated page. site_identity_status_ = SITE_IDENTITY_STATUS_DNSSEC_CERT; site_identity_details_.assign(l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY, UTF8ToUTF16("DNSSEC"))); } else { // Non-EV OK HTTPS page. site_identity_status_ = SITE_IDENTITY_STATUS_CERT; string16 issuer_name(UTF8ToUTF16(cert->issuer().GetDisplayName())); if (issuer_name.empty()) { issuer_name.assign(l10n_util::GetStringUTF16( IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY)); } site_identity_details_.assign(l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY, issuer_name)); } } else { // HTTP or HTTPS with errors (not warnings). site_identity_details_.assign(l10n_util::GetStringUTF16( IDS_PAGE_INFO_SECURITY_TAB_INSECURE_IDENTITY)); if (ssl.security_style == content::SECURITY_STYLE_UNAUTHENTICATED) site_identity_status_ = SITE_IDENTITY_STATUS_NO_CERT; else site_identity_status_ = SITE_IDENTITY_STATUS_ERROR; const string16 bullet = UTF8ToUTF16("\n • "); std::vector errors; SSLErrorInfo::GetErrorsForCertStatus(ssl.cert_id, ssl.cert_status, url, &errors); for (size_t i = 0; i < errors.size(); ++i) { site_identity_details_ += bullet; site_identity_details_ += errors[i].short_description(); } if (ssl.cert_status & net::CERT_STATUS_NON_UNIQUE_NAME) { site_identity_details_ += ASCIIToUTF16("\n\n"); site_identity_details_ += l10n_util::GetStringUTF16( IDS_PAGE_INFO_SECURITY_TAB_NON_UNIQUE_NAME); } } // Site Connection // We consider anything less than 80 bits encryption to be weak encryption. // TODO(wtc): Bug 1198735: report mixed/unsafe content for unencrypted and // weakly encrypted connections. site_connection_status_ = SITE_CONNECTION_STATUS_UNKNOWN; if (!ssl.cert_id) { // Not HTTPS. DCHECK_EQ(ssl.security_style, content::SECURITY_STYLE_UNAUTHENTICATED); if (ssl.security_style == content::SECURITY_STYLE_UNAUTHENTICATED) site_connection_status_ = SITE_CONNECTION_STATUS_UNENCRYPTED; else site_connection_status_ = SITE_CONNECTION_STATUS_ENCRYPTED_ERROR; site_connection_details_.assign(l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_NOT_ENCRYPTED_CONNECTION_TEXT, subject_name)); } else if (ssl.security_bits < 0) { // Security strength is unknown. Say nothing. site_connection_status_ = SITE_CONNECTION_STATUS_ENCRYPTED_ERROR; } else if (ssl.security_bits == 0) { DCHECK_NE(ssl.security_style, content::SECURITY_STYLE_UNAUTHENTICATED); site_connection_status_ = SITE_CONNECTION_STATUS_ENCRYPTED_ERROR; site_connection_details_.assign(l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_NOT_ENCRYPTED_CONNECTION_TEXT, subject_name)); } else if (ssl.security_bits < 80) { site_connection_status_ = SITE_CONNECTION_STATUS_ENCRYPTED_ERROR; site_connection_details_.assign(l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_WEAK_ENCRYPTION_CONNECTION_TEXT, subject_name)); } else { site_connection_status_ = SITE_CONNECTION_STATUS_ENCRYPTED; site_connection_details_.assign(l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_CONNECTION_TEXT, subject_name, base::IntToString16(ssl.security_bits))); if (ssl.content_status) { bool ran_insecure_content = !!(ssl.content_status & content::SSLStatus::RAN_INSECURE_CONTENT); site_connection_status_ = ran_insecure_content ? SITE_CONNECTION_STATUS_ENCRYPTED_ERROR : SITE_CONNECTION_STATUS_MIXED_CONTENT; site_connection_details_.assign(l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_SENTENCE_LINK, site_connection_details_, 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 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); site_connection_details_ += ASCIIToUTF16("\n\n"); site_connection_details_ += l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_SSL_VERSION, ASCIIToUTF16(ssl_version_str)); bool did_fallback = (ssl.connection_status & net::SSL_CONNECTION_SSL3_FALLBACK) != 0; bool no_renegotiation = (ssl.connection_status & net::SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION) != 0; const char *key_exchange, *cipher, *mac; net::SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, cipher_suite); site_connection_details_ += ASCIIToUTF16("\n\n"); site_connection_details_ += l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTION_DETAILS, ASCIIToUTF16(cipher), ASCIIToUTF16(mac), ASCIIToUTF16(key_exchange)); site_connection_details_ += ASCIIToUTF16("\n\n"); uint8 compression_id = net::SSLConnectionStatusToCompression(ssl.connection_status); if (compression_id) { const char* compression; net::SSLCompressionToString(&compression, compression_id); site_connection_details_ += l10n_util::GetStringFUTF16( IDS_PAGE_INFO_SECURITY_TAB_COMPRESSION_DETAILS, ASCIIToUTF16(compression)); } else { site_connection_details_ += l10n_util::GetStringUTF16( IDS_PAGE_INFO_SECURITY_TAB_NO_COMPRESSION); } if (did_fallback) { // For now, only SSLv3 fallback will trigger a warning icon. if (site_connection_status_ < SITE_CONNECTION_STATUS_MIXED_CONTENT) site_connection_status_ = SITE_CONNECTION_STATUS_MIXED_CONTENT; site_connection_details_ += ASCIIToUTF16("\n\n"); site_connection_details_ += l10n_util::GetStringUTF16( IDS_PAGE_INFO_SECURITY_TAB_FALLBACK_MESSAGE); } if (no_renegotiation) { site_connection_details_ += ASCIIToUTF16("\n\n"); site_connection_details_ += l10n_util::GetStringUTF16( IDS_PAGE_INFO_SECURITY_TAB_RENEGOTIATION_MESSAGE); } } } void WebsiteSettings::PresentSitePermissions() { PermissionInfoList permission_info_list; WebsiteSettingsUI::PermissionInfo permission_info; for (size_t i = 0; i < arraysize(kPermissionType); ++i) { permission_info.type = kPermissionType[i]; content_settings::SettingInfo info; scoped_ptr value(content_settings_->GetWebsiteSetting( site_url_, site_url_, permission_info.type, "", &info)); DCHECK(value.get()); permission_info.setting = content_settings::ValueToContentSetting(value.get()); if (permission_info.setting != CONTENT_SETTING_ASK) { if (info.primary_pattern == ContentSettingsPattern::Wildcard() && info.secondary_pattern == ContentSettingsPattern::Wildcard()) { permission_info.default_setting = permission_info.setting; permission_info.setting = CONTENT_SETTING_DEFAULT; } else { permission_info.default_setting = content_settings_->GetDefaultContentSetting(permission_info.type, NULL); } permission_info_list.push_back(permission_info); } } ui_->SetPermissionInfo(permission_info_list); } void WebsiteSettings::PresentSiteData() { CookieInfoList cookie_info_list; WebsiteSettingsUI::CookieInfo cookie_info; cookie_info.cookie_source = site_url_.host(); cookie_info.allowed = GetLocalStoredObjectCount( tab_specific_content_settings()->allowed_local_shared_objects()); cookie_info.blocked = GetLocalStoredObjectCount( tab_specific_content_settings()->blocked_local_shared_objects()); cookie_info_list.push_back(cookie_info); ui_->SetCookieInfo(cookie_info_list); } void WebsiteSettings::PresentSiteIdentity() { // After initialization the status about the site's connection // and it's identity must be available. DCHECK_NE(site_identity_status_, SITE_IDENTITY_STATUS_UNKNOWN); DCHECK_NE(site_connection_status_, SITE_CONNECTION_STATUS_UNKNOWN); WebsiteSettingsUI::IdentityInfo info; if (site_identity_status_ == SITE_IDENTITY_STATUS_EV_CERT) info.site_identity = UTF16ToUTF8(organization_name()); else info.site_identity = site_url_.host(); info.connection_status = site_connection_status_; info.connection_status_description = UTF16ToUTF8(site_connection_details_); info.identity_status = site_identity_status_; info.identity_status_description = UTF16ToUTF8(site_identity_details_); info.cert_id = cert_id_; ui_->SetIdentityInfo(info); } // static void WebsiteSettings::Show(gfx::NativeWindow parent, Profile* profile, TabContentsWrapper* tab_contents_wrapper, const GURL& url, const content::SSLStatus& ssl) { #if defined(TOOLKIT_GTK) // The WebsiteSettingsModel will delete itself after the UI is closed. new WebsiteSettings(new WebsiteSettingsPopupGtk(parent, profile, tab_contents_wrapper), profile, tab_contents_wrapper->content_settings(), url, ssl, content::CertStore::GetInstance()); #endif }