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

Side by Side Diff: chrome/browser/ssl/chrome_security_state_model_client.cc

Issue 2448943002: Refactor SecurityStateModel/Clients for simplicity and reusability. (Closed)
Patch Set: Refactor -> WebContentsSecurityStateModel. Created 4 years, 1 month 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
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 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 #include "chrome/browser/ssl/chrome_security_state_model_client.h" 5 #include "chrome/browser/ssl/chrome_security_state_model_client.h"
6 6
7 #include <openssl/ssl.h>
8
9 #include <vector>
10
11 #include "base/command_line.h"
12 #include "base/metrics/field_trial.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "build/build_config.h" 7 #include "build/build_config.h"
17 #include "chrome/browser/browser_process.h" 8 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chromeos/policy/policy_cert_service.h" 9 #include "chrome/browser/chromeos/policy/policy_cert_service.h"
19 #include "chrome/browser/chromeos/policy/policy_cert_service_factory.h" 10 #include "chrome/browser/chromeos/policy/policy_cert_service_factory.h"
20 #include "chrome/browser/profiles/profile.h" 11 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 12 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
22 #include "chrome/browser/safe_browsing/ui_manager.h" 13 #include "chrome/browser/safe_browsing/ui_manager.h"
23 #include "chrome/grit/chromium_strings.h"
24 #include "chrome/grit/generated_resources.h"
25 #include "content/public/browser/navigation_entry.h" 14 #include "content/public/browser/navigation_entry.h"
26 #include "content/public/browser/navigation_handle.h" 15 #include "content/public/browser/navigation_handle.h"
27 #include "content/public/browser/render_frame_host.h" 16 #include "content/public/browser/render_frame_host.h"
28 #include "content/public/browser/security_style_explanation.h"
29 #include "content/public/browser/security_style_explanations.h"
30 #include "content/public/browser/ssl_status.h"
31 #include "content/public/browser/web_contents.h" 17 #include "content/public/browser/web_contents.h"
32 #include "content/public/common/origin_util.h"
33 #include "net/base/net_errors.h"
34 #include "net/cert/x509_certificate.h"
35 #include "net/ssl/ssl_cipher_suite_names.h"
36 #include "net/ssl/ssl_connection_status_flags.h"
37 #include "ui/base/l10n/l10n_util.h"
38 18
39 DEFINE_WEB_CONTENTS_USER_DATA_KEY(ChromeSecurityStateModelClient); 19 DEFINE_WEB_CONTENTS_USER_DATA_KEY(ChromeSecurityStateModelClient);
40 20
41 using safe_browsing::SafeBrowsingUIManager; 21 using safe_browsing::SafeBrowsingUIManager;
42 using security_state::SecurityStateModel; 22 using security_state::SecurityStateModel;
43 23
44 namespace {
45
46 // Note: This is a lossy operation. Not all of the policies that can be
47 // expressed by a SecurityLevel (a //chrome concept) can be expressed by
48 // a blink::WebSecurityStyle.
49 blink::WebSecurityStyle SecurityLevelToSecurityStyle(
50 SecurityStateModel::SecurityLevel security_level) {
51 switch (security_level) {
52 case SecurityStateModel::NONE:
53 case SecurityStateModel::HTTP_SHOW_WARNING:
54 return blink::WebSecurityStyleUnauthenticated;
55 case SecurityStateModel::SECURITY_WARNING:
56 case SecurityStateModel::SECURE_WITH_POLICY_INSTALLED_CERT:
57 return blink::WebSecurityStyleWarning;
58 case SecurityStateModel::EV_SECURE:
59 case SecurityStateModel::SECURE:
60 return blink::WebSecurityStyleAuthenticated;
61 case SecurityStateModel::DANGEROUS:
62 return blink::WebSecurityStyleAuthenticationBroken;
63 }
64
65 NOTREACHED();
66 return blink::WebSecurityStyleUnknown;
67 }
68
69 void AddConnectionExplanation(
70 const security_state::SecurityStateModel::SecurityInfo& security_info,
71 content::SecurityStyleExplanations* security_style_explanations) {
72
73 // Avoid showing TLS details when we couldn't even establish a TLS connection
74 // (e.g. for net errors) or if there was no real connection (some tests). We
75 // check the |connection_status| to see if there was a connection.
76 if (security_info.connection_status == 0) {
77 return;
78 }
79
80 int ssl_version =
81 net::SSLConnectionStatusToVersion(security_info.connection_status);
82 const char* protocol;
83 net::SSLVersionToString(&protocol, ssl_version);
84 const char* key_exchange;
85 const char* cipher;
86 const char* mac;
87 bool is_aead;
88 bool is_tls13;
89 uint16_t cipher_suite =
90 net::SSLConnectionStatusToCipherSuite(security_info.connection_status);
91 net::SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, &is_aead,
92 &is_tls13, cipher_suite);
93 base::string16 protocol_name = base::ASCIIToUTF16(protocol);
94 const base::string16 cipher_name =
95 (mac == NULL) ? base::ASCIIToUTF16(cipher)
96 : l10n_util::GetStringFUTF16(IDS_CIPHER_WITH_MAC,
97 base::ASCIIToUTF16(cipher),
98 base::ASCIIToUTF16(mac));
99
100 // Include the key exchange group (previously known as curve) if specified.
101 base::string16 key_exchange_name;
102 if (is_tls13) {
103 key_exchange_name = base::ASCIIToUTF16(
104 SSL_get_curve_name(security_info.key_exchange_group));
105 } else if (security_info.key_exchange_group != 0) {
106 key_exchange_name = l10n_util::GetStringFUTF16(
107 IDS_SSL_KEY_EXCHANGE_WITH_GROUP, base::ASCIIToUTF16(key_exchange),
108 base::ASCIIToUTF16(
109 SSL_get_curve_name(security_info.key_exchange_group)));
110 } else {
111 key_exchange_name = base::ASCIIToUTF16(key_exchange);
112 }
113
114 if (security_info.obsolete_ssl_status == net::OBSOLETE_SSL_NONE) {
115 security_style_explanations->secure_explanations.push_back(
116 content::SecurityStyleExplanation(
117 l10n_util::GetStringUTF8(IDS_STRONG_SSL_SUMMARY),
118 l10n_util::GetStringFUTF8(IDS_STRONG_SSL_DESCRIPTION, protocol_name,
119 key_exchange_name, cipher_name)));
120 return;
121 }
122
123 std::vector<base::string16> description_replacements;
124 int status = security_info.obsolete_ssl_status;
125 int str_id;
126
127 str_id = (status & net::OBSOLETE_SSL_MASK_PROTOCOL)
128 ? IDS_SSL_AN_OBSOLETE_PROTOCOL
129 : IDS_SSL_A_STRONG_PROTOCOL;
130 description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
131 description_replacements.push_back(protocol_name);
132
133 str_id = (status & net::OBSOLETE_SSL_MASK_KEY_EXCHANGE)
134 ? IDS_SSL_AN_OBSOLETE_KEY_EXCHANGE
135 : IDS_SSL_A_STRONG_KEY_EXCHANGE;
136 description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
137 description_replacements.push_back(key_exchange_name);
138
139 str_id = (status & net::OBSOLETE_SSL_MASK_CIPHER) ? IDS_SSL_AN_OBSOLETE_CIPHER
140 : IDS_SSL_A_STRONG_CIPHER;
141 description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
142 description_replacements.push_back(cipher_name);
143
144 security_style_explanations->info_explanations.push_back(
145 content::SecurityStyleExplanation(
146 l10n_util::GetStringUTF8(IDS_OBSOLETE_SSL_SUMMARY),
147 base::UTF16ToUTF8(
148 l10n_util::GetStringFUTF16(IDS_OBSOLETE_SSL_DESCRIPTION,
149 description_replacements, nullptr))));
150 }
151
152 // Check to see whether the security state should be downgraded to reflect
153 // a Safe Browsing verdict.
154 void CheckSafeBrowsingStatus(content::NavigationEntry* entry,
155 content::WebContents* web_contents,
156 SecurityStateModel::VisibleSecurityState* state) {
157 safe_browsing::SafeBrowsingService* sb_service =
158 g_browser_process->safe_browsing_service();
159 if (!sb_service)
160 return;
161 scoped_refptr<SafeBrowsingUIManager> sb_ui_manager = sb_service->ui_manager();
162 if (sb_ui_manager->IsUrlWhitelistedOrPendingForWebContents(
163 entry->GetURL(), false, entry, web_contents, false)) {
164 state->fails_malware_check = true;
165 }
166 }
167
168 } // namespace
169
170 ChromeSecurityStateModelClient::ChromeSecurityStateModelClient( 24 ChromeSecurityStateModelClient::ChromeSecurityStateModelClient(
171 content::WebContents* web_contents) 25 content::WebContents* web_contents)
172 : content::WebContentsObserver(web_contents), 26 : content::WebContentsObserver(web_contents),
173 web_contents_(web_contents), 27 security_state_model_(
174 security_state_model_(new SecurityStateModel()), 28 new security_state::WebContentsSecurityStateModel(web_contents)),
175 logged_http_warning_on_current_navigation_(false) { 29 logged_http_warning_on_current_navigation_(false) {
176 security_state_model_->SetClient(this); 30 security_state_model_->SetClient(this);
177 } 31 }
178 32
179 ChromeSecurityStateModelClient::~ChromeSecurityStateModelClient() {} 33 ChromeSecurityStateModelClient::~ChromeSecurityStateModelClient() {}
180 34
181 // static
182 blink::WebSecurityStyle ChromeSecurityStateModelClient::GetSecurityStyle(
183 const security_state::SecurityStateModel::SecurityInfo& security_info,
184 content::SecurityStyleExplanations* security_style_explanations) {
185 const blink::WebSecurityStyle security_style =
186 SecurityLevelToSecurityStyle(security_info.security_level);
187
188 if (security_info.security_level ==
189 security_state::SecurityStateModel::HTTP_SHOW_WARNING) {
190 // If the HTTP_SHOW_WARNING field trial is in use, display an
191 // unauthenticated explanation explaining why the omnibox warning is
192 // present.
193 security_style_explanations->unauthenticated_explanations.push_back(
194 content::SecurityStyleExplanation(
195 l10n_util::GetStringUTF8(IDS_PRIVATE_USER_DATA_INPUT),
196 l10n_util::GetStringUTF8(IDS_PRIVATE_USER_DATA_INPUT_DESCRIPTION)));
197 } else if (security_info.security_level ==
198 security_state::SecurityStateModel::NONE &&
199 security_info.displayed_private_user_data_input_on_http) {
200 // If the HTTP_SHOW_WARNING field trial isn't in use yet, display an
201 // informational note that the omnibox will contain a warning for
202 // this site in a future version of Chrome.
203 security_style_explanations->info_explanations.push_back(
204 content::SecurityStyleExplanation(
205 l10n_util::GetStringUTF8(IDS_PRIVATE_USER_DATA_INPUT),
206 l10n_util::GetStringUTF8(
207 IDS_PRIVATE_USER_DATA_INPUT_FUTURE_DESCRIPTION)));
208 }
209
210 security_style_explanations->ran_insecure_content_style =
211 SecurityLevelToSecurityStyle(
212 SecurityStateModel::kRanInsecureContentLevel);
213 security_style_explanations->displayed_insecure_content_style =
214 SecurityLevelToSecurityStyle(
215 SecurityStateModel::kDisplayedInsecureContentLevel);
216
217 // Check if the page is HTTP; if so, no more explanations are needed. Note
218 // that SecurityStyleUnauthenticated does not necessarily mean that
219 // the page is loaded over HTTP, because the security style merely
220 // represents how the embedder wishes to display the security state of
221 // the page, and the embedder can choose to display HTTPS page as HTTP
222 // if it wants to (for example, displaying deprecated crypto
223 // algorithms with the same UI treatment as HTTP pages).
224 security_style_explanations->scheme_is_cryptographic =
225 security_info.scheme_is_cryptographic;
226 if (!security_info.scheme_is_cryptographic) {
227 return security_style;
228 }
229
230 if (security_info.sha1_deprecation_status ==
231 SecurityStateModel::DEPRECATED_SHA1_MAJOR) {
232 security_style_explanations->broken_explanations.push_back(
233 content::SecurityStyleExplanation(
234 l10n_util::GetStringUTF8(IDS_MAJOR_SHA1),
235 l10n_util::GetStringUTF8(IDS_MAJOR_SHA1_DESCRIPTION),
236 !!security_info.certificate));
237 } else if (security_info.sha1_deprecation_status ==
238 SecurityStateModel::DEPRECATED_SHA1_MINOR) {
239 security_style_explanations->unauthenticated_explanations.push_back(
240 content::SecurityStyleExplanation(
241 l10n_util::GetStringUTF8(IDS_MINOR_SHA1),
242 l10n_util::GetStringUTF8(IDS_MINOR_SHA1_DESCRIPTION),
243 !!security_info.certificate));
244 }
245
246 // Record the presence of mixed content (HTTP subresources on an HTTPS
247 // page).
248 security_style_explanations->ran_mixed_content =
249 security_info.mixed_content_status ==
250 SecurityStateModel::CONTENT_STATUS_RAN ||
251 security_info.mixed_content_status ==
252 SecurityStateModel::CONTENT_STATUS_DISPLAYED_AND_RAN;
253 security_style_explanations->displayed_mixed_content =
254 security_info.mixed_content_status ==
255 SecurityStateModel::CONTENT_STATUS_DISPLAYED ||
256 security_info.mixed_content_status ==
257 SecurityStateModel::CONTENT_STATUS_DISPLAYED_AND_RAN;
258
259 bool is_cert_status_error = net::IsCertStatusError(security_info.cert_status);
260 bool is_cert_status_minor_error =
261 net::IsCertStatusMinorError(security_info.cert_status);
262
263 // If the main resource was loaded no certificate errors or only minor
264 // certificate errors, then record the presence of subresources with
265 // certificate errors. Subresource certificate errors aren't recorded
266 // when the main resource was loaded with major certificate errors
267 // because, in the common case, these subresource certificate errors
268 // would be duplicative with the main resource's error.
269 if (!is_cert_status_error || is_cert_status_minor_error) {
270 security_style_explanations->ran_content_with_cert_errors =
271 security_info.content_with_cert_errors_status ==
272 SecurityStateModel::CONTENT_STATUS_RAN ||
273 security_info.content_with_cert_errors_status ==
274 SecurityStateModel::CONTENT_STATUS_DISPLAYED_AND_RAN;
275 security_style_explanations->displayed_content_with_cert_errors =
276 security_info.content_with_cert_errors_status ==
277 SecurityStateModel::CONTENT_STATUS_DISPLAYED ||
278 security_info.content_with_cert_errors_status ==
279 SecurityStateModel::CONTENT_STATUS_DISPLAYED_AND_RAN;
280 }
281
282 if (is_cert_status_error) {
283 base::string16 error_string = base::UTF8ToUTF16(net::ErrorToString(
284 net::MapCertStatusToNetError(security_info.cert_status)));
285
286 content::SecurityStyleExplanation explanation(
287 l10n_util::GetStringUTF8(IDS_CERTIFICATE_CHAIN_ERROR),
288 l10n_util::GetStringFUTF8(
289 IDS_CERTIFICATE_CHAIN_ERROR_DESCRIPTION_FORMAT, error_string),
290 !!security_info.certificate);
291
292 if (is_cert_status_minor_error) {
293 security_style_explanations->unauthenticated_explanations.push_back(
294 explanation);
295 } else {
296 security_style_explanations->broken_explanations.push_back(explanation);
297 }
298 } else {
299 // If the certificate does not have errors and is not using
300 // deprecated SHA1, then add an explanation that the certificate is
301 // valid.
302 if (security_info.sha1_deprecation_status ==
303 SecurityStateModel::NO_DEPRECATED_SHA1) {
304 security_style_explanations->secure_explanations.push_back(
305 content::SecurityStyleExplanation(
306 l10n_util::GetStringUTF8(IDS_VALID_SERVER_CERTIFICATE),
307 l10n_util::GetStringUTF8(
308 IDS_VALID_SERVER_CERTIFICATE_DESCRIPTION),
309 !!security_info.certificate));
310 }
311 }
312
313 AddConnectionExplanation(security_info, security_style_explanations);
314
315 security_style_explanations->pkp_bypassed = security_info.pkp_bypassed;
316 if (security_info.pkp_bypassed) {
317 security_style_explanations->info_explanations.push_back(
318 content::SecurityStyleExplanation(
319 "Public-Key Pinning Bypassed",
320 "Public-key pinning was bypassed by a local root certificate."));
321 }
322
323 return security_style;
324 }
325
326 void ChromeSecurityStateModelClient::GetSecurityInfo( 35 void ChromeSecurityStateModelClient::GetSecurityInfo(
327 SecurityStateModel::SecurityInfo* result) const { 36 SecurityStateModel::SecurityInfo* result) const {
328 security_state_model_->GetSecurityInfo(result); 37 security_state_model_->GetSecurityInfo(result);
329 } 38 }
330 39
331 void ChromeSecurityStateModelClient::VisibleSSLStateChanged() { 40 void ChromeSecurityStateModelClient::VisibleSSLStateChanged() {
332 if (logged_http_warning_on_current_navigation_) 41 if (logged_http_warning_on_current_navigation_)
333 return; 42 return;
334 43
335 security_state::SecurityStateModel::SecurityInfo security_info; 44 security_state::SecurityStateModel::SecurityInfo security_info;
(...skipping 13 matching lines...) Expand all
349 warning = 58 warning =
350 "This page includes a password or credit card input in a non-secure " 59 "This page includes a password or credit card input in a non-secure "
351 "context. A warning will be added to the URL bar in Chrome 56 (Jan " 60 "context. A warning will be added to the URL bar in Chrome 56 (Jan "
352 "2017). For more information, see https://goo.gl/zmWq3m."; 61 "2017). For more information, see https://goo.gl/zmWq3m.";
353 break; 62 break;
354 default: 63 default:
355 return; 64 return;
356 } 65 }
357 66
358 logged_http_warning_on_current_navigation_ = true; 67 logged_http_warning_on_current_navigation_ = true;
359 web_contents_->GetMainFrame()->AddMessageToConsole( 68 web_contents()->GetMainFrame()->AddMessageToConsole(
360 content::CONSOLE_MESSAGE_LEVEL_WARNING, warning); 69 content::CONSOLE_MESSAGE_LEVEL_WARNING, warning);
361 } 70 }
362 71
363 void ChromeSecurityStateModelClient::DidFinishNavigation( 72 void ChromeSecurityStateModelClient::DidFinishNavigation(
364 content::NavigationHandle* navigation_handle) { 73 content::NavigationHandle* navigation_handle) {
365 if (navigation_handle->IsInMainFrame() && 74 if (navigation_handle->IsInMainFrame() &&
366 !navigation_handle->IsSynchronousNavigation()) { 75 !navigation_handle->IsSynchronousNavigation()) {
367 // Only reset the console message flag for main-frame navigations, 76 // Only reset the console message flag for main-frame navigations,
368 // and not for synchronous navigations like reference fragments and 77 // and not for synchronous navigations like reference fragments and
369 // pushState. 78 // pushState.
370 logged_http_warning_on_current_navigation_ = false; 79 logged_http_warning_on_current_navigation_ = false;
371 } 80 }
372 } 81 }
373 82
374 bool ChromeSecurityStateModelClient::UsedPolicyInstalledCertificate() { 83 bool ChromeSecurityStateModelClient::UsedPolicyInstalledCertificate() {
375 #if defined(OS_CHROMEOS) 84 #if defined(OS_CHROMEOS)
376 policy::PolicyCertService* service = 85 policy::PolicyCertService* service =
377 policy::PolicyCertServiceFactory::GetForProfile( 86 policy::PolicyCertServiceFactory::GetForProfile(
378 Profile::FromBrowserContext(web_contents_->GetBrowserContext())); 87 Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
379 if (service && service->UsedPolicyCertificates()) 88 if (service && service->UsedPolicyCertificates())
380 return true; 89 return true;
381 #endif 90 #endif
382 return false; 91 return false;
383 } 92 }
384 93
385 bool ChromeSecurityStateModelClient::IsOriginSecure(const GURL& url) { 94 // Check to see whether the security state should be downgraded to reflect
386 return content::IsOriginSecure(url); 95 // a Safe Browsing verdict.
96 bool ChromeSecurityStateModelClient::GetMalwareStatus(
97 content::NavigationEntry* entry,
98 content::WebContents* web_contents) {
99 safe_browsing::SafeBrowsingService* sb_service =
100 g_browser_process->safe_browsing_service();
101 if (!sb_service)
102 return false;
103 scoped_refptr<SafeBrowsingUIManager> sb_ui_manager = sb_service->ui_manager();
104 if (sb_ui_manager->IsUrlWhitelistedOrPendingForWebContents(
105 entry->GetURL(), false, entry, web_contents, false)) {
106 return true;
107 }
108 return false;
387 } 109 }
388
389 void ChromeSecurityStateModelClient::GetVisibleSecurityState(
390 SecurityStateModel::VisibleSecurityState* state) {
391 content::NavigationEntry* entry =
392 web_contents_->GetController().GetVisibleEntry();
393 if (!entry) {
394 *state = SecurityStateModel::VisibleSecurityState();
395 return;
396 }
397
398 if (!entry->GetSSL().initialized) {
399 *state = SecurityStateModel::VisibleSecurityState();
400 // Connection security information is still being initialized, but malware
401 // status might already be known.
402 CheckSafeBrowsingStatus(entry, web_contents_, state);
403 return;
404 }
405
406 state->connection_info_initialized = true;
407 state->url = entry->GetURL();
408 const content::SSLStatus& ssl = entry->GetSSL();
409 state->certificate = ssl.certificate;
410 state->cert_status = ssl.cert_status;
411 state->connection_status = ssl.connection_status;
412 state->key_exchange_group = ssl.key_exchange_group;
413 state->security_bits = ssl.security_bits;
414 state->pkp_bypassed = ssl.pkp_bypassed;
415 state->sct_verify_statuses.clear();
416 state->sct_verify_statuses.insert(state->sct_verify_statuses.begin(),
417 ssl.sct_statuses.begin(),
418 ssl.sct_statuses.end());
419 state->displayed_mixed_content =
420 !!(ssl.content_status & content::SSLStatus::DISPLAYED_INSECURE_CONTENT);
421 state->ran_mixed_content =
422 !!(ssl.content_status & content::SSLStatus::RAN_INSECURE_CONTENT);
423 state->displayed_content_with_cert_errors =
424 !!(ssl.content_status &
425 content::SSLStatus::DISPLAYED_CONTENT_WITH_CERT_ERRORS);
426 state->ran_content_with_cert_errors =
427 !!(ssl.content_status & content::SSLStatus::RAN_CONTENT_WITH_CERT_ERRORS);
428 state->displayed_password_field_on_http =
429 !!(ssl.content_status &
430 content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP);
431 state->displayed_credit_card_field_on_http =
432 !!(ssl.content_status &
433 content::SSLStatus::DISPLAYED_CREDIT_CARD_FIELD_ON_HTTP);
434
435 CheckSafeBrowsingStatus(entry, web_contents_, state);
436 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698