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

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

Issue 369703002: Remember user decisions on invalid certificates behind a flag (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: More changes from felt Created 6 years, 5 months 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 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_decisions.h"
6
7 #include "base/base64.h"
8 #include "base/command_line.h"
9 #include "base/logging.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/time/default_clock.h"
12 #include "base/time/time.h"
13 #include "chrome/browser/content_settings/host_content_settings_map.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/common/chrome_switches.h"
16 #include "chrome/common/content_settings_types.h"
17 #include "url/gurl.h"
18
19 namespace {
20
21 const int64 kForgetAtSessionEndSwitchValue = -1;
22 const int64 kExpirationZeroInSeconds = 0;
23 const int64 kExpirationOneDayInSeconds = 86400;
24 const int64 kExpirationThreeDaysInSeconds = 259200;
25 const int64 kExpirationOneWeekInSeconds = 604800;
26 const int64 kExpirationOneMonthInSeconds = 2592000;
27
28 // This is a helper function that returns the length of time before a
29 // certificate decision expires based on the command line flags. Returns a
30 // non-negative value in seconds or a value of -1 indicating that decisions
31 // should not be remembered after the current session has ended (but should be
32 // remembered indefinitely as long as the session does not end), which is the
33 // "old" style of certificate decision memory.
34 int64 GetExpirationDelta() {
35 if (CommandLine::ForCurrentProcess()->HasSwitch(
36 switches::kRememberCertErrorDecisionsDisable))
37 return kForgetAtSessionEndSwitchValue;
38 else if (CommandLine::ForCurrentProcess()->HasSwitch(
39 switches::kRememberCertErrorDecisionsNone))
40 return kExpirationZeroInSeconds;
41 else if (CommandLine::ForCurrentProcess()->HasSwitch(
42 switches::kRememberCertErrorDecisionsOneDay))
43 return kExpirationOneDayInSeconds;
44 else if (CommandLine::ForCurrentProcess()->HasSwitch(
45 switches::kRememberCertErrorDecisionsThreeDays))
46 return kExpirationThreeDaysInSeconds;
47 else if (CommandLine::ForCurrentProcess()->HasSwitch(
48 switches::kRememberCertErrorDecisionsOneWeek))
49 return kExpirationOneWeekInSeconds;
50 else if (CommandLine::ForCurrentProcess()->HasSwitch(
51 switches::kRememberCertErrorDecisionsOneMonth))
52 return kExpirationOneMonthInSeconds;
53
54 return kForgetAtSessionEndSwitchValue;
55 }
56
57 const char kSSLCertDecisionCertErrorMapKey[] = "cert_exceptions_map";
58 const char kSSLCertDecisionExpirationTimeKey[] = "decision_expiration_time";
59 const char kSSLCertDecisionVersionKey[] = "version";
60 const int kDefaultSSLCertDecisionVersion = 1;
61
62 std::string GetKey(net::X509Certificate* cert, net::CertStatus error) {
63 net::SHA1HashValue fingerprint = cert->fingerprint();
64 std::string base64_fingerprint;
65 base::Base64Encode(
66 base::StringPiece(reinterpret_cast<const char*>(fingerprint.data),
67 sizeof(fingerprint.data)),
68 &base64_fingerprint);
69 return base::UintToString(error) + base64_fingerprint;
70 }
71
72 } // namespace
73
74 // This helper function gets the dictionary of certificate fingerprints to
75 // errors of certificates that have been accepted by the user from the content
76 // dictionary that has been passed in. The returned pointer is owned by the the
77 // argument dict that is passed in.
78 //
79 // If create_entries is set to |DoNotCreateDictionaryEntries|,
80 // GetValidCertDecisionsDict will return NULL if there is anything invalid about
81 // the setting, such as an invalid version or invalid value types (in addition
82 // to there not be any values in the dictionary). If create_entries is set to
83 // |CreateDictionaryEntries|, if no dictionary is found or the decisions are
84 // expired, a new dictionary will be created
85 base::DictionaryValue* ChromeSSLHostStateDecisions::GetValidCertDecisionsDict(
86 base::DictionaryValue* dict,
87 CreateDictionaryEntriesDisposition create_entries) {
88 // Extract the version of the certificate decision structure from the content
89 // setting.
90 int version;
91 bool success = dict->GetInteger(kSSLCertDecisionVersionKey, &version);
92 if (!success) {
93 if (create_entries == DoNotCreateDictionaryEntries)
94 return NULL;
95
96 dict->SetInteger(kSSLCertDecisionVersionKey,
97 kDefaultSSLCertDecisionVersion);
98 version = kDefaultSSLCertDecisionVersion;
99 }
100
101 // If the version is somehow a newer version than Chrome can handle, there's
102 // really nothing to do other than fail silently and pretend it doesn't exist
103 // (or is malformed).
104 if (version > kDefaultSSLCertDecisionVersion) {
105 LOG(ERROR) << "Failed to parse a certificate error exception that is in a "
106 << "newer version format (" << version << ") than is supported ("
107 << kDefaultSSLCertDecisionVersion << ")";
108 return NULL;
109 }
110
111 // Extract the certificate decision's expiration time from the content
112 // setting. If there is no expiration time, that means it should never expire
113 // and it should resest only at session restart, so skip all of the expiration
114 // checks.
115 bool expired = false;
116 double decision_expiration_dbl;
117 base::Time now = clock_->Now();
118 base::Time decision_expiration;
119 if (dict->HasKey(kSSLCertDecisionExpirationTimeKey)) {
120 success = dict->GetDouble(kSSLCertDecisionExpirationTimeKey,
121 &decision_expiration_dbl);
122 decision_expiration = base::Time::FromDoubleT(decision_expiration_dbl);
123 }
124
125 // Check if the user certificate decision has expired. If it has expired, and
126 // create_entries is |DoNotCreateDictionaryEntries|, treat the entry as if it
127 // doesn't exist and return |NULL|. If it has expired, and create_entries is
128 // |DoNotCreateDictionaryEntries|, set the entry's expiration date to the
129 // current time plus the expiration time delta. Also, set expired to |true|
130 // which will force the creation of a new dictionary in the dictionary
131 // extraction pass. None of this should be done if expiration happens on
132 // session end (i.e. shouldRememberSSLDecisions_ is set to
133 // ForgetSSLExceptionDecisionsAtSessionEnd).
134 if (shouldRememberSSLDecisions_ != ForgetSSLExceptionDecisionsAtSessionEnd &&
135 decision_expiration <= now) {
136 if (create_entries == DoNotCreateDictionaryEntries)
137 return NULL;
138
139 expired = true;
140
141 base::Time expiration_time = now + defaultSSLCertDecisionExpirationDelta_;
142 dict->SetDouble(kSSLCertDecisionExpirationTimeKey,
143 expiration_time.ToDoubleT());
144 }
145
146 // Extract the map of certificate fingerprints to errors from the setting.
147 base::DictionaryValue* cert_error_dict; // Owned by dict
148 if (expired ||
149 !dict->GetDictionary(kSSLCertDecisionCertErrorMapKey, &cert_error_dict)) {
150 if (create_entries == DoNotCreateDictionaryEntries)
151 return NULL;
152
153 cert_error_dict = new base::DictionaryValue();
154 // dict takes ownership of cert_error_dict
155 dict->Set(kSSLCertDecisionCertErrorMapKey, cert_error_dict);
156 }
157
158 return cert_error_dict;
159 }
160
161 // If shouldRememberSSLDecisions_ is ForgetSSLExceptionDecisionsAtSessionEnd,
162 // that means that all invalid certificate proceed decisions should be forgotten
163 // when the session ends. At attempt is made in the destructor to remove the
164 // entries, but in the case that things didn't shut down cleanly, on start,
165 // Clear is called to guarantee a clean state.
166 ChromeSSLHostStateDecisions::ChromeSSLHostStateDecisions(Profile* profile)
167 : clock_(new base::DefaultClock()), profile_(profile) {
168 int64 expiration_delta = GetExpirationDelta();
169 if (expiration_delta == kForgetAtSessionEndSwitchValue) {
170 shouldRememberSSLDecisions_ = ForgetSSLExceptionDecisionsAtSessionEnd;
171 expiration_delta = 0;
172 Clear();
173 } else {
174 shouldRememberSSLDecisions_ = RememberSSLExceptionDecisionsForDelta;
175 }
176 defaultSSLCertDecisionExpirationDelta_ =
177 base::TimeDelta::FromSeconds(expiration_delta);
178 }
179
180 ChromeSSLHostStateDecisions::~ChromeSSLHostStateDecisions() {
181 if (shouldRememberSSLDecisions_ == ForgetSSLExceptionDecisionsAtSessionEnd)
182 Clear();
183 }
184
185 void ChromeSSLHostStateDecisions::DenyCert(const GURL& url,
186 net::X509Certificate* cert,
187 net::CertStatus error) {
188 ChangeCertPolicy(url, cert, error, net::CertPolicy::DENIED);
189 }
190
191 void ChromeSSLHostStateDecisions::AllowCert(const GURL& url,
192 net::X509Certificate* cert,
193 net::CertStatus error) {
194 ChangeCertPolicy(url, cert, error, net::CertPolicy::ALLOWED);
195 }
196
197 void ChromeSSLHostStateDecisions::Clear() {
198 profile_->GetHostContentSettingsMap()->ClearSettingsForOneType(
199 CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS);
200 }
201
202 net::CertPolicy::Judgment ChromeSSLHostStateDecisions::QueryPolicy(
203 const GURL& url,
204 net::X509Certificate* cert,
205 net::CertStatus error) {
206 HostContentSettingsMap* map = profile_->GetHostContentSettingsMap();
207 scoped_ptr<base::Value> value(map->GetWebsiteSetting(
208 url, url, CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS, "", NULL));
209
210 DCHECK(value.get());
211
212 if (!value->IsType(base::Value::TYPE_DICTIONARY))
213 return net::CertPolicy::UNKNOWN;
214
215 base::DictionaryValue* dict; // Owned by value
216 int policy_decision; // Owned by dict
217 bool success = value->GetAsDictionary(&dict);
218 DCHECK(success);
219
220 // First, check that the version of the dictionary of decisions is a version
221 // that is known. Otherwise, fail.
222 base::DictionaryValue* cert_error_dict; // Owned by value
223 cert_error_dict =
224 GetValidCertDecisionsDict(dict, DoNotCreateDictionaryEntries);
225 if (!cert_error_dict)
226 return net::CertPolicy::UNKNOWN;
227
228 success = cert_error_dict->GetIntegerWithoutPathExpansion(GetKey(cert, error),
229 &policy_decision);
230
231 if (!success)
232 return net::CertPolicy::Judgment::UNKNOWN;
233
234 return static_cast<net::CertPolicy::Judgment>(policy_decision);
235 }
236
237 void ChromeSSLHostStateDecisions::RevokeAllowAndDenyPreferences(
238 const GURL& url) {
239 const ContentSettingsPattern pattern =
240 ContentSettingsPattern::FromURLNoWildcard(url);
241 HostContentSettingsMap* map = profile_->GetHostContentSettingsMap();
242
243 map->SetWebsiteSetting(
244 pattern, pattern, CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS, "", NULL);
245 }
246
247 bool ChromeSSLHostStateDecisions::HasAllowedOrDeniedCert(const GURL& url) {
248 const ContentSettingsPattern pattern =
249 ContentSettingsPattern::FromURLNoWildcard(url);
250 HostContentSettingsMap* map = profile_->GetHostContentSettingsMap();
251
252 scoped_ptr<base::Value> value(map->GetWebsiteSetting(
253 url, url, CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS, "", NULL));
254
255 if (!value->IsType(base::Value::TYPE_DICTIONARY))
256 return false;
257
258 base::DictionaryValue* dict; // Owned by value
259 bool success = value->GetAsDictionary(&dict);
260 DCHECK(success);
261
262 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
263 int policy_decision; // Owned by dict
264 success = it.value().GetAsInteger(&policy_decision);
265 if (success && (static_cast<net::CertPolicy::Judgment>(policy_decision) !=
266 net::CertPolicy::UNKNOWN))
267 return true;
268 }
269
270 return false;
271 }
272
273 void ChromeSSLHostStateDecisions::ChangeCertPolicy(
274 const GURL& url,
275 net::X509Certificate* cert,
276 net::CertStatus error,
277 net::CertPolicy::Judgment judgment) {
278 const ContentSettingsPattern pattern =
279 ContentSettingsPattern::FromURLNoWildcard(url);
280 HostContentSettingsMap* map = profile_->GetHostContentSettingsMap();
281 scoped_ptr<base::Value> value(map->GetWebsiteSetting(
282 url, url, CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS, "", NULL));
283
284 if (!value->IsType(base::Value::TYPE_DICTIONARY))
285 value.reset(new base::DictionaryValue());
286
287 base::DictionaryValue* dict;
288 bool success = value->GetAsDictionary(&dict);
289 DCHECK(success);
290
291 base::DictionaryValue* cert_dict =
292 GetValidCertDecisionsDict(dict, CreateDictionaryEntries);
293 // If a a valid certificate dictionary cannot be extracted from the content
294 // setting, that means it's in an unknown format. Unfortunately, there's
295 // nothing to be done in that case, so a silent fail is the only option.
296 if (!cert_dict)
297 return;
298
299 dict->SetIntegerWithoutPathExpansion(kSSLCertDecisionVersionKey,
300 kDefaultSSLCertDecisionVersion);
301 cert_dict->SetIntegerWithoutPathExpansion(GetKey(cert, error), judgment);
302
303 // The map takes ownership of the value, so it is released in the call to
304 // SetWebsiteSetting.
305 map->SetWebsiteSetting(pattern,
306 pattern,
307 CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS,
308 "",
309 value.release());
310 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698