OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "net/http/transport_security_persister.h" | 5 #include "net/http/transport_security_persister.h" |
6 | 6 |
7 #include <memory> | 7 #include <memory> |
8 #include <utility> | 8 #include <utility> |
9 | 9 |
10 #include "base/base64.h" | 10 #include "base/base64.h" |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "base/feature_list.h" |
12 #include "base/files/file_path.h" | 13 #include "base/files/file_path.h" |
13 #include "base/files/file_util.h" | 14 #include "base/files/file_util.h" |
14 #include "base/json/json_reader.h" | 15 #include "base/json/json_reader.h" |
15 #include "base/json/json_writer.h" | 16 #include "base/json/json_writer.h" |
16 #include "base/location.h" | 17 #include "base/location.h" |
17 #include "base/sequenced_task_runner.h" | 18 #include "base/sequenced_task_runner.h" |
18 #include "base/task_runner_util.h" | 19 #include "base/task_runner_util.h" |
19 #include "base/threading/thread_task_runner_handle.h" | 20 #include "base/threading/thread_task_runner_handle.h" |
20 #include "base/values.h" | 21 #include "base/values.h" |
21 #include "crypto/sha2.h" | 22 #include "crypto/sha2.h" |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
74 const char kDynamicSPKIHashesExpiry[] = "dynamic_spki_hashes_expiry"; | 75 const char kDynamicSPKIHashesExpiry[] = "dynamic_spki_hashes_expiry"; |
75 const char kDynamicSPKIHashes[] = "dynamic_spki_hashes"; | 76 const char kDynamicSPKIHashes[] = "dynamic_spki_hashes"; |
76 const char kForceHTTPS[] = "force-https"; | 77 const char kForceHTTPS[] = "force-https"; |
77 const char kStrict[] = "strict"; | 78 const char kStrict[] = "strict"; |
78 const char kDefault[] = "default"; | 79 const char kDefault[] = "default"; |
79 const char kPinningOnly[] = "pinning-only"; | 80 const char kPinningOnly[] = "pinning-only"; |
80 const char kCreated[] = "created"; | 81 const char kCreated[] = "created"; |
81 const char kStsObserved[] = "sts_observed"; | 82 const char kStsObserved[] = "sts_observed"; |
82 const char kPkpObserved[] = "pkp_observed"; | 83 const char kPkpObserved[] = "pkp_observed"; |
83 const char kReportUri[] = "report-uri"; | 84 const char kReportUri[] = "report-uri"; |
| 85 // The keys below are contained in a subdictionary keyed as |
| 86 // |kExpectCTSubdictionary|. |
| 87 const char kExpectCTSubdictionary[] = "expect_ct"; |
| 88 const char kExpectCTExpiry[] = "expect_ct_expiry"; |
| 89 const char kExpectCTObserved[] = "expect_ct_observed"; |
| 90 const char kExpectCTEnforce[] = "expect_ct_enforce"; |
| 91 const char kExpectCTReportUri[] = "expect_ct_report_uri"; |
84 | 92 |
85 std::string LoadState(const base::FilePath& path) { | 93 std::string LoadState(const base::FilePath& path) { |
86 std::string result; | 94 std::string result; |
87 if (!base::ReadFileToString(path, &result)) { | 95 if (!base::ReadFileToString(path, &result)) { |
88 return ""; | 96 return ""; |
89 } | 97 } |
90 return result; | 98 return result; |
91 } | 99 } |
92 | 100 |
93 } // namespace | 101 bool IsDynamicExpectCTEnabled() { |
94 | 102 return base::FeatureList::IsEnabled( |
95 TransportSecurityPersister::TransportSecurityPersister( | 103 TransportSecurityState::kDynamicExpectCTFeature); |
96 TransportSecurityState* state, | |
97 const base::FilePath& profile_path, | |
98 const scoped_refptr<base::SequencedTaskRunner>& background_runner, | |
99 bool readonly) | |
100 : transport_security_state_(state), | |
101 writer_(profile_path.AppendASCII("TransportSecurity"), background_runner), | |
102 foreground_runner_(base::ThreadTaskRunnerHandle::Get()), | |
103 background_runner_(background_runner), | |
104 readonly_(readonly), | |
105 weak_ptr_factory_(this) { | |
106 transport_security_state_->SetDelegate(this); | |
107 | |
108 base::PostTaskAndReplyWithResult( | |
109 background_runner_.get(), FROM_HERE, | |
110 base::Bind(&LoadState, writer_.path()), | |
111 base::Bind(&TransportSecurityPersister::CompleteLoad, | |
112 weak_ptr_factory_.GetWeakPtr())); | |
113 } | 104 } |
114 | 105 |
115 TransportSecurityPersister::~TransportSecurityPersister() { | 106 // Populates |host| with default values for the STS and PKP states. |
116 DCHECK(foreground_runner_->RunsTasksOnCurrentThread()); | 107 // These default values represent "null" states and are only useful to keep |
| 108 // the entries in the resulting JSON consistent. The deserializer will ignore |
| 109 // "null" states. |
| 110 // TODO(davidben): This can be removed when the STS and PKP states are stored |
| 111 // independently on disk. https://crbug.com/470295 |
| 112 void PopulateEntryWithDefaults(base::DictionaryValue* host) { |
| 113 host->Clear(); |
117 | 114 |
118 if (writer_.HasPendingWrite()) | 115 // STS default values. |
119 writer_.DoScheduledWrite(); | 116 host->SetBoolean(kStsIncludeSubdomains, false); |
| 117 host->SetDouble(kStsObserved, 0.0); |
| 118 host->SetDouble(kExpiry, 0.0); |
| 119 host->SetString(kMode, kDefault); |
120 | 120 |
121 transport_security_state_->SetDelegate(NULL); | 121 // PKP default values. |
| 122 host->SetBoolean(kPkpIncludeSubdomains, false); |
| 123 host->SetDouble(kPkpObserved, 0.0); |
| 124 host->SetDouble(kDynamicSPKIHashesExpiry, 0.0); |
122 } | 125 } |
123 | 126 |
124 void TransportSecurityPersister::StateIsDirty( | 127 // Serializes STS data from |state| into |toplevel|. Any existing state in |
125 TransportSecurityState* state) { | 128 // |toplevel| for each item is overwritten. |
126 DCHECK(foreground_runner_->RunsTasksOnCurrentThread()); | 129 void SerializeSTSData(TransportSecurityState* state, |
127 DCHECK_EQ(transport_security_state_, state); | 130 base::DictionaryValue* toplevel) { |
128 | 131 TransportSecurityState::STSStateIterator sts_iterator(*state); |
129 if (!readonly_) | |
130 writer_.ScheduleWrite(this); | |
131 } | |
132 | |
133 bool TransportSecurityPersister::SerializeData(std::string* output) { | |
134 DCHECK(foreground_runner_->RunsTasksOnCurrentThread()); | |
135 | |
136 base::DictionaryValue toplevel; | |
137 base::Time now = base::Time::Now(); | |
138 | |
139 // TODO(davidben): Fix the serialization format by splitting the on-disk | |
140 // representation of the STS and PKP states. https://crbug.com/470295. | |
141 TransportSecurityState::STSStateIterator sts_iterator( | |
142 *transport_security_state_); | |
143 for (; sts_iterator.HasNext(); sts_iterator.Advance()) { | 132 for (; sts_iterator.HasNext(); sts_iterator.Advance()) { |
144 const std::string& hostname = sts_iterator.hostname(); | 133 const std::string& hostname = sts_iterator.hostname(); |
145 const TransportSecurityState::STSState& sts_state = | 134 const TransportSecurityState::STSState& sts_state = |
146 sts_iterator.domain_state(); | 135 sts_iterator.domain_state(); |
147 | 136 |
148 const std::string key = HashedDomainToExternalString(hostname); | 137 const std::string key = HashedDomainToExternalString(hostname); |
149 std::unique_ptr<base::DictionaryValue> serialized( | 138 std::unique_ptr<base::DictionaryValue> serialized( |
150 new base::DictionaryValue); | 139 new base::DictionaryValue); |
151 PopulateEntryWithDefaults(serialized.get()); | 140 PopulateEntryWithDefaults(serialized.get()); |
152 | 141 |
153 serialized->SetBoolean(kStsIncludeSubdomains, sts_state.include_subdomains); | 142 serialized->SetBoolean(kStsIncludeSubdomains, sts_state.include_subdomains); |
154 serialized->SetDouble(kStsObserved, sts_state.last_observed.ToDoubleT()); | 143 serialized->SetDouble(kStsObserved, sts_state.last_observed.ToDoubleT()); |
155 serialized->SetDouble(kExpiry, sts_state.expiry.ToDoubleT()); | 144 serialized->SetDouble(kExpiry, sts_state.expiry.ToDoubleT()); |
156 | 145 |
157 switch (sts_state.upgrade_mode) { | 146 switch (sts_state.upgrade_mode) { |
158 case TransportSecurityState::STSState::MODE_FORCE_HTTPS: | 147 case TransportSecurityState::STSState::MODE_FORCE_HTTPS: |
159 serialized->SetString(kMode, kForceHTTPS); | 148 serialized->SetString(kMode, kForceHTTPS); |
160 break; | 149 break; |
161 case TransportSecurityState::STSState::MODE_DEFAULT: | 150 case TransportSecurityState::STSState::MODE_DEFAULT: |
162 serialized->SetString(kMode, kDefault); | 151 serialized->SetString(kMode, kDefault); |
163 break; | 152 break; |
164 default: | 153 default: |
165 NOTREACHED() << "STSState with unknown mode"; | 154 NOTREACHED() << "STSState with unknown mode"; |
166 continue; | 155 continue; |
167 } | 156 } |
168 | 157 |
169 toplevel.Set(key, std::move(serialized)); | 158 toplevel->Set(key, std::move(serialized)); |
170 } | 159 } |
| 160 } |
171 | 161 |
172 TransportSecurityState::PKPStateIterator pkp_iterator( | 162 // Serializes PKP data from |state| into |toplevel|. For each PKP item in |
173 *transport_security_state_); | 163 // |state|, if |toplevel| already contains an item for that hostname, the item |
| 164 // is updated with the PKP data. |
| 165 void SerializePKPData(TransportSecurityState* state, |
| 166 base::DictionaryValue* toplevel) { |
| 167 base::Time now = base::Time::Now(); |
| 168 TransportSecurityState::PKPStateIterator pkp_iterator(*state); |
174 for (; pkp_iterator.HasNext(); pkp_iterator.Advance()) { | 169 for (; pkp_iterator.HasNext(); pkp_iterator.Advance()) { |
175 const std::string& hostname = pkp_iterator.hostname(); | 170 const std::string& hostname = pkp_iterator.hostname(); |
176 const TransportSecurityState::PKPState& pkp_state = | 171 const TransportSecurityState::PKPState& pkp_state = |
177 pkp_iterator.domain_state(); | 172 pkp_iterator.domain_state(); |
178 | 173 |
179 // See if the current |hostname| already has STS state and, if so, update | 174 // See if the current |hostname| already has STS state and, if so, update |
180 // that entry. | 175 // that entry. |
181 const std::string key = HashedDomainToExternalString(hostname); | 176 const std::string key = HashedDomainToExternalString(hostname); |
182 base::DictionaryValue* serialized = nullptr; | 177 base::DictionaryValue* serialized = nullptr; |
183 if (!toplevel.GetDictionary(key, &serialized)) { | 178 if (!toplevel->GetDictionary(key, &serialized)) { |
184 std::unique_ptr<base::DictionaryValue> serialized_scoped( | 179 std::unique_ptr<base::DictionaryValue> serialized_scoped( |
185 new base::DictionaryValue); | 180 new base::DictionaryValue); |
186 serialized = serialized_scoped.get(); | 181 serialized = serialized_scoped.get(); |
187 PopulateEntryWithDefaults(serialized); | 182 PopulateEntryWithDefaults(serialized); |
188 toplevel.Set(key, std::move(serialized_scoped)); | 183 toplevel->Set(key, std::move(serialized_scoped)); |
189 } | 184 } |
190 | 185 |
191 serialized->SetBoolean(kPkpIncludeSubdomains, pkp_state.include_subdomains); | 186 serialized->SetBoolean(kPkpIncludeSubdomains, pkp_state.include_subdomains); |
192 serialized->SetDouble(kPkpObserved, pkp_state.last_observed.ToDoubleT()); | 187 serialized->SetDouble(kPkpObserved, pkp_state.last_observed.ToDoubleT()); |
193 serialized->SetDouble(kDynamicSPKIHashesExpiry, | 188 serialized->SetDouble(kDynamicSPKIHashesExpiry, |
194 pkp_state.expiry.ToDoubleT()); | 189 pkp_state.expiry.ToDoubleT()); |
195 | 190 |
196 // TODO(svaldez): Historically, both SHA-1 and SHA-256 hashes were | 191 // TODO(svaldez): Historically, both SHA-1 and SHA-256 hashes were |
197 // accepted in pins. Per spec, only SHA-256 is accepted now, however | 192 // accepted in pins. Per spec, only SHA-256 is accepted now, however |
198 // existing serialized pins are still processed. Migrate historical pins | 193 // existing serialized pins are still processed. Migrate historical pins |
199 // with SHA-1 hashes properly, either by dropping just the bad hashes or | 194 // with SHA-1 hashes properly, either by dropping just the bad hashes or |
200 // the entire pin. See https://crbug.com/448501. | 195 // the entire pin. See https://crbug.com/448501. |
201 if (now < pkp_state.expiry) { | 196 if (now < pkp_state.expiry) { |
202 serialized->Set(kDynamicSPKIHashes, | 197 serialized->Set(kDynamicSPKIHashes, |
203 SPKIHashesToListValue(pkp_state.spki_hashes)); | 198 SPKIHashesToListValue(pkp_state.spki_hashes)); |
204 } | 199 } |
205 | 200 |
206 serialized->SetString(kReportUri, pkp_state.report_uri.spec()); | 201 serialized->SetString(kReportUri, pkp_state.report_uri.spec()); |
207 } | 202 } |
| 203 } |
| 204 |
| 205 // Serializes Expect-CT data from |state| into |toplevel|. For each Expect-CT |
| 206 // item in |state|, if |toplevel| already contains an item for that hostname, |
| 207 // the item is updated to include a subdictionary with key |
| 208 // |kExpectCTSubdictionary|; otherwise an item is created for that hostname with |
| 209 // a |kExpectCTSubdictionary| subdictionary. |
| 210 void SerializeExpectCTData(TransportSecurityState* state, |
| 211 base::DictionaryValue* toplevel) { |
| 212 if (!IsDynamicExpectCTEnabled()) |
| 213 return; |
| 214 TransportSecurityState::ExpectCTStateIterator expect_ct_iterator(*state); |
| 215 for (; expect_ct_iterator.HasNext(); expect_ct_iterator.Advance()) { |
| 216 const std::string& hostname = expect_ct_iterator.hostname(); |
| 217 const TransportSecurityState::ExpectCTState& expect_ct_state = |
| 218 expect_ct_iterator.domain_state(); |
| 219 |
| 220 // See if the current |hostname| already has STS/PKP state and, if so, |
| 221 // update that entry. |
| 222 const std::string key = HashedDomainToExternalString(hostname); |
| 223 base::DictionaryValue* serialized = nullptr; |
| 224 if (!toplevel->GetDictionary(key, &serialized)) { |
| 225 std::unique_ptr<base::DictionaryValue> serialized_scoped( |
| 226 new base::DictionaryValue); |
| 227 serialized = serialized_scoped.get(); |
| 228 PopulateEntryWithDefaults(serialized); |
| 229 toplevel->Set(key, std::move(serialized_scoped)); |
| 230 } |
| 231 |
| 232 std::unique_ptr<base::DictionaryValue> expect_ct_subdictionary( |
| 233 new base::DictionaryValue()); |
| 234 expect_ct_subdictionary->SetDouble( |
| 235 kExpectCTObserved, expect_ct_state.last_observed.ToDoubleT()); |
| 236 expect_ct_subdictionary->SetDouble(kExpectCTExpiry, |
| 237 expect_ct_state.expiry.ToDoubleT()); |
| 238 expect_ct_subdictionary->SetBoolean(kExpectCTEnforce, |
| 239 expect_ct_state.enforce); |
| 240 expect_ct_subdictionary->SetString(kExpectCTReportUri, |
| 241 expect_ct_state.report_uri.spec()); |
| 242 serialized->Set(kExpectCTSubdictionary, std::move(expect_ct_subdictionary)); |
| 243 } |
| 244 } |
| 245 |
| 246 // Populates |state| with the values in the |kExpectCTSubdictionary| |
| 247 // subdictionary in |parsed|. Returns false if |parsed| is malformed |
| 248 // (e.g. missing a required Expect-CT key) and true otherwise. Note that true |
| 249 // does not necessarily mean that Expect-CT state was present in |parsed|. |
| 250 bool DeserializeExpectCTState(const base::DictionaryValue* parsed, |
| 251 TransportSecurityState::ExpectCTState* state) { |
| 252 const base::DictionaryValue* expect_ct_subdictionary; |
| 253 if (!parsed->GetDictionary(kExpectCTSubdictionary, |
| 254 &expect_ct_subdictionary)) { |
| 255 // Expect-CT data is not required, so this item is not malformed. |
| 256 return true; |
| 257 } |
| 258 double observed; |
| 259 bool has_observed = |
| 260 expect_ct_subdictionary->GetDouble(kExpectCTObserved, &observed); |
| 261 double expiry; |
| 262 bool has_expiry = |
| 263 expect_ct_subdictionary->GetDouble(kExpectCTExpiry, &expiry); |
| 264 bool enforce; |
| 265 bool has_enforce = |
| 266 expect_ct_subdictionary->GetBoolean(kExpectCTEnforce, &enforce); |
| 267 std::string report_uri_str; |
| 268 bool has_report_uri = |
| 269 expect_ct_subdictionary->GetString(kExpectCTReportUri, &report_uri_str); |
| 270 |
| 271 // If an Expect-CT subdictionary is present, it must have the required keys. |
| 272 if (!has_observed || !has_expiry || !has_enforce) |
| 273 return false; |
| 274 |
| 275 state->last_observed = base::Time::FromDoubleT(observed); |
| 276 state->expiry = base::Time::FromDoubleT(expiry); |
| 277 state->enforce = enforce; |
| 278 if (has_report_uri) { |
| 279 GURL report_uri(report_uri_str); |
| 280 if (report_uri.is_valid()) |
| 281 state->report_uri = report_uri; |
| 282 } |
| 283 return true; |
| 284 } |
| 285 |
| 286 } // namespace |
| 287 |
| 288 TransportSecurityPersister::TransportSecurityPersister( |
| 289 TransportSecurityState* state, |
| 290 const base::FilePath& profile_path, |
| 291 const scoped_refptr<base::SequencedTaskRunner>& background_runner, |
| 292 bool readonly) |
| 293 : transport_security_state_(state), |
| 294 writer_(profile_path.AppendASCII("TransportSecurity"), background_runner), |
| 295 foreground_runner_(base::ThreadTaskRunnerHandle::Get()), |
| 296 background_runner_(background_runner), |
| 297 readonly_(readonly), |
| 298 weak_ptr_factory_(this) { |
| 299 transport_security_state_->SetDelegate(this); |
| 300 |
| 301 base::PostTaskAndReplyWithResult( |
| 302 background_runner_.get(), FROM_HERE, |
| 303 base::Bind(&LoadState, writer_.path()), |
| 304 base::Bind(&TransportSecurityPersister::CompleteLoad, |
| 305 weak_ptr_factory_.GetWeakPtr())); |
| 306 } |
| 307 |
| 308 TransportSecurityPersister::~TransportSecurityPersister() { |
| 309 DCHECK(foreground_runner_->RunsTasksOnCurrentThread()); |
| 310 |
| 311 if (writer_.HasPendingWrite()) |
| 312 writer_.DoScheduledWrite(); |
| 313 |
| 314 transport_security_state_->SetDelegate(NULL); |
| 315 } |
| 316 |
| 317 void TransportSecurityPersister::StateIsDirty(TransportSecurityState* state) { |
| 318 DCHECK(foreground_runner_->RunsTasksOnCurrentThread()); |
| 319 DCHECK_EQ(transport_security_state_, state); |
| 320 |
| 321 if (!readonly_) |
| 322 writer_.ScheduleWrite(this); |
| 323 } |
| 324 |
| 325 bool TransportSecurityPersister::SerializeData(std::string* output) { |
| 326 DCHECK(foreground_runner_->RunsTasksOnCurrentThread()); |
| 327 |
| 328 base::DictionaryValue toplevel; |
| 329 |
| 330 // TODO(davidben): Fix the serialization format by splitting the on-disk |
| 331 // representation of the STS and PKP states. https://crbug.com/470295. |
| 332 SerializeSTSData(transport_security_state_, &toplevel); |
| 333 SerializePKPData(transport_security_state_, &toplevel); |
| 334 SerializeExpectCTData(transport_security_state_, &toplevel); |
208 | 335 |
209 base::JSONWriter::WriteWithOptions( | 336 base::JSONWriter::WriteWithOptions( |
210 toplevel, base::JSONWriter::OPTIONS_PRETTY_PRINT, output); | 337 toplevel, base::JSONWriter::OPTIONS_PRETTY_PRINT, output); |
211 return true; | 338 return true; |
212 } | 339 } |
213 | 340 |
214 bool TransportSecurityPersister::LoadEntries(const std::string& serialized, | 341 bool TransportSecurityPersister::LoadEntries(const std::string& serialized, |
215 bool* dirty) { | 342 bool* dirty) { |
216 DCHECK(foreground_runner_->RunsTasksOnCurrentThread()); | 343 DCHECK(foreground_runner_->RunsTasksOnCurrentThread()); |
217 | 344 |
(...skipping 16 matching lines...) Expand all Loading... |
234 for (base::DictionaryValue::Iterator i(*dict_value); | 361 for (base::DictionaryValue::Iterator i(*dict_value); |
235 !i.IsAtEnd(); i.Advance()) { | 362 !i.IsAtEnd(); i.Advance()) { |
236 const base::DictionaryValue* parsed = NULL; | 363 const base::DictionaryValue* parsed = NULL; |
237 if (!i.value().GetAsDictionary(&parsed)) { | 364 if (!i.value().GetAsDictionary(&parsed)) { |
238 LOG(WARNING) << "Could not parse entry " << i.key() << "; skipping entry"; | 365 LOG(WARNING) << "Could not parse entry " << i.key() << "; skipping entry"; |
239 continue; | 366 continue; |
240 } | 367 } |
241 | 368 |
242 TransportSecurityState::STSState sts_state; | 369 TransportSecurityState::STSState sts_state; |
243 TransportSecurityState::PKPState pkp_state; | 370 TransportSecurityState::PKPState pkp_state; |
| 371 TransportSecurityState::ExpectCTState expect_ct_state; |
244 | 372 |
245 // kIncludeSubdomains is a legacy synonym for kStsIncludeSubdomains and | 373 // kIncludeSubdomains is a legacy synonym for kStsIncludeSubdomains and |
246 // kPkpIncludeSubdomains. Parse at least one of these properties, | 374 // kPkpIncludeSubdomains. Parse at least one of these properties, |
247 // preferably the new ones. | 375 // preferably the new ones. |
248 bool include_subdomains = false; | 376 bool include_subdomains = false; |
249 bool parsed_include_subdomains = parsed->GetBoolean(kIncludeSubdomains, | 377 bool parsed_include_subdomains = parsed->GetBoolean(kIncludeSubdomains, |
250 &include_subdomains); | 378 &include_subdomains); |
251 sts_state.include_subdomains = include_subdomains; | 379 sts_state.include_subdomains = include_subdomains; |
252 pkp_state.include_subdomains = include_subdomains; | 380 pkp_state.include_subdomains = include_subdomains; |
253 if (parsed->GetBoolean(kStsIncludeSubdomains, &include_subdomains)) { | 381 if (parsed->GetBoolean(kStsIncludeSubdomains, &include_subdomains)) { |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
316 } | 444 } |
317 if (parsed->GetDouble(kPkpObserved, &pkp_observed)) { | 445 if (parsed->GetDouble(kPkpObserved, &pkp_observed)) { |
318 pkp_state.last_observed = base::Time::FromDoubleT(pkp_observed); | 446 pkp_state.last_observed = base::Time::FromDoubleT(pkp_observed); |
319 } else if (parsed->GetDouble(kCreated, &pkp_observed)) { | 447 } else if (parsed->GetDouble(kCreated, &pkp_observed)) { |
320 pkp_state.last_observed = base::Time::FromDoubleT(pkp_observed); | 448 pkp_state.last_observed = base::Time::FromDoubleT(pkp_observed); |
321 } else { | 449 } else { |
322 dirtied = true; | 450 dirtied = true; |
323 pkp_state.last_observed = base::Time::Now(); | 451 pkp_state.last_observed = base::Time::Now(); |
324 } | 452 } |
325 | 453 |
| 454 if (!DeserializeExpectCTState(parsed, &expect_ct_state)) { |
| 455 continue; |
| 456 } |
| 457 |
326 bool has_sts = | 458 bool has_sts = |
327 sts_state.expiry > current_time && sts_state.ShouldUpgradeToSSL(); | 459 sts_state.expiry > current_time && sts_state.ShouldUpgradeToSSL(); |
328 bool has_pkp = | 460 bool has_pkp = |
329 pkp_state.expiry > current_time && pkp_state.HasPublicKeyPins(); | 461 pkp_state.expiry > current_time && pkp_state.HasPublicKeyPins(); |
330 if (!has_sts && !has_pkp) { | 462 bool has_expect_ct = |
| 463 expect_ct_state.expiry > current_time && |
| 464 (expect_ct_state.enforce || !expect_ct_state.report_uri.is_empty()); |
| 465 if (!has_sts && !has_pkp && !has_expect_ct) { |
331 // Make sure we dirty the state if we drop an entry. The entries can only | 466 // Make sure we dirty the state if we drop an entry. The entries can only |
332 // be dropped when both the STS and PKP states are expired or invalid. | 467 // be dropped when all the STS, PKP, and Expect-CT states are expired or |
| 468 // invalid. |
333 dirtied = true; | 469 dirtied = true; |
334 continue; | 470 continue; |
335 } | 471 } |
336 | 472 |
337 std::string hashed = ExternalStringToHashedDomain(i.key()); | 473 std::string hashed = ExternalStringToHashedDomain(i.key()); |
338 if (hashed.empty()) { | 474 if (hashed.empty()) { |
339 dirtied = true; | 475 dirtied = true; |
340 continue; | 476 continue; |
341 } | 477 } |
342 | 478 |
343 // Until the on-disk storage is split, there will always be 'null' entries. | 479 // Until the on-disk storage is split, there will always be 'null' entries. |
344 // We only register entries that have actual state. | 480 // We only register entries that have actual state. |
345 if (has_sts) | 481 if (has_sts) |
346 state->AddOrUpdateEnabledSTSHosts(hashed, sts_state); | 482 state->AddOrUpdateEnabledSTSHosts(hashed, sts_state); |
347 if (has_pkp) | 483 if (has_pkp) |
348 state->AddOrUpdateEnabledPKPHosts(hashed, pkp_state); | 484 state->AddOrUpdateEnabledPKPHosts(hashed, pkp_state); |
| 485 if (has_expect_ct) |
| 486 state->AddOrUpdateEnabledExpectCTHosts(hashed, expect_ct_state); |
349 } | 487 } |
350 | 488 |
351 *dirty = dirtied; | 489 *dirty = dirtied; |
352 return true; | 490 return true; |
353 } | 491 } |
354 | 492 |
355 void TransportSecurityPersister::PopulateEntryWithDefaults( | |
356 base::DictionaryValue* host) { | |
357 host->Clear(); | |
358 | |
359 // STS default values. | |
360 host->SetBoolean(kStsIncludeSubdomains, false); | |
361 host->SetDouble(kStsObserved, 0.0); | |
362 host->SetDouble(kExpiry, 0.0); | |
363 host->SetString(kMode, kDefault); | |
364 | |
365 // PKP default values. | |
366 host->SetBoolean(kPkpIncludeSubdomains, false); | |
367 host->SetDouble(kPkpObserved, 0.0); | |
368 host->SetDouble(kDynamicSPKIHashesExpiry, 0.0); | |
369 } | |
370 | |
371 void TransportSecurityPersister::CompleteLoad(const std::string& state) { | 493 void TransportSecurityPersister::CompleteLoad(const std::string& state) { |
372 DCHECK(foreground_runner_->RunsTasksOnCurrentThread()); | 494 DCHECK(foreground_runner_->RunsTasksOnCurrentThread()); |
373 | 495 |
374 if (state.empty()) | 496 if (state.empty()) |
375 return; | 497 return; |
376 | 498 |
377 bool dirty = false; | 499 bool dirty = false; |
378 if (!LoadEntries(state, &dirty)) { | 500 if (!LoadEntries(state, &dirty)) { |
379 LOG(ERROR) << "Failed to deserialize state: " << state; | 501 LOG(ERROR) << "Failed to deserialize state: " << state; |
380 return; | 502 return; |
381 } | 503 } |
382 if (dirty) | 504 if (dirty) |
383 StateIsDirty(transport_security_state_); | 505 StateIsDirty(transport_security_state_); |
384 } | 506 } |
385 | 507 |
386 } // namespace net | 508 } // namespace net |
OLD | NEW |