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

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) {
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) {
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));
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))
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",
174 &dynamic_spki_hashes_expiry);
175
176 ListValue* pins_list = NULL;
177 FingerprintVector static_spki_hashes;
178 if (parsed->GetList("preloaded_spki_hashes", &pins_list))
179 SPKIHashesFromListValue(&static_spki_hashes, *pins_list);
180
181 FingerprintVector dynamic_spki_hashes;
182 if (parsed->GetList("dynamic_spki_hashes", &pins_list))
183 SPKIHashesFromListValue(&dynamic_spki_hashes, *pins_list);
184
185 TransportSecurityState::DomainState::UpgradeMode mode;
186 if (mode_string == "strict") {
187 mode = TransportSecurityState::DomainState::MODE_FORCE_HTTPS;
188 } else if (mode_string == "spdy-only") {
189 mode = TransportSecurityState::DomainState::MODE_DEFAULT;
190 } else if (mode_string == "pinning-only") {
191 mode = TransportSecurityState::DomainState::MODE_DEFAULT;
192 } else {
193 LOG(WARNING) << "Unknown TransportSecurityState mode string found: "
194 << mode_string;
195 continue;
196 }
197
198 base::Time expiry_time = base::Time::FromDoubleT(expiry);
199 base::Time dynamic_spki_hashes_expiry_time =
200 base::Time::FromDoubleT(dynamic_spki_hashes_expiry);
201 base::Time created_time;
202 if (parsed->GetDouble("created", &created)) {
203 created_time = base::Time::FromDoubleT(created);
204 } else {
205 // We're migrating an old entry with no creation date. Make sure we
206 // write the new date back in a reasonable time frame.
207 dirtied = true;
208 created_time = base::Time::Now();
209 }
210
211 if (expiry_time <= current_time &&
212 dynamic_spki_hashes_expiry_time <= current_time) {
213 // Make sure we dirty the state if we drop an entry.
214 dirtied = true;
215 continue;
216 }
217
218 std::string hashed = ExternalStringToHashedDomain(*i);
219 if (hashed.empty()) {
220 dirtied = true;
221 continue;
222 }
223
224 TransportSecurityState::DomainState domain_state;
225 domain_state.upgrade_mode = mode;
226 domain_state.created = created_time;
227 domain_state.upgrade_expiry = expiry_time;
228 domain_state.include_subdomains = include_subdomains;
229 domain_state.static_spki_hashes = static_spki_hashes;
230 domain_state.dynamic_spki_hashes = dynamic_spki_hashes;
231 domain_state.dynamic_spki_hashes_expiry = dynamic_spki_hashes_expiry_time;
232 state->EnableHost(hashed, domain_state);
233 }
234
235 *dirty = dirtied;
236 return true;
237 }
238
239 bool TransportSecurityPersister::LoadEntries(const std::string& serialized,
240 bool* dirty,
241 TransportSecurityState* state) {
242 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
243
244 state->Clear();
245 return Deserialize(serialized, dirty, state);
246 }
247
83 void TransportSecurityPersister::CompleteLoad(const std::string& state) { 248 void TransportSecurityPersister::CompleteLoad(const std::string& state) {
84 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
85 250
86 bool dirty = false; 251 bool dirty = false;
87 if (!transport_security_state_->LoadEntries(state, &dirty)) { 252 if (!LoadEntries(state, &dirty, transport_security_state_)) {
88 LOG(ERROR) << "Failed to deserialize state: " << state; 253 LOG(ERROR) << "Failed to deserialize state: " << state;
89 return; 254 return;
90 } 255 }
91 if (dirty) 256 if (dirty)
92 StateIsDirty(transport_security_state_); 257 StateIsDirty(transport_security_state_);
93 } 258 }
94 259
95 void TransportSecurityPersister::StateIsDirty( 260 void TransportSecurityPersister::StateIsDirty(
96 net::TransportSecurityState* state) { 261 TransportSecurityState* state) {
97 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
98 DCHECK_EQ(transport_security_state_, state); 263 DCHECK_EQ(transport_security_state_, state);
99 264
100 if (!readonly_) 265 if (!readonly_)
101 writer_.ScheduleWrite(this); 266 writer_.ScheduleWrite(this);
102 } 267 }
103 268
269 bool TransportSecurityPersister::Serialize(std::string* output) const {
270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
271
272 DictionaryValue toplevel;
273 base::Time now = base::Time::Now();
274 TransportSecurityState::Iterator state(*transport_security_state_);
275 for (; state.HasNext(); state.Advance()) {
276 const std::string& hostname = state.hostname();
277 const TransportSecurityState::DomainState& domain_state =
278 state.domain_state();
279
280 DictionaryValue* serialized = new DictionaryValue;
281 serialized->SetBoolean("include_subdomains",
282 domain_state.include_subdomains);
283 serialized->SetDouble("created", domain_state.created.ToDoubleT());
284 serialized->SetDouble("expiry", domain_state.upgrade_expiry.ToDoubleT());
285 serialized->SetDouble("dynamic_spki_hashes_expiry",
286 domain_state.dynamic_spki_hashes_expiry.ToDoubleT());
287
288 switch (domain_state.upgrade_mode) {
289 case TransportSecurityState::DomainState::MODE_FORCE_HTTPS:
290 serialized->SetString("mode", "strict");
291 break;
292 case TransportSecurityState::DomainState::MODE_DEFAULT:
293 serialized->SetString("mode", "pinning-only");
294 break;
295 default:
296 NOTREACHED() << "DomainState with unknown mode";
297 delete serialized;
298 continue;
299 }
300
301 serialized->Set("preloaded_spki_hashes",
302 SPKIHashesToListValue(domain_state.static_spki_hashes));
303
304 if (now < domain_state.dynamic_spki_hashes_expiry) {
305 serialized->Set("dynamic_spki_hashes",
306 SPKIHashesToListValue(domain_state.dynamic_spki_hashes));
307 }
308
309 toplevel.Set(HashedDomainToExternalString(hostname), serialized);
310 }
311
312 base::JSONWriter::WriteWithOptions(&toplevel,
313 base::JSONWriter::OPTIONS_PRETTY_PRINT,
314 output);
315 return true;
316 }
317
104 bool TransportSecurityPersister::SerializeData(std::string* data) { 318 bool TransportSecurityPersister::SerializeData(std::string* data) {
105 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 319 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
106 return transport_security_state_->Serialise(data); 320 return Serialize(data);
107 } 321 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698