blob: 9553204b55525fb914efcc9b2f370c605c25ae59 [file] [log] [blame]
[email protected]03ef4b2a2012-03-06 15:04:201// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]0b9fdd72012-04-04 10:00:335#include "chrome/browser/website_settings.h"
[email protected]03ef4b2a2012-03-06 15:04:206
7#include <string>
8#include <vector>
9
10#include "base/string_number_conversions.h"
11#include "base/utf_string_conversions.h"
[email protected]0b9fdd72012-04-04 10:00:3312#include "base/values.h"
13#include "chrome/browser/content_settings/content_settings_utils.h"
14#include "chrome/browser/content_settings/host_content_settings_map.h"
[email protected]03ef4b2a2012-03-06 15:04:2015#include "chrome/browser/profiles/profile.h"
16#include "chrome/browser/ssl/ssl_error_info.h"
[email protected]0b9fdd72012-04-04 10:00:3317#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
18#include "chrome/browser/ui/website_settings_ui.h"
19#include "chrome/common/content_settings_pattern.h"
20#include "content/public/browser/browser_thread.h"
[email protected]b59c6cf02012-03-12 20:51:4221#include "content/public/browser/cert_store.h"
[email protected]03ef4b2a2012-03-06 15:04:2022#include "content/public/common/ssl_status.h"
23#include "content/public/common/url_constants.h"
24#include "grit/chromium_strings.h"
25#include "grit/generated_resources.h"
26#include "net/base/cert_status_flags.h"
27#include "net/base/ssl_cipher_suite_names.h"
28#include "net/base/ssl_connection_status_flags.h"
29#include "net/base/x509_certificate.h"
30#include "ui/base/l10n/l10n_util.h"
31#include "ui/base/resource/resource_bundle.h"
32
[email protected]0b9fdd72012-04-04 10:00:3333#if defined(TOOLKIT_USES_GTK)
34#include "chrome/browser/ui/gtk/website_settings_popup_gtk.h"
35#endif
36
37using content::BrowserThread;
38
39namespace {
40
41ContentSettingsType kPermissionType[] = {
42 CONTENT_SETTINGS_TYPE_POPUPS,
43 CONTENT_SETTINGS_TYPE_PLUGINS,
44 CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
45 CONTENT_SETTINGS_TYPE_GEOLOCATION,
46};
47
48} // namespace
49
50WebsiteSettings::WebsiteSettings(
51 WebsiteSettingsUI* ui,
52 Profile* profile,
53 const GURL& url,
54 const content::SSLStatus& ssl,
55 content::CertStore* cert_store)
56 : ui_(ui),
57 site_url_(url),
58 site_identity_status_(SITE_IDENTITY_STATUS_UNKNOWN),
[email protected]03ef4b2a2012-03-06 15:04:2059 site_connection_status_(SITE_CONNECTION_STATUS_UNKNOWN),
[email protected]0b9fdd72012-04-04 10:00:3360 cert_store_(cert_store),
61 content_settings_(profile->GetHostContentSettingsMap()) {
62 ui_->SetPresenter(this);
[email protected]03ef4b2a2012-03-06 15:04:2063 Init(profile, url, ssl);
64 // After initialization the status about the site's connection
65 // and it's identity must be available.
66 DCHECK_NE(site_identity_status_, SITE_IDENTITY_STATUS_UNKNOWN);
67 DCHECK_NE(site_connection_status_, SITE_CONNECTION_STATUS_UNKNOWN);
[email protected]0b9fdd72012-04-04 10:00:3368
69 // TODO(markusheintz): Add the strings below to the grd file once a decision
70 // has been made about the exact wording.
71 std::string site_info;
72 switch (site_identity_status_) {
73 case WebsiteSettings::SITE_IDENTITY_STATUS_CERT:
74 case WebsiteSettings::SITE_IDENTITY_STATUS_DNSSEC_CERT:
75 site_info = "Identity verified";
76 break;
77 case WebsiteSettings::SITE_IDENTITY_STATUS_EV_CERT:
78 site_info = UTF16ToUTF8(organization_name());
79 site_info += " - Identity verified";
80 break;
81 default:
82 site_info = "Identity not verified";
83 break;
84 }
85 ui_->SetSiteInfo(site_info);
86
87 PresentSitePermissions();
[email protected]03ef4b2a2012-03-06 15:04:2088}
89
[email protected]0b9fdd72012-04-04 10:00:3390WebsiteSettings::~WebsiteSettings() {
[email protected]03ef4b2a2012-03-06 15:04:2091}
92
[email protected]0b9fdd72012-04-04 10:00:3393void WebsiteSettings::PresentSitePermissions() {
94 PermissionInfoList permission_info_list;
95
96 WebsiteSettingsUI::PermissionInfo permission_info;
97 for (size_t i = 0; i < arraysize(kPermissionType); ++i) {
98 permission_info.type = kPermissionType[i];
99
100 content_settings::SettingInfo info;
101 scoped_ptr<Value> value(content_settings_->GetWebsiteSetting(
102 site_url_, site_url_, permission_info.type, "", &info));
103 DCHECK(value.get());
104 permission_info.setting =
105 content_settings::ValueToContentSetting(value.get());
106
107 if (permission_info.setting != CONTENT_SETTING_ASK) {
108 if (info.primary_pattern == ContentSettingsPattern::Wildcard() &&
109 info.secondary_pattern == ContentSettingsPattern::Wildcard()) {
110 permission_info.default_setting = permission_info.setting;
111 permission_info.setting = CONTENT_SETTING_DEFAULT;
112 } else {
113 permission_info.default_setting =
114 content_settings_->GetDefaultContentSetting(permission_info.type,
115 NULL);
116 }
117 permission_info_list.push_back(permission_info);
118 }
119 }
120
121 ui_->SetPermissionInfo(permission_info_list);
122}
123
124void WebsiteSettings::Init(Profile* profile,
125 const GURL& url,
126 const content::SSLStatus& ssl) {
[email protected]03ef4b2a2012-03-06 15:04:20127 if (url.SchemeIs(chrome::kChromeUIScheme)) {
128 site_identity_status_ = SITE_IDENTITY_STATUS_INTERNAL_PAGE;
129 site_identity_details_ =
130 l10n_util::GetStringUTF16(IDS_PAGE_INFO_INTERNAL_PAGE);
131 site_connection_status_ = SITE_CONNECTION_STATUS_INTERNAL_PAGE;
132 return;
133 }
134
135 scoped_refptr<net::X509Certificate> cert;
136
137 // Identity section.
138 string16 subject_name(UTF8ToUTF16(url.host()));
[email protected]03ef4b2a2012-03-06 15:04:20139 if (subject_name.empty()) {
140 subject_name.assign(
141 l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY));
[email protected]03ef4b2a2012-03-06 15:04:20142 }
143
144 if (ssl.cert_id &&
145 cert_store_->RetrieveCert(ssl.cert_id, &cert) &&
146 (!net::IsCertStatusError(ssl.cert_status) ||
147 net::IsCertStatusMinorError(ssl.cert_status))) {
148 // There are no major errors. Check for minor errors.
149 if (net::IsCertStatusMinorError(ssl.cert_status)) {
150 site_identity_status_ = SITE_IDENTITY_STATUS_CERT_REVOCATION_UNKNOWN;
151 string16 issuer_name(UTF8ToUTF16(cert->issuer().GetDisplayName()));
152 if (issuer_name.empty()) {
153 issuer_name.assign(l10n_util::GetStringUTF16(
154 IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY));
155 }
156 site_identity_details_.assign(l10n_util::GetStringFUTF16(
157 IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY, issuer_name));
158
159 site_identity_details_ += ASCIIToUTF16("\n\n");
160 if (ssl.cert_status & net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION) {
161 site_identity_details_ += l10n_util::GetStringUTF16(
162 IDS_PAGE_INFO_SECURITY_TAB_UNABLE_TO_CHECK_REVOCATION);
163 } else if (ssl.cert_status & net::CERT_STATUS_NO_REVOCATION_MECHANISM) {
164 site_identity_details_ += l10n_util::GetStringUTF16(
165 IDS_PAGE_INFO_SECURITY_TAB_NO_REVOCATION_MECHANISM);
166 } else {
167 NOTREACHED() << "Need to specify string for this warning";
168 }
169 } else if (ssl.cert_status & net::CERT_STATUS_IS_EV) {
170 // EV HTTPS page.
171 site_identity_status_ = SITE_IDENTITY_STATUS_EV_CERT;
172 DCHECK(!cert->subject().organization_names.empty());
173 organization_name_ = UTF8ToUTF16(cert->subject().organization_names[0]);
174 // An EV Cert is required to have a city (localityName) and country but
175 // state is "if any".
176 DCHECK(!cert->subject().locality_name.empty());
177 DCHECK(!cert->subject().country_name.empty());
178 string16 locality;
179 if (!cert->subject().state_or_province_name.empty()) {
180 locality = l10n_util::GetStringFUTF16(
181 IDS_PAGEINFO_ADDRESS,
182 UTF8ToUTF16(cert->subject().locality_name),
183 UTF8ToUTF16(cert->subject().state_or_province_name),
184 UTF8ToUTF16(cert->subject().country_name));
185 } else {
186 locality = l10n_util::GetStringFUTF16(
187 IDS_PAGEINFO_PARTIAL_ADDRESS,
188 UTF8ToUTF16(cert->subject().locality_name),
189 UTF8ToUTF16(cert->subject().country_name));
190 }
191 DCHECK(!cert->subject().organization_names.empty());
192 site_identity_details_.assign(l10n_util::GetStringFUTF16(
193 IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY_EV,
194 UTF8ToUTF16(cert->subject().organization_names[0]),
195 locality,
196 UTF8ToUTF16(cert->issuer().GetDisplayName())));
197 } else if (ssl.cert_status & net::CERT_STATUS_IS_DNSSEC) {
198 // DNSSEC authenticated page.
199 site_identity_status_ = SITE_IDENTITY_STATUS_DNSSEC_CERT;
200 site_identity_details_.assign(l10n_util::GetStringFUTF16(
201 IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY, UTF8ToUTF16("DNSSEC")));
202 } else {
203 // Non-EV OK HTTPS page.
204 site_identity_status_ = SITE_IDENTITY_STATUS_CERT;
205 string16 issuer_name(UTF8ToUTF16(cert->issuer().GetDisplayName()));
206 if (issuer_name.empty()) {
207 issuer_name.assign(l10n_util::GetStringUTF16(
208 IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY));
209 }
210 site_identity_details_.assign(l10n_util::GetStringFUTF16(
211 IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY, issuer_name));
212 }
213 } else {
214 // HTTP or HTTPS with errors (not warnings).
215 site_identity_details_.assign(l10n_util::GetStringUTF16(
216 IDS_PAGE_INFO_SECURITY_TAB_INSECURE_IDENTITY));
217 if (ssl.security_style == content::SECURITY_STYLE_UNAUTHENTICATED)
218 site_identity_status_ = SITE_IDENTITY_STATUS_NO_CERT;
219 else
220 site_identity_status_ = SITE_IDENTITY_STATUS_ERROR;
221
222 const string16 bullet = UTF8ToUTF16("\n • ");
223 std::vector<SSLErrorInfo> errors;
224 SSLErrorInfo::GetErrorsForCertStatus(ssl.cert_id, ssl.cert_status,
225 url, &errors);
226 for (size_t i = 0; i < errors.size(); ++i) {
227 site_identity_details_ += bullet;
228 site_identity_details_ += errors[i].short_description();
229 }
230
231 if (ssl.cert_status & net::CERT_STATUS_NON_UNIQUE_NAME) {
232 site_identity_details_ += ASCIIToUTF16("\n\n");
233 site_identity_details_ += l10n_util::GetStringUTF16(
234 IDS_PAGE_INFO_SECURITY_TAB_NON_UNIQUE_NAME);
235 }
236 }
237
238 // Site Connection
239 // We consider anything less than 80 bits encryption to be weak encryption.
240 // TODO(wtc): Bug 1198735: report mixed/unsafe content for unencrypted and
241 // weakly encrypted connections.
242 site_connection_status_ = SITE_CONNECTION_STATUS_UNKNOWN;
243
244 if (!ssl.cert_id) {
245 // Not HTTPS.
246 DCHECK_EQ(ssl.security_style, content::SECURITY_STYLE_UNAUTHENTICATED);
247 if (ssl.security_style == content::SECURITY_STYLE_UNAUTHENTICATED)
248 site_connection_status_ = SITE_CONNECTION_STATUS_UNENCRYPTED;
249 else
250 site_connection_status_ = SITE_CONNECTION_STATUS_ENCRYPTED_ERROR;
251
252 site_connection_details_.assign(l10n_util::GetStringFUTF16(
253 IDS_PAGE_INFO_SECURITY_TAB_NOT_ENCRYPTED_CONNECTION_TEXT,
254 subject_name));
255 } else if (ssl.security_bits < 0) {
256 // Security strength is unknown. Say nothing.
257 site_connection_status_ = SITE_CONNECTION_STATUS_ENCRYPTED_ERROR;
258 } else if (ssl.security_bits == 0) {
259 DCHECK_NE(ssl.security_style, content::SECURITY_STYLE_UNAUTHENTICATED);
260 site_connection_status_ = SITE_CONNECTION_STATUS_ENCRYPTED_ERROR;
261 site_connection_details_.assign(l10n_util::GetStringFUTF16(
262 IDS_PAGE_INFO_SECURITY_TAB_NOT_ENCRYPTED_CONNECTION_TEXT,
263 subject_name));
264 } else if (ssl.security_bits < 80) {
265 site_connection_status_ = SITE_CONNECTION_STATUS_ENCRYPTED_ERROR;
266 site_connection_details_.assign(l10n_util::GetStringFUTF16(
267 IDS_PAGE_INFO_SECURITY_TAB_WEAK_ENCRYPTION_CONNECTION_TEXT,
268 subject_name));
269 } else {
270 site_connection_status_ = SITE_CONNECTION_STATUS_ENCRYPTED;
271 site_connection_details_.assign(l10n_util::GetStringFUTF16(
272 IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_CONNECTION_TEXT,
273 subject_name,
274 base::IntToString16(ssl.security_bits)));
275 if (ssl.content_status) {
276 bool ran_insecure_content =
277 !!(ssl.content_status & content::SSLStatus::RAN_INSECURE_CONTENT);
278 site_connection_status_ = ran_insecure_content ?
279 SITE_CONNECTION_STATUS_ENCRYPTED_ERROR
280 : SITE_CONNECTION_STATUS_MIXED_CONTENT;
281 site_connection_details_.assign(l10n_util::GetStringFUTF16(
282 IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_SENTENCE_LINK,
283 site_connection_details_,
284 l10n_util::GetStringUTF16(ran_insecure_content ?
285 IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_INSECURE_CONTENT_ERROR :
286 IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_INSECURE_CONTENT_WARNING)));
287 }
288 }
289
290 uint16 cipher_suite =
291 net::SSLConnectionStatusToCipherSuite(ssl.connection_status);
292 if (ssl.security_bits > 0 && cipher_suite) {
293 int ssl_version =
294 net::SSLConnectionStatusToVersion(ssl.connection_status);
295 const char* ssl_version_str;
296 net::SSLVersionToString(&ssl_version_str, ssl_version);
297 site_connection_details_ += ASCIIToUTF16("\n\n");
298 site_connection_details_ += l10n_util::GetStringFUTF16(
299 IDS_PAGE_INFO_SECURITY_TAB_SSL_VERSION,
300 ASCIIToUTF16(ssl_version_str));
301
302 bool did_fallback = (ssl.connection_status &
303 net::SSL_CONNECTION_SSL3_FALLBACK) != 0;
304 bool no_renegotiation =
305 (ssl.connection_status &
306 net::SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION) != 0;
307 const char *key_exchange, *cipher, *mac;
308 net::SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, cipher_suite);
309
310 site_connection_details_ += ASCIIToUTF16("\n\n");
311 site_connection_details_ += l10n_util::GetStringFUTF16(
312 IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTION_DETAILS,
313 ASCIIToUTF16(cipher), ASCIIToUTF16(mac), ASCIIToUTF16(key_exchange));
314
315 site_connection_details_ += ASCIIToUTF16("\n\n");
316 uint8 compression_id =
317 net::SSLConnectionStatusToCompression(ssl.connection_status);
318 if (compression_id) {
319 const char* compression;
320 net::SSLCompressionToString(&compression, compression_id);
321 site_connection_details_ += l10n_util::GetStringFUTF16(
322 IDS_PAGE_INFO_SECURITY_TAB_COMPRESSION_DETAILS,
323 ASCIIToUTF16(compression));
324 } else {
325 site_connection_details_ += l10n_util::GetStringUTF16(
326 IDS_PAGE_INFO_SECURITY_TAB_NO_COMPRESSION);
327 }
328
329 if (did_fallback) {
330 // For now, only SSLv3 fallback will trigger a warning icon.
331 if (site_connection_status_ < SITE_CONNECTION_STATUS_MIXED_CONTENT)
332 site_connection_status_ = SITE_CONNECTION_STATUS_MIXED_CONTENT;
333 site_connection_details_ += ASCIIToUTF16("\n\n");
334 site_connection_details_ += l10n_util::GetStringUTF16(
335 IDS_PAGE_INFO_SECURITY_TAB_FALLBACK_MESSAGE);
336 }
337 if (no_renegotiation) {
338 site_connection_details_ += ASCIIToUTF16("\n\n");
339 site_connection_details_ += l10n_util::GetStringUTF16(
340 IDS_PAGE_INFO_SECURITY_TAB_RENEGOTIATION_MESSAGE);
341 }
342 }
343}
[email protected]0b9fdd72012-04-04 10:00:33344
345void WebsiteSettings::OnUIClosing() {
346 ui_->SetPresenter(NULL);
347 delete this;
348}
349
350void WebsiteSettings::OnSitePermissionChanged(ContentSettingsType type,
351 ContentSetting setting) {
352 ContentSettingsPattern primary_pattern;
353 ContentSettingsPattern secondary_pattern;
354 switch (type) {
355 case CONTENT_SETTINGS_TYPE_GEOLOCATION:
356 // TODO(markusheintz): The rule we create here should also change the
357 // location permission for iframed content.
358 primary_pattern = ContentSettingsPattern::FromURLNoWildcard(site_url_);
359 secondary_pattern = ContentSettingsPattern::FromURLNoWildcard(site_url_);
360 break;
361 case CONTENT_SETTINGS_TYPE_NOTIFICATIONS:
362 primary_pattern = ContentSettingsPattern::FromURLNoWildcard(site_url_);
363 secondary_pattern = ContentSettingsPattern::Wildcard();
364 break;
365 case CONTENT_SETTINGS_TYPE_PLUGINS:
366 case CONTENT_SETTINGS_TYPE_POPUPS:
367 primary_pattern = ContentSettingsPattern::FromURL(site_url_);
368 secondary_pattern = ContentSettingsPattern::Wildcard();
369 break;
370 default:
371 NOTREACHED() << "ContentSettingsType " << type << "is not supported.";
372 break;
373 }
374
375 // Permission settings are specified via rules. There exists always at least
376 // one rule for the default setting. Get the rule that currently defines
377 // the permission for the given permission |type|. Then test whether the
378 // existing rule is more specific than the rule we are about to create. If
379 // the existing rule is more specific, than change the existing rule instead
380 // of creating a new rule that would be hidden behind the existing rule.
381 content_settings::SettingInfo info;
382 scoped_ptr<Value> v(content_settings_->GetWebsiteSetting(
383 site_url_, site_url_, type, "", &info));
384 DCHECK(info.source == content_settings::SETTING_SOURCE_USER);
385 ContentSettingsPattern::Relation r1 =
386 info.primary_pattern.Compare(primary_pattern);
387 DCHECK(r1 != ContentSettingsPattern::DISJOINT_ORDER_POST &&
388 r1 != ContentSettingsPattern::DISJOINT_ORDER_PRE);
389 if (r1 == ContentSettingsPattern::PREDECESSOR) {
390 primary_pattern = info.primary_pattern;
391 } else if (r1 == ContentSettingsPattern::IDENTITY) {
392 ContentSettingsPattern::Relation r2 =
393 info.secondary_pattern.Compare(secondary_pattern);
394 DCHECK(r2 != ContentSettingsPattern::DISJOINT_ORDER_POST &&
395 r2 != ContentSettingsPattern::DISJOINT_ORDER_PRE);
396 if (r2 == ContentSettingsPattern::PREDECESSOR)
397 secondary_pattern = info.secondary_pattern;
398 }
399
400 Value* value = NULL;
401 if (setting != CONTENT_SETTING_DEFAULT)
402 value = Value::CreateIntegerValue(setting);
403 content_settings_->SetWebsiteSetting(
404 primary_pattern, secondary_pattern, type, "", value);
405}