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

Side by Side Diff: chrome/browser/transport_security_persister.cc

Issue 9415040: Refactor TransportSecurityState. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 9 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
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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/transport_security_persister.h" 5 #include "chrome/browser/transport_security_persister.h"
6 6
7 #include "base/base64.h"
7 #include "base/bind.h" 8 #include "base/bind.h"
8 #include "base/file_path.h" 9 #include "base/file_path.h"
9 #include "base/file_util.h" 10 #include "base/file_util.h"
11 #include "base/json/json_reader.h"
12 #include "base/json/json_writer.h"
10 #include "base/message_loop.h" 13 #include "base/message_loop.h"
11 #include "base/path_service.h" 14 #include "base/path_service.h"
15 #include "base/values.h"
12 #include "chrome/common/chrome_paths.h" 16 #include "chrome/common/chrome_paths.h"
13 #include "content/public/browser/browser_thread.h" 17 #include "content/public/browser/browser_thread.h"
18 #include "crypto/sha2.h"
14 #include "net/base/transport_security_state.h" 19 #include "net/base/transport_security_state.h"
20 #include "net/base/x509_certificate.h"
15 21
16 using content::BrowserThread; 22 using content::BrowserThread;
23 using net::Fingerprint;
24 using net::FingerprintVector;
25 using net::TransportSecurityState;
17 26
18 class TransportSecurityPersister::Loader { 27 class TransportSecurityPersister::Loader {
19 public: 28 public:
20 Loader(const base::WeakPtr<TransportSecurityPersister>& persister, 29 Loader(const base::WeakPtr<TransportSecurityPersister>& persister,
21 const FilePath& path) 30 const FilePath& path)
22 : persister_(persister), 31 : persister_(persister),
23 path_(path), 32 path_(path),
24 state_valid_(false) { 33 state_valid_(false) {
25 } 34 }
26 35
(...skipping 18 matching lines...) Expand all
45 54
46 FilePath path_; 55 FilePath path_;
47 56
48 std::string state_; 57 std::string state_;
49 bool state_valid_; 58 bool state_valid_;
50 59
51 DISALLOW_COPY_AND_ASSIGN(Loader); 60 DISALLOW_COPY_AND_ASSIGN(Loader);
52 }; 61 };
53 62
54 TransportSecurityPersister::TransportSecurityPersister( 63 TransportSecurityPersister::TransportSecurityPersister(
55 net::TransportSecurityState* state, 64 TransportSecurityState* state,
56 const FilePath& profile_path, 65 const FilePath& profile_path,
57 bool readonly) 66 bool readonly)
58 : transport_security_state_(state), 67 : transport_security_state_(state),
59 writer_(profile_path.AppendASCII("TransportSecurity"), 68 writer_(profile_path.AppendASCII("TransportSecurity"),
60 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)), 69 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)),
61 readonly_(readonly), 70 readonly_(readonly),
62 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { 71 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
64 73
65 transport_security_state_->SetDelegate(this); 74 transport_security_state_->SetDelegate(this);
66 75
67 Loader* loader = new Loader(weak_ptr_factory_.GetWeakPtr(), writer_.path()); 76 Loader* loader = new Loader(weak_ptr_factory_.GetWeakPtr(), writer_.path());
68 BrowserThread::PostTaskAndReply( 77 BrowserThread::PostTaskAndReply(
69 BrowserThread::FILE, FROM_HERE, 78 BrowserThread::FILE, FROM_HERE,
70 base::Bind(&Loader::Load, base::Unretained(loader)), 79 base::Bind(&Loader::Load, base::Unretained(loader)),
71 base::Bind(&Loader::CompleteLoad, base::Unretained(loader))); 80 base::Bind(&Loader::CompleteLoad, base::Unretained(loader)));
72 } 81 }
73 82
74 TransportSecurityPersister::~TransportSecurityPersister() { 83 TransportSecurityPersister::~TransportSecurityPersister() {
75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 84 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
76 85
77 if (writer_.HasPendingWrite()) 86 if (writer_.HasPendingWrite())
78 writer_.DoScheduledWrite(); 87 writer_.DoScheduledWrite();
79 88
80 transport_security_state_->SetDelegate(NULL); 89 transport_security_state_->SetDelegate(NULL);
81 } 90 }
82 91
92 static ListValue* SPKIHashesToListValue(const FingerprintVector& hashes) {
Ryan Sleevi 2012/03/28 00:50:32 nit: The dominant pattern in Chromium code is to u
palmer 2012/04/10 23:25:51 Done.
93 ListValue* pins = new ListValue;
94
95 for (FingerprintVector::const_iterator i = hashes.begin();
96 i != hashes.end(); ++i) {
97 std::string hash_str(reinterpret_cast<const char*>(i->data),
98 sizeof(i->data));
99 std::string b64;
100 base::Base64Encode(hash_str, &b64);
101 pins->Append(new StringValue("sha1/" + b64));
102 }
103
104 return pins;
105 }
106
107 static void SPKIHashesFromListValue(FingerprintVector* hashes,
108 const ListValue& pins) {
Ryan Sleevi 2012/03/28 00:50:32 nit: Parameter order: in -> out http://google-sty
palmer 2012/04/10 23:25:51 Done.
109 size_t num_pins = pins.GetSize();
110 for (size_t i = 0; i < num_pins; ++i) {
111 std::string type_and_base64;
112 Fingerprint fingerprint;
113 if (pins.GetString(i, &type_and_base64) &&
114 TransportSecurityState::ParsePin(type_and_base64, &fingerprint)) {
115 hashes->push_back(fingerprint);
116 }
117 }
118 }
119
120 // This function converts the binary hashes, which we store in
121 // |enabled_hosts_|, to a base64 string which we can include in a JSON file.
122 static std::string HashedDomainToExternalString(const std::string& hashed) {
123 std::string out;
124 CHECK(base::Base64Encode(hashed, &out));
Ryan Sleevi 2012/03/28 00:50:32 Do you really want to crash the browser process he
palmer 2012/04/10 23:25:51 Done.
125 return out;
126 }
127
128 // This inverts |HashedDomainToExternalString|, above. It turns an external
129 // string (from a JSON file) into an internal (binary) string.
130 static std::string ExternalStringToHashedDomain(const std::string& external) {
131 std::string out;
132 if (!base::Base64Decode(external, &out) ||
133 out.size() != crypto::kSHA256Length) {
134 return std::string();
135 }
136
137 return out;
138 }
139
140 // static
141 bool TransportSecurityPersister::Deserialize(const std::string& serialized,
142 bool* dirty,
143 TransportSecurityState* state) {
144 scoped_ptr<Value> value(
145 base::JSONReader::Read(serialized,
146 false /* do not allow trailing commas */));
147 if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY))
Ryan Sleevi 2012/03/28 00:50:32 nit: You can replace 147 - 150 with: DictionaryVa
palmer 2012/04/10 23:25:51 Done.
148 return false;
149
150 DictionaryValue* dict_value = reinterpret_cast<DictionaryValue*>(value.get());
151 const base::Time current_time(base::Time::Now());
152 bool dirtied = false;
153
154 for (DictionaryValue::key_iterator i = dict_value->begin_keys();
155 i != dict_value->end_keys(); ++i) {
156 DictionaryValue* parsed;
157 if (!dict_value->GetDictionaryWithoutPathExpansion(*i, &parsed))
158 continue;
159
160 bool include_subdomains;
161 std::string mode_string;
162 double created;
163 double expiry;
164 double dynamic_spki_hashes_expiry = 0.0;
165
166 if (!parsed->GetBoolean("include_subdomains", &include_subdomains) ||
167 !parsed->GetString("mode", &mode_string) ||
168 !parsed->GetDouble("expiry", &expiry)) {
169 continue;
170 }
171
172 // Don't fail if this key is not present.
173 (void) parsed->GetDouble("dynamic_spki_hashes_expiry",
Ryan Sleevi 2012/03/28 00:50:32 This method isn't WARN_UNUSED_RESULT, so is this (
palmer 2012/04/10 23:25:51 No, it's not needed. Removed.
174 &dynamic_spki_hashes_expiry);
175
176 ListValue* pins_list = NULL;
177 FingerprintVector static_spki_hashes;
178 // preloaded_spki_hashes is a legacy synonym for static_spki_hashes.
179 if (parsed->GetList("static_spki_hashes", &pins_list))
180 SPKIHashesFromListValue(&static_spki_hashes, *pins_list);
181 else if (parsed->GetList("preloaded_spki_hashes", &pins_list))
182 SPKIHashesFromListValue(&static_spki_hashes, *pins_list);
183
184 FingerprintVector dynamic_spki_hashes;
185 if (parsed->GetList("dynamic_spki_hashes", &pins_list))
Ryan Sleevi 2012/03/28 00:50:32 nit: Since you re-use these strings in both serial
palmer 2012/04/10 23:25:51 Done.
186 SPKIHashesFromListValue(&dynamic_spki_hashes, *pins_list);
187
188 TransportSecurityState::DomainState::UpgradeMode mode;
189 if (mode_string == "force-https" || mode_string == "strict") {
190 mode = TransportSecurityState::DomainState::MODE_FORCE_HTTPS;
191 } else if (mode_string == "default" || mode_string == "pinning-only") {
192 mode = TransportSecurityState::DomainState::MODE_DEFAULT;
193 } else {
194 LOG(WARNING) << "Unknown TransportSecurityState mode string found: "
195 << mode_string;
Ryan Sleevi 2012/03/28 00:50:32 What happened to spdy-only? Looks like you're dro
palmer 2012/04/10 23:25:51 It had been deprecated for a long time, and a sear
196 continue;
197 }
198
199 base::Time expiry_time = base::Time::FromDoubleT(expiry);
200 base::Time dynamic_spki_hashes_expiry_time =
201 base::Time::FromDoubleT(dynamic_spki_hashes_expiry);
202 base::Time created_time;
203 if (parsed->GetDouble("created", &created)) {
204 created_time = base::Time::FromDoubleT(created);
205 } else {
206 // We're migrating an old entry with no creation date. Make sure we
207 // write the new date back in a reasonable time frame.
208 dirtied = true;
209 created_time = base::Time::Now();
210 }
211
212 if (expiry_time <= current_time &&
213 dynamic_spki_hashes_expiry_time <= current_time) {
214 // Make sure we dirty the state if we drop an entry.
215 dirtied = true;
216 continue;
217 }
218
219 std::string hashed = ExternalStringToHashedDomain(*i);
220 if (hashed.empty()) {
221 dirtied = true;
222 continue;
223 }
224
225 TransportSecurityState::DomainState domain_state;
226 domain_state.upgrade_mode = mode;
227 domain_state.created = created_time;
228 domain_state.upgrade_expiry = expiry_time;
229 domain_state.include_subdomains = include_subdomains;
230 domain_state.static_spki_hashes = static_spki_hashes;
231 domain_state.dynamic_spki_hashes = dynamic_spki_hashes;
232 domain_state.dynamic_spki_hashes_expiry = dynamic_spki_hashes_expiry_time;
233 state->EnableHost(hashed, domain_state);
234 }
235
236 *dirty = dirtied;
237 return true;
238 }
239
240 bool TransportSecurityPersister::LoadEntries(const std::string& serialized,
241 bool* dirty,
242 TransportSecurityState* state) {
243 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
244
245 state->Clear();
246 return Deserialize(serialized, dirty, state);
247 }
248
83 void TransportSecurityPersister::CompleteLoad(const std::string& state) { 249 void TransportSecurityPersister::CompleteLoad(const std::string& state) {
84 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
85 251
86 bool dirty = false; 252 bool dirty = false;
87 if (!transport_security_state_->LoadEntries(state, &dirty)) { 253 if (!LoadEntries(state, &dirty, transport_security_state_)) {
88 LOG(ERROR) << "Failed to deserialize state: " << state; 254 LOG(ERROR) << "Failed to deserialize state: " << state;
89 return; 255 return;
90 } 256 }
91 if (dirty) 257 if (dirty)
92 StateIsDirty(transport_security_state_); 258 StateIsDirty(transport_security_state_);
93 } 259 }
94 260
95 void TransportSecurityPersister::StateIsDirty( 261 void TransportSecurityPersister::StateIsDirty(
96 net::TransportSecurityState* state) { 262 TransportSecurityState* state) {
97 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
98 DCHECK_EQ(transport_security_state_, state); 264 DCHECK_EQ(transport_security_state_, state);
99 265
100 if (!readonly_) 266 if (!readonly_)
101 writer_.ScheduleWrite(this); 267 writer_.ScheduleWrite(this);
102 } 268 }
103 269
270 bool TransportSecurityPersister::Serialize(std::string* output) const {
271 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
272
273 DictionaryValue toplevel;
274 base::Time now = base::Time::Now();
275 TransportSecurityState::Iterator state(*transport_security_state_);
276 for (; state.HasNext(); state.Advance()) {
277 const std::string& hostname = state.hostname();
278 const TransportSecurityState::DomainState& domain_state =
279 state.domain_state();
280
281 DictionaryValue* serialized = new DictionaryValue;
282 serialized->SetBoolean("include_subdomains",
283 domain_state.include_subdomains);
284 serialized->SetDouble("created", domain_state.created.ToDoubleT());
285 serialized->SetDouble("expiry", domain_state.upgrade_expiry.ToDoubleT());
286 serialized->SetDouble("dynamic_spki_hashes_expiry",
287 domain_state.dynamic_spki_hashes_expiry.ToDoubleT());
288
289 switch (domain_state.upgrade_mode) {
290 case TransportSecurityState::DomainState::MODE_FORCE_HTTPS:
291 serialized->SetString("mode", "force-https");
292 break;
293 case TransportSecurityState::DomainState::MODE_DEFAULT:
294 serialized->SetString("mode", "default");
295 break;
296 default:
297 NOTREACHED() << "DomainState with unknown mode";
298 delete serialized;
299 continue;
300 }
301
302 serialized->Set("static_spki_hashes",
303 SPKIHashesToListValue(domain_state.static_spki_hashes));
304
305 if (now < domain_state.dynamic_spki_hashes_expiry) {
306 serialized->Set("dynamic_spki_hashes",
307 SPKIHashesToListValue(domain_state.dynamic_spki_hashes));
308 }
309
310 toplevel.Set(HashedDomainToExternalString(hostname), serialized);
311 }
312
313 base::JSONWriter::WriteWithOptions(&toplevel,
314 base::JSONWriter::OPTIONS_PRETTY_PRINT,
315 output);
316 return true;
317 }
318
104 bool TransportSecurityPersister::SerializeData(std::string* data) { 319 bool TransportSecurityPersister::SerializeData(std::string* data) {
105 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 320 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
106 return transport_security_state_->Serialise(data); 321 return Serialize(data);
107 } 322 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698