Chromium Code Reviews| 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 "chrome/browser/net/transport_security_persister.h" | 5 #include "chrome/browser/net/transport_security_persister.h" |
| 6 | 6 |
| 7 #include "base/base64.h" | 7 #include "base/base64.h" |
| 8 #include "base/bind.h" | 8 #include "base/bind.h" |
| 9 #include "base/file_util.h" | 9 #include "base/file_util.h" |
| 10 #include "base/files/file_path.h" | 10 #include "base/files/file_path.h" |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 152 DCHECK_EQ(transport_security_state_, state); | 152 DCHECK_EQ(transport_security_state_, state); |
| 153 | 153 |
| 154 if (!readonly_) | 154 if (!readonly_) |
| 155 writer_.ScheduleWrite(this); | 155 writer_.ScheduleWrite(this); |
| 156 } | 156 } |
| 157 | 157 |
| 158 bool TransportSecurityPersister::SerializeData(std::string* output) { | 158 bool TransportSecurityPersister::SerializeData(std::string* output) { |
| 159 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 159 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 160 | 160 |
| 161 DictionaryValue toplevel; | 161 DictionaryValue toplevel; |
| 162 base::Time now = base::Time::Now(); | 162 std::map<std::string, TransportSecurityState::DynamicEntry>::const_iterator |
| 163 TransportSecurityState::Iterator state(*transport_security_state_); | 163 iter = transport_security_state_->GetHSTSEntries().begin(); |
|
Ryan Sleevi
2013/04/29 22:06:14
So I understand why you felt the format should cha
unsafe
2013/04/30 10:42:57
I can follow-up this CL with HPKP serialization.
| |
| 164 for (; state.HasNext(); state.Advance()) { | 164 for (; iter != transport_security_state_->GetHSTSEntries().end(); ++iter) { |
| 165 const std::string& hostname = state.hostname(); | 165 const std::string& hashed_host = iter->first; |
| 166 const TransportSecurityState::DomainState& domain_state = | 166 const TransportSecurityState::DynamicEntry& entry = iter->second; |
| 167 state.domain_state(); | |
| 168 | 167 |
| 169 DictionaryValue* serialized = new DictionaryValue; | 168 DictionaryValue* serialized = new DictionaryValue; |
| 170 serialized->SetBoolean(kIncludeSubdomains, | 169 serialized->SetBoolean(kIncludeSubdomains, |
| 171 domain_state.include_subdomains); | 170 entry.include_subdomains_); |
| 172 serialized->SetDouble(kCreated, domain_state.created.ToDoubleT()); | 171 serialized->SetDouble(kCreated, entry.created_.ToDoubleT()); |
| 173 serialized->SetDouble(kExpiry, domain_state.upgrade_expiry.ToDoubleT()); | 172 serialized->SetDouble(kExpiry, entry.expiry_.ToDoubleT()); |
| 174 serialized->SetDouble(kDynamicSPKIHashesExpiry, | 173 serialized->SetString(kMode, kForceHTTPS); |
| 175 domain_state.dynamic_spki_hashes_expiry.ToDoubleT()); | 174 toplevel.Set(HashedDomainToExternalString(hashed_host), serialized); |
| 176 | |
| 177 switch (domain_state.upgrade_mode) { | |
| 178 case TransportSecurityState::DomainState::MODE_FORCE_HTTPS: | |
| 179 serialized->SetString(kMode, kForceHTTPS); | |
| 180 break; | |
| 181 case TransportSecurityState::DomainState::MODE_DEFAULT: | |
| 182 serialized->SetString(kMode, kDefault); | |
| 183 break; | |
| 184 default: | |
| 185 NOTREACHED() << "DomainState with unknown mode"; | |
| 186 delete serialized; | |
| 187 continue; | |
| 188 } | |
| 189 | |
| 190 serialized->Set(kStaticSPKIHashes, | |
| 191 SPKIHashesToListValue(domain_state.static_spki_hashes)); | |
| 192 | |
| 193 if (now < domain_state.dynamic_spki_hashes_expiry) { | |
| 194 serialized->Set(kDynamicSPKIHashes, | |
| 195 SPKIHashesToListValue(domain_state.dynamic_spki_hashes)); | |
| 196 } | |
| 197 | |
| 198 toplevel.Set(HashedDomainToExternalString(hostname), serialized); | |
| 199 } | 175 } |
| 200 | 176 |
| 201 base::JSONWriter::WriteWithOptions(&toplevel, | 177 base::JSONWriter::WriteWithOptions(&toplevel, |
| 202 base::JSONWriter::OPTIONS_PRETTY_PRINT, | 178 base::JSONWriter::OPTIONS_PRETTY_PRINT, |
| 203 output); | 179 output); |
| 204 return true; | 180 return true; |
| 205 } | 181 } |
| 206 | 182 |
| 207 bool TransportSecurityPersister::DeserializeFromCommandLine( | |
| 208 const std::string& serialized) { | |
| 209 // Purposefully ignore |dirty| because we do not want to persist entries | |
| 210 // deserialized in this way. | |
| 211 bool dirty; | |
| 212 return Deserialize(serialized, true, &dirty, transport_security_state_); | |
| 213 } | |
| 214 | |
| 215 bool TransportSecurityPersister::LoadEntries(const std::string& serialized, | 183 bool TransportSecurityPersister::LoadEntries(const std::string& serialized, |
| 216 bool* dirty) { | 184 bool* dirty) { |
| 217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 218 | 186 |
| 219 transport_security_state_->ClearDynamicData(); | 187 transport_security_state_->ClearDynamicData(); |
| 220 return Deserialize(serialized, false, dirty, transport_security_state_); | 188 return Deserialize(serialized, false, dirty, transport_security_state_); |
| 221 } | 189 } |
| 222 | 190 |
| 223 // static | 191 // static |
| 224 bool TransportSecurityPersister::Deserialize(const std::string& serialized, | 192 bool TransportSecurityPersister::Deserialize(const std::string& serialized, |
| 225 bool forced, | 193 bool forced, |
| 226 bool* dirty, | 194 bool* dirty, |
| 227 TransportSecurityState* state) { | 195 TransportSecurityState* state) { |
| 228 scoped_ptr<Value> value(base::JSONReader::Read(serialized)); | 196 scoped_ptr<Value> value(base::JSONReader::Read(serialized)); |
| 229 DictionaryValue* dict_value; | 197 DictionaryValue* dict_value; |
| 230 if (!value.get() || !value->GetAsDictionary(&dict_value)) | 198 if (!value.get() || !value->GetAsDictionary(&dict_value)) |
| 231 return false; | 199 return false; |
| 232 | 200 |
| 233 const base::Time current_time(base::Time::Now()); | 201 const base::Time current_time(base::Time::Now()); |
| 234 bool dirtied = false; | 202 bool dirtied = false; |
| 235 | 203 |
| 236 for (DictionaryValue::key_iterator i = dict_value->begin_keys(); | 204 for (DictionaryValue::key_iterator i = dict_value->begin_keys(); |
| 237 i != dict_value->end_keys(); ++i) { | 205 i != dict_value->end_keys(); ++i) { |
| 238 DictionaryValue* parsed; | 206 DictionaryValue* parsed; |
| 239 if (!dict_value->GetDictionaryWithoutPathExpansion(*i, &parsed)) { | 207 if (!dict_value->GetDictionaryWithoutPathExpansion(*i, &parsed)) { |
| 240 LOG(WARNING) << "Could not parse entry " << *i << "; skipping entry"; | 208 LOG(WARNING) << "Could not parse entry " << *i << "; skipping entry"; |
| 241 continue; | 209 continue; |
| 242 } | 210 } |
| 243 | 211 |
| 212 bool include_subdomains = false; | |
| 213 double created_double = 0, expiry_double = 0; | |
|
Ryan Sleevi
2013/04/29 22:06:14
I think it was better without the default initiali
unsafe
2013/04/30 10:42:57
Done.
| |
| 214 base::Time created, expiry; | |
| 244 std::string mode_string; | 215 std::string mode_string; |
| 245 double created; | |
| 246 double expiry; | |
| 247 double dynamic_spki_hashes_expiry = 0.0; | |
| 248 TransportSecurityState::DomainState domain_state; | |
| 249 | 216 |
| 250 if (!parsed->GetBoolean(kIncludeSubdomains, | 217 if (!parsed->GetBoolean(kIncludeSubdomains, |
| 251 &domain_state.include_subdomains) || | 218 &include_subdomains) || |
| 252 !parsed->GetString(kMode, &mode_string) || | 219 !parsed->GetString(kMode, &mode_string) || |
| 253 !parsed->GetDouble(kExpiry, &expiry)) { | 220 !parsed->GetDouble(kExpiry, &expiry_double)) { |
| 254 LOG(WARNING) << "Could not parse some elements of entry " << *i | 221 LOG(WARNING) << "Could not parse some elements of entry " << *i |
| 255 << "; skipping entry"; | 222 << "; skipping entry"; |
| 256 continue; | 223 continue; |
| 257 } | 224 } |
| 258 | 225 |
| 259 // Don't fail if this key is not present. | 226 if (parsed->GetDouble(kCreated, &created_double)) { |
| 260 parsed->GetDouble(kDynamicSPKIHashesExpiry, | 227 created = base::Time::FromDoubleT(created_double); |
| 261 &dynamic_spki_hashes_expiry); | |
| 262 | |
| 263 ListValue* pins_list = NULL; | |
| 264 // preloaded_spki_hashes is a legacy synonym for static_spki_hashes. | |
| 265 if (parsed->GetList(kStaticSPKIHashes, &pins_list)) | |
| 266 SPKIHashesFromListValue(*pins_list, &domain_state.static_spki_hashes); | |
| 267 else if (parsed->GetList(kPreloadedSPKIHashes, &pins_list)) | |
| 268 SPKIHashesFromListValue(*pins_list, &domain_state.static_spki_hashes); | |
| 269 | |
| 270 if (parsed->GetList(kDynamicSPKIHashes, &pins_list)) | |
| 271 SPKIHashesFromListValue(*pins_list, &domain_state.dynamic_spki_hashes); | |
| 272 | |
| 273 if (mode_string == kForceHTTPS || mode_string == kStrict) { | |
| 274 domain_state.upgrade_mode = | |
| 275 TransportSecurityState::DomainState::MODE_FORCE_HTTPS; | |
| 276 } else if (mode_string == kDefault || mode_string == kPinningOnly) { | |
| 277 domain_state.upgrade_mode = | |
| 278 TransportSecurityState::DomainState::MODE_DEFAULT; | |
| 279 } else { | |
| 280 LOG(WARNING) << "Unknown TransportSecurityState mode string " | |
| 281 << mode_string << " found for entry " << *i | |
| 282 << "; skipping entry"; | |
| 283 continue; | |
| 284 } | |
| 285 | |
| 286 domain_state.upgrade_expiry = base::Time::FromDoubleT(expiry); | |
| 287 domain_state.dynamic_spki_hashes_expiry = | |
| 288 base::Time::FromDoubleT(dynamic_spki_hashes_expiry); | |
| 289 if (parsed->GetDouble(kCreated, &created)) { | |
| 290 domain_state.created = base::Time::FromDoubleT(created); | |
| 291 } else { | 228 } else { |
| 292 // We're migrating an old entry with no creation date. Make sure we | 229 // We're migrating an old entry with no creation date. Make sure we |
| 293 // write the new date back in a reasonable time frame. | 230 // write the new date back in a reasonable time frame. |
| 294 dirtied = true; | 231 dirtied = true; |
| 295 domain_state.created = base::Time::Now(); | 232 created = base::Time::Now(); |
| 296 } | 233 } |
| 297 | 234 |
| 298 if (domain_state.upgrade_expiry <= current_time && | 235 if (mode_string == kForceHTTPS || mode_string == kStrict) { |
| 299 domain_state.dynamic_spki_hashes_expiry <= current_time) { | 236 std::string hashed_host = ExternalStringToHashedDomain(*i); |
| 300 // Make sure we dirty the state if we drop an entry. | 237 base::Time expiry = base::Time::FromDoubleT(expiry_double); |
| 301 dirtied = true; | 238 if (expiry > current_time) |
| 302 continue; | 239 state->AddHSTSHashedHost(hashed_host, created, expiry, |
| 240 include_subdomains); | |
| 241 else | |
| 242 dirtied = true; | |
|
Ryan Sleevi
2013/04/29 22:06:14
style: You should have braces around this if/else
unsafe
2013/04/30 10:42:57
Done.
| |
| 303 } | 243 } |
| 304 | |
| 305 std::string hashed = ExternalStringToHashedDomain(*i); | |
| 306 if (hashed.empty()) { | |
| 307 dirtied = true; | |
| 308 continue; | |
| 309 } | |
| 310 | |
| 311 if (forced) | |
| 312 state->AddOrUpdateForcedHosts(hashed, domain_state); | |
| 313 else | |
| 314 state->AddOrUpdateEnabledHosts(hashed, domain_state); | |
| 315 } | 244 } |
| 316 | 245 |
| 317 *dirty = dirtied; | 246 *dirty = dirtied; |
| 318 return true; | 247 return true; |
| 319 } | 248 } |
| 320 | 249 |
| 321 void TransportSecurityPersister::CompleteLoad(const std::string& state) { | 250 void TransportSecurityPersister::CompleteLoad(const std::string& state) { |
| 322 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 251 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 323 | 252 |
| 324 bool dirty = false; | 253 bool dirty = false; |
| 325 if (!LoadEntries(state, &dirty)) { | 254 if (!LoadEntries(state, &dirty)) { |
| 326 LOG(ERROR) << "Failed to deserialize state: " << state; | 255 LOG(ERROR) << "Failed to deserialize state: " << state; |
| 327 return; | 256 return; |
| 328 } | 257 } |
| 329 if (dirty) | 258 if (dirty) |
| 330 StateIsDirty(transport_security_state_); | 259 StateIsDirty(transport_security_state_); |
| 331 } | 260 } |
| OLD | NEW |