Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ssl / chrome_ssl_host_state_delegate.cc
1 // Copyright 2014 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
5 #include "chrome/browser/ssl/chrome_ssl_host_state_delegate.h"
6
7 #include "base/base64.h"
8 #include "base/bind.h"
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "base/metrics/field_trial.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/time/clock.h"
14 #include "base/time/default_clock.h"
15 #include "base/time/time.h"
16 #include "chrome/browser/content_settings/host_content_settings_map.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/common/chrome_switches.h"
19 #include "components/content_settings/core/common/content_settings_types.h"
20 #include "components/variations/variations_associated_data.h"
21 #include "net/base/hash_value.h"
22 #include "net/cert/x509_certificate.h"
23 #include "net/http/http_transaction_factory.h"
24 #include "net/url_request/url_request_context.h"
25 #include "net/url_request/url_request_context_getter.h"
26 #include "url/gurl.h"
27
28 namespace {
29
30 // Switch value that specifies that certificate decisions should be forgotten at
31 // the end of the current session.
32 const int64 kForgetAtSessionEndSwitchValue = -1;
33
34 // Experiment information
35 const char kRememberCertificateErrorDecisionsFieldTrialName[] =
36     "RememberCertificateErrorDecisions";
37 const char kRememberCertificateErrorDecisionsFieldTrialDefaultGroup[] =
38     "Default";
39 const char kRememberCertificateErrorDecisionsFieldTrialLengthParam[] = "length";
40
41 // Keys for the per-site error + certificate finger to judgement content
42 // settings map.
43 const char kSSLCertDecisionCertErrorMapKey[] = "cert_exceptions_map";
44 const char kSSLCertDecisionExpirationTimeKey[] = "decision_expiration_time";
45 const char kSSLCertDecisionVersionKey[] = "version";
46
47 const int kDefaultSSLCertDecisionVersion = 1;
48
49 // Closes all idle network connections for the given URLRequestContext. This is
50 // a big hammer and should be wielded with extreme caution as it can have a big,
51 // negative impact on network performance. In this case, it is used by
52 // RevokeUserDecisionsHard, which should only be called by rare, user initiated
53 // events. See the comment before RevokeUserDecisionsHard implementation for
54 // more information.
55 void CloseIdleConnections(
56     scoped_refptr<net::URLRequestContextGetter> url_request_context_getter) {
57   url_request_context_getter->
58       GetURLRequestContext()->
59       http_transaction_factory()->
60       GetSession()->
61       CloseIdleConnections();
62 }
63
64 // All SSL decisions are per host (and are shared arcoss schemes), so this
65 // canonicalizes all hosts into a secure scheme GURL to use with content
66 // settings. The returned GURL will be the passed in host with an empty path and
67 // https:// as the scheme.
68 GURL GetSecureGURLForHost(const std::string& host) {
69   std::string url = "https://" + host;
70   return GURL(url);
71 }
72
73 // This is a helper function that returns the length of time before a
74 // certificate decision expires based on the command line flags. Returns a
75 // non-negative value in seconds or a value of -1  indicating that decisions
76 // should not be remembered after the current session has ended (but should be
77 // remembered indefinitely as long as the session does not end), which is the
78 // "old" style of certificate decision memory. Uses the experimental group
79 // unless overridden by a command line flag.
80 int64 GetExpirationDelta() {
81   // Check command line flags first to give them priority, then check
82   // experimental groups.
83   if (CommandLine::ForCurrentProcess()->HasSwitch(
84           switches::kRememberCertErrorDecisions)) {
85     std::string switch_value =
86         CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
87             switches::kRememberCertErrorDecisions);
88     int64 expiration_delta;
89     if (!base::StringToInt64(base::StringPiece(switch_value),
90                              &expiration_delta) ||
91         expiration_delta < kForgetAtSessionEndSwitchValue) {
92       LOG(ERROR) << "Failed to parse the certificate error decision "
93                  << "memory length: " << switch_value;
94       return kForgetAtSessionEndSwitchValue;
95     }
96
97     return expiration_delta;
98   }
99
100   // If the user is in the field trial, set the expiration to the length
101   // associated with that experimental group.  The default group cannot have
102   // parameters associated with it, so it needs to be handled explictly.
103   std::string group_name = base::FieldTrialList::FindFullName(
104       kRememberCertificateErrorDecisionsFieldTrialName);
105   if (!group_name.empty() &&
106       group_name.compare(
107           kRememberCertificateErrorDecisionsFieldTrialDefaultGroup) != 0) {
108     int64 field_trial_param_length;
109     std::string param = variations::GetVariationParamValue(
110         kRememberCertificateErrorDecisionsFieldTrialName,
111         kRememberCertificateErrorDecisionsFieldTrialLengthParam);
112     if (!param.empty() && base::StringToInt64(base::StringPiece(param),
113                                               &field_trial_param_length)) {
114       return field_trial_param_length;
115     }
116   }
117
118   return kForgetAtSessionEndSwitchValue;
119 }
120
121 std::string GetKey(net::X509Certificate* cert, net::CertStatus error) {
122   // Since a security decision will be made based on the fingerprint, Chrome
123   // should use the SHA-256 fingerprint for the certificate.
124   net::SHA256HashValue fingerprint =
125       net::X509Certificate::CalculateChainFingerprint256(
126           cert->os_cert_handle(), cert->GetIntermediateCertificates());
127   std::string base64_fingerprint;
128   base::Base64Encode(
129       base::StringPiece(reinterpret_cast<const char*>(fingerprint.data),
130                         sizeof(fingerprint.data)),
131       &base64_fingerprint);
132   return base::UintToString(error) + base64_fingerprint;
133 }
134
135 }  // namespace
136
137 // This helper function gets the dictionary of certificate fingerprints to
138 // errors of certificates that have been accepted by the user from the content
139 // dictionary that has been passed in. The returned pointer is owned by the the
140 // argument dict that is passed in.
141 //
142 // If create_entries is set to |DoNotCreateDictionaryEntries|,
143 // GetValidCertDecisionsDict will return NULL if there is anything invalid about
144 // the setting, such as an invalid version or invalid value types (in addition
145 // to there not be any values in the dictionary). If create_entries is set to
146 // |CreateDictionaryEntries|, if no dictionary is found or the decisions are
147 // expired, a new dictionary will be created
148 base::DictionaryValue* ChromeSSLHostStateDelegate::GetValidCertDecisionsDict(
149     base::DictionaryValue* dict,
150     CreateDictionaryEntriesDisposition create_entries,
151     bool* expired_previous_decision) {
152   // This needs to be done first in case the method is short circuited by an
153   // early failure.
154   *expired_previous_decision = false;
155
156   // Extract the version of the certificate decision structure from the content
157   // setting.
158   int version;
159   bool success = dict->GetInteger(kSSLCertDecisionVersionKey, &version);
160   if (!success) {
161     if (create_entries == DoNotCreateDictionaryEntries)
162       return NULL;
163
164     dict->SetInteger(kSSLCertDecisionVersionKey,
165                      kDefaultSSLCertDecisionVersion);
166     version = kDefaultSSLCertDecisionVersion;
167   }
168
169   // If the version is somehow a newer version than Chrome can handle, there's
170   // really nothing to do other than fail silently and pretend it doesn't exist
171   // (or is malformed).
172   if (version > kDefaultSSLCertDecisionVersion) {
173     LOG(ERROR) << "Failed to parse a certificate error exception that is in a "
174                << "newer version format (" << version << ") than is supported ("
175                << kDefaultSSLCertDecisionVersion << ")";
176     return NULL;
177   }
178
179   // Extract the certificate decision's expiration time from the content
180   // setting. If there is no expiration time, that means it should never expire
181   // and it should reset only at session restart, so skip all of the expiration
182   // checks.
183   bool expired = false;
184   base::Time now = clock_->Now();
185   base::Time decision_expiration;
186   if (dict->HasKey(kSSLCertDecisionExpirationTimeKey)) {
187     std::string decision_expiration_string;
188     int64 decision_expiration_int64;
189     success = dict->GetString(kSSLCertDecisionExpirationTimeKey,
190                               &decision_expiration_string);
191     if (!base::StringToInt64(base::StringPiece(decision_expiration_string),
192                              &decision_expiration_int64)) {
193       LOG(ERROR) << "Failed to parse a certificate error exception that has a "
194                  << "bad value for an expiration time: "
195                  << decision_expiration_string;
196       return NULL;
197     }
198     decision_expiration =
199         base::Time::FromInternalValue(decision_expiration_int64);
200   }
201
202   // Check to see if the user's certificate decision has expired.
203   // - Expired and |create_entries| is DoNotCreateDictionaryEntries, return
204   // NULL.
205   // - Expired and |create_entries| is CreateDictionaryEntries, update the
206   // expiration time.
207   if (should_remember_ssl_decisions_ !=
208           ForgetSSLExceptionDecisionsAtSessionEnd &&
209       decision_expiration.ToInternalValue() <= now.ToInternalValue()) {
210     *expired_previous_decision = true;
211
212     if (create_entries == DoNotCreateDictionaryEntries)
213       return NULL;
214
215     expired = true;
216     base::Time expiration_time =
217         now + default_ssl_cert_decision_expiration_delta_;
218     // Unfortunately, JSON (and thus content settings) doesn't support int64
219     // values, only doubles. Since this mildly depends on precision, it is
220     // better to store the value as a string.
221     dict->SetString(kSSLCertDecisionExpirationTimeKey,
222                     base::Int64ToString(expiration_time.ToInternalValue()));
223   }
224
225   // Extract the map of certificate fingerprints to errors from the setting.
226   base::DictionaryValue* cert_error_dict = NULL;  // Will be owned by dict
227   if (expired ||
228       !dict->GetDictionary(kSSLCertDecisionCertErrorMapKey, &cert_error_dict)) {
229     if (create_entries == DoNotCreateDictionaryEntries)
230       return NULL;
231
232     cert_error_dict = new base::DictionaryValue();
233     // dict takes ownership of cert_error_dict
234     dict->Set(kSSLCertDecisionCertErrorMapKey, cert_error_dict);
235   }
236
237   return cert_error_dict;
238 }
239
240 // If |should_remember_ssl_decisions_| is
241 // ForgetSSLExceptionDecisionsAtSessionEnd, that means that all invalid
242 // certificate proceed decisions should be forgotten when the session ends. At
243 // attempt is made in the destructor to remove the entries, but in the case that
244 // things didn't shut down cleanly, on start, Clear is called to guarantee a
245 // clean state.
246 ChromeSSLHostStateDelegate::ChromeSSLHostStateDelegate(Profile* profile)
247     : clock_(new base::DefaultClock()), profile_(profile) {
248   int64 expiration_delta = GetExpirationDelta();
249   if (expiration_delta == kForgetAtSessionEndSwitchValue) {
250     should_remember_ssl_decisions_ = ForgetSSLExceptionDecisionsAtSessionEnd;
251     expiration_delta = 0;
252     Clear();
253   } else {
254     should_remember_ssl_decisions_ = RememberSSLExceptionDecisionsForDelta;
255   }
256   default_ssl_cert_decision_expiration_delta_ =
257       base::TimeDelta::FromSeconds(expiration_delta);
258 }
259
260 ChromeSSLHostStateDelegate::~ChromeSSLHostStateDelegate() {
261   if (should_remember_ssl_decisions_ == ForgetSSLExceptionDecisionsAtSessionEnd)
262     Clear();
263 }
264
265 void ChromeSSLHostStateDelegate::DenyCert(const std::string& host,
266                                           net::X509Certificate* cert,
267                                           net::CertStatus error) {
268   ChangeCertPolicy(host, cert, error, net::CertPolicy::DENIED);
269 }
270
271 void ChromeSSLHostStateDelegate::AllowCert(const std::string& host,
272                                            net::X509Certificate* cert,
273                                            net::CertStatus error) {
274   ChangeCertPolicy(host, cert, error, net::CertPolicy::ALLOWED);
275 }
276
277 void ChromeSSLHostStateDelegate::Clear() {
278   profile_->GetHostContentSettingsMap()->ClearSettingsForOneType(
279       CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS);
280 }
281
282 net::CertPolicy::Judgment ChromeSSLHostStateDelegate::QueryPolicy(
283     const std::string& host,
284     net::X509Certificate* cert,
285     net::CertStatus error,
286     bool* expired_previous_decision) {
287   HostContentSettingsMap* map = profile_->GetHostContentSettingsMap();
288   GURL url = GetSecureGURLForHost(host);
289   scoped_ptr<base::Value> value(map->GetWebsiteSetting(
290       url, url, CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS, std::string(), NULL));
291
292   // Set a default value in case this method is short circuited and doesn't do a
293   // full query.
294   *expired_previous_decision = false;
295   if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY))
296     return net::CertPolicy::UNKNOWN;
297
298   base::DictionaryValue* dict;  // Owned by value
299   int policy_decision;
300   bool success = value->GetAsDictionary(&dict);
301   DCHECK(success);
302
303   base::DictionaryValue* cert_error_dict;  // Owned by value
304   cert_error_dict = GetValidCertDecisionsDict(
305       dict, DoNotCreateDictionaryEntries, expired_previous_decision);
306   if (!cert_error_dict) {
307     // This revoke is necessary to clear any old expired setting that may
308     // lingering in the case that an old decision expried.
309     RevokeUserDecisions(host);
310     return net::CertPolicy::UNKNOWN;
311   }
312
313   success = cert_error_dict->GetIntegerWithoutPathExpansion(GetKey(cert, error),
314                                                             &policy_decision);
315
316   // If a policy decision was successfully retrieved and it's a valid value of
317   // ALLOWED or DENIED, return the valid value. Otherwise, return UNKNOWN.
318   if (success && policy_decision == net::CertPolicy::Judgment::ALLOWED)
319     return net::CertPolicy::Judgment::ALLOWED;
320   else if (success && policy_decision == net::CertPolicy::Judgment::DENIED)
321     return net::CertPolicy::Judgment::DENIED;
322
323   return net::CertPolicy::Judgment::UNKNOWN;
324 }
325
326 void ChromeSSLHostStateDelegate::RevokeUserDecisions(const std::string& host) {
327   GURL url = GetSecureGURLForHost(host);
328   const ContentSettingsPattern pattern =
329       ContentSettingsPattern::FromURLNoWildcard(url);
330   HostContentSettingsMap* map = profile_->GetHostContentSettingsMap();
331
332   map->SetWebsiteSetting(pattern,
333                          pattern,
334                          CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS,
335                          std::string(),
336                          NULL);
337 }
338
339 // TODO(jww): This will revoke all of the decisions in the browser context.
340 // However, the networking stack actually keeps track of its own list of
341 // exceptions per-HttpNetworkTransaction in the SSLConfig structure (see the
342 // allowed_bad_certs Vector in net/ssl/ssl_config.h). This dual-tracking of
343 // exceptions introduces a problem where the browser context can revoke a
344 // certificate, but if a transaction reuses a cached version of the SSLConfig
345 // (probably from a pooled socket), it may bypass the intestitial layer.
346 //
347 // Over time, the cached versions should expire and it should converge on
348 // showing the interstitial. We probably need to introduce into the networking
349 // stack a way revoke SSLConfig's allowed_bad_certs lists per socket.
350 //
351 // For now, RevokeUserDecisionsHard is our solution for the rare case where it
352 // is necessary to revoke the preferences immediately. It does so by flushing
353 // idle sockets.
354 void ChromeSSLHostStateDelegate::RevokeUserDecisionsHard(
355     const std::string& host) {
356   RevokeUserDecisions(host);
357   scoped_refptr<net::URLRequestContextGetter> getter(
358       profile_->GetRequestContext());
359   profile_->GetRequestContext()->GetNetworkTaskRunner()->PostTask(
360       FROM_HERE, base::Bind(&CloseIdleConnections, getter));
361 }
362
363 bool ChromeSSLHostStateDelegate::HasUserDecision(const std::string& host) {
364   GURL url = GetSecureGURLForHost(host);
365   const ContentSettingsPattern pattern =
366       ContentSettingsPattern::FromURLNoWildcard(url);
367   HostContentSettingsMap* map = profile_->GetHostContentSettingsMap();
368
369   scoped_ptr<base::Value> value(map->GetWebsiteSetting(
370       url, url, CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS, std::string(), NULL));
371
372   if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY))
373     return false;
374
375   base::DictionaryValue* dict;  // Owned by value
376   bool success = value->GetAsDictionary(&dict);
377   DCHECK(success);
378
379   for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
380     int policy_decision;  // Owned by dict
381     success = it.value().GetAsInteger(&policy_decision);
382     if (success && (static_cast<net::CertPolicy::Judgment>(policy_decision) !=
383                     net::CertPolicy::UNKNOWN))
384       return true;
385   }
386
387   return false;
388 }
389
390 void ChromeSSLHostStateDelegate::HostRanInsecureContent(const std::string& host,
391                                                         int pid) {
392   ran_insecure_content_hosts_.insert(BrokenHostEntry(host, pid));
393 }
394
395 bool ChromeSSLHostStateDelegate::DidHostRunInsecureContent(
396     const std::string& host,
397     int pid) const {
398   return !!ran_insecure_content_hosts_.count(BrokenHostEntry(host, pid));
399 }
400 void ChromeSSLHostStateDelegate::SetClock(scoped_ptr<base::Clock> clock) {
401   clock_.reset(clock.release());
402 }
403
404 void ChromeSSLHostStateDelegate::ChangeCertPolicy(
405     const std::string& host,
406     net::X509Certificate* cert,
407     net::CertStatus error,
408     net::CertPolicy::Judgment judgment) {
409   GURL url = GetSecureGURLForHost(host);
410   const ContentSettingsPattern pattern =
411       ContentSettingsPattern::FromURLNoWildcard(url);
412   HostContentSettingsMap* map = profile_->GetHostContentSettingsMap();
413   scoped_ptr<base::Value> value(map->GetWebsiteSetting(
414       url, url, CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS, std::string(), NULL));
415
416   if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY))
417     value.reset(new base::DictionaryValue());
418
419   base::DictionaryValue* dict;
420   bool success = value->GetAsDictionary(&dict);
421   DCHECK(success);
422
423   bool expired_previous_decision;  // unused value in this function
424   base::DictionaryValue* cert_dict = GetValidCertDecisionsDict(
425       dict, CreateDictionaryEntries, &expired_previous_decision);
426   // If a a valid certificate dictionary cannot be extracted from the content
427   // setting, that means it's in an unknown format. Unfortunately, there's
428   // nothing to be done in that case, so a silent fail is the only option.
429   if (!cert_dict)
430     return;
431
432   dict->SetIntegerWithoutPathExpansion(kSSLCertDecisionVersionKey,
433                                        kDefaultSSLCertDecisionVersion);
434   cert_dict->SetIntegerWithoutPathExpansion(GetKey(cert, error), judgment);
435
436   // The map takes ownership of the value, so it is released in the call to
437   // SetWebsiteSetting.
438   map->SetWebsiteSetting(pattern,
439                          pattern,
440                          CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS,
441                          std::string(),
442                          value.release());
443 }