Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/extras/sqlite/sqlite_channel_id_store.h" | 5 #include "net/extras/sqlite/sqlite_channel_id_store.h" |
| 6 | 6 |
| 7 #include <memory> | 7 #include <memory> |
| 8 #include <set> | 8 #include <set> |
| 9 #include <utility> | 9 #include <utility> |
| 10 #include <vector> | 10 #include <vector> |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 29 #include "sql/statement.h" | 29 #include "sql/statement.h" |
| 30 #include "sql/transaction.h" | 30 #include "sql/transaction.h" |
| 31 #include "url/gurl.h" | 31 #include "url/gurl.h" |
| 32 | 32 |
| 33 namespace { | 33 namespace { |
| 34 | 34 |
| 35 // Version number of the database. | 35 // Version number of the database. |
| 36 const int kCurrentVersionNumber = 6; | 36 const int kCurrentVersionNumber = 6; |
| 37 const int kCompatibleVersionNumber = 6; | 37 const int kCompatibleVersionNumber = 6; |
| 38 | 38 |
| 39 // Used in the DomainBoundCerts.DBLoadStatus histogram to record the status of | |
| 40 // the Channel ID database when loading it from disk. It reports reasons why the | |
| 41 // db could fail to load, or that it was loaded successfully. | |
| 42 // Do not change or re-use values. | |
| 43 enum DbLoadStatus { | |
| 44 // The path for the directory containing the db doesn't exist and couldn't be | |
| 45 // created. | |
| 46 PATH_DOES_NOT_EXIST = 0, | |
| 47 // Unable to open the database. | |
| 48 FAILED_TO_OPEN = 1, | |
| 49 // Failed to migrate the db to the current version. | |
| 50 MIGRATION_FAILED = 2, | |
| 51 // Unable to execute SELECT statement to load contents from db. | |
| 52 INVALID_SELECT_STATEMENT = 3, | |
| 53 // New database successfully created. | |
| 54 NEW_DB = 4, | |
| 55 // Database successfully loaded. | |
| 56 LOADED = 5, | |
| 57 DB_LOAD_STATUS_MAX | |
| 58 }; | |
| 59 | |
| 60 void RecordDbLoadStatus(DbLoadStatus status) { | |
| 61 UMA_HISTOGRAM_ENUMERATION("DomainBoundCerts.DBLoadStatus", status, | |
| 62 DB_LOAD_STATUS_MAX); | |
| 63 } | |
| 64 | |
| 39 } // namespace | 65 } // namespace |
| 40 | 66 |
| 41 namespace net { | 67 namespace net { |
| 42 | 68 |
| 43 // This class is designed to be shared between any calling threads and the | 69 // This class is designed to be shared between any calling threads and the |
| 44 // background task runner. It batches operations and commits them on a timer. | 70 // background task runner. It batches operations and commits them on a timer. |
| 45 class SQLiteChannelIDStore::Backend | 71 class SQLiteChannelIDStore::Backend |
| 46 : public base::RefCountedThreadSafe<SQLiteChannelIDStore::Backend> { | 72 : public base::RefCountedThreadSafe<SQLiteChannelIDStore::Backend> { |
| 47 public: | 73 public: |
| 48 Backend( | 74 Backend( |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 169 DCHECK(background_task_runner_->RunsTasksOnCurrentThread()); | 195 DCHECK(background_task_runner_->RunsTasksOnCurrentThread()); |
| 170 | 196 |
| 171 // This method should be called only once per instance. | 197 // This method should be called only once per instance. |
| 172 DCHECK(!db_.get()); | 198 DCHECK(!db_.get()); |
| 173 | 199 |
| 174 base::TimeTicks start = base::TimeTicks::Now(); | 200 base::TimeTicks start = base::TimeTicks::Now(); |
| 175 | 201 |
| 176 // Ensure the parent directory for storing certs is created before reading | 202 // Ensure the parent directory for storing certs is created before reading |
| 177 // from it. | 203 // from it. |
| 178 const base::FilePath dir = path_.DirName(); | 204 const base::FilePath dir = path_.DirName(); |
| 179 if (!base::PathExists(dir) && !base::CreateDirectory(dir)) | 205 if (!base::PathExists(dir) && !base::CreateDirectory(dir)) { |
| 206 RecordDbLoadStatus(PATH_DOES_NOT_EXIST); | |
| 180 return; | 207 return; |
| 208 } | |
| 181 | 209 |
| 182 db_.reset(new sql::Connection); | 210 db_.reset(new sql::Connection); |
| 183 db_->set_histogram_tag("DomainBoundCerts"); | 211 db_->set_histogram_tag("DomainBoundCerts"); |
| 184 | 212 |
| 185 // Unretained to avoid a ref loop with db_. | 213 // Unretained to avoid a ref loop with db_. |
| 186 db_->set_error_callback( | 214 db_->set_error_callback( |
| 187 base::Bind(&SQLiteChannelIDStore::Backend::DatabaseErrorCallback, | 215 base::Bind(&SQLiteChannelIDStore::Backend::DatabaseErrorCallback, |
| 188 base::Unretained(this))); | 216 base::Unretained(this))); |
| 189 | 217 |
| 218 bool is_new = false; | |
| 219 if (!base::PathExists(path_)) { | |
| 220 is_new = true; | |
| 221 } | |
| 222 | |
| 190 if (!db_->Open(path_)) { | 223 if (!db_->Open(path_)) { |
| 191 NOTREACHED() << "Unable to open cert DB."; | 224 NOTREACHED() << "Unable to open cert DB."; |
| 192 if (corruption_detected_) | 225 if (corruption_detected_) |
| 193 KillDatabase(); | 226 KillDatabase(); |
| 194 db_.reset(); | 227 db_.reset(); |
| 228 RecordDbLoadStatus(FAILED_TO_OPEN); | |
| 195 return; | 229 return; |
| 196 } | 230 } |
| 197 | 231 |
| 198 if (!EnsureDatabaseVersion()) { | 232 if (!EnsureDatabaseVersion()) { |
| 199 NOTREACHED() << "Unable to open cert DB."; | 233 NOTREACHED() << "Unable to open cert DB."; |
| 200 if (corruption_detected_) | 234 if (corruption_detected_) |
| 201 KillDatabase(); | 235 KillDatabase(); |
| 202 meta_table_.Reset(); | 236 meta_table_.Reset(); |
| 203 db_.reset(); | 237 db_.reset(); |
| 238 RecordDbLoadStatus(MIGRATION_FAILED); | |
| 204 return; | 239 return; |
| 205 } | 240 } |
| 206 | 241 |
| 207 db_->Preload(); | 242 db_->Preload(); |
| 208 | 243 |
| 209 // Slurp all the certs into the out-vector. | 244 // Slurp all the certs into the out-vector. |
| 210 sql::Statement smt(db_->GetUniqueStatement( | 245 sql::Statement smt(db_->GetUniqueStatement( |
| 211 "SELECT host, private_key, creation_time FROM channel_id")); | 246 "SELECT host, private_key, creation_time FROM channel_id")); |
| 212 if (!smt.is_valid()) { | 247 if (!smt.is_valid()) { |
| 213 if (corruption_detected_) | 248 if (corruption_detected_) |
| 214 KillDatabase(); | 249 KillDatabase(); |
| 215 meta_table_.Reset(); | 250 meta_table_.Reset(); |
| 216 db_.reset(); | 251 db_.reset(); |
| 252 RecordDbLoadStatus(INVALID_SELECT_STATEMENT); | |
| 217 return; | 253 return; |
| 218 } | 254 } |
| 219 | 255 |
| 220 while (smt.Step()) { | 256 while (smt.Step()) { |
| 221 std::vector<uint8_t> private_key_from_db; | 257 std::vector<uint8_t> private_key_from_db; |
| 222 smt.ColumnBlobAsVector(1, &private_key_from_db); | 258 smt.ColumnBlobAsVector(1, &private_key_from_db); |
| 223 std::unique_ptr<crypto::ECPrivateKey> key( | 259 std::unique_ptr<crypto::ECPrivateKey> key( |
| 224 crypto::ECPrivateKey::CreateFromPrivateKeyInfo(private_key_from_db)); | 260 crypto::ECPrivateKey::CreateFromPrivateKeyInfo(private_key_from_db)); |
| 225 if (!key) | 261 if (!key) |
|
mattm
2017/04/08 04:17:38
Should this be recorded somehow?
nharper
2017/04/10 19:29:49
Added a new enum value for this.
| |
| 226 continue; | 262 continue; |
| 227 std::unique_ptr<DefaultChannelIDStore::ChannelID> channel_id( | 263 std::unique_ptr<DefaultChannelIDStore::ChannelID> channel_id( |
| 228 new DefaultChannelIDStore::ChannelID( | 264 new DefaultChannelIDStore::ChannelID( |
| 229 smt.ColumnString(0), // host | 265 smt.ColumnString(0), // host |
| 230 base::Time::FromInternalValue(smt.ColumnInt64(2)), std::move(key))); | 266 base::Time::FromInternalValue(smt.ColumnInt64(2)), std::move(key))); |
| 231 channel_ids->push_back(std::move(channel_id)); | 267 channel_ids->push_back(std::move(channel_id)); |
| 232 } | 268 } |
| 233 | 269 |
| 234 UMA_HISTOGRAM_COUNTS_10000( | 270 UMA_HISTOGRAM_COUNTS_10000( |
| 235 "DomainBoundCerts.DBLoadedCount", | 271 "DomainBoundCerts.DBLoadedCount", |
| 236 static_cast<base::HistogramBase::Sample>(channel_ids->size())); | 272 static_cast<base::HistogramBase::Sample>(channel_ids->size())); |
| 237 base::TimeDelta load_time = base::TimeTicks::Now() - start; | 273 base::TimeDelta load_time = base::TimeTicks::Now() - start; |
| 238 UMA_HISTOGRAM_CUSTOM_TIMES("DomainBoundCerts.DBLoadTime", | 274 UMA_HISTOGRAM_CUSTOM_TIMES("DomainBoundCerts.DBLoadTime", |
| 239 load_time, | 275 load_time, |
| 240 base::TimeDelta::FromMilliseconds(1), | 276 base::TimeDelta::FromMilliseconds(1), |
| 241 base::TimeDelta::FromMinutes(1), | 277 base::TimeDelta::FromMinutes(1), |
| 242 50); | 278 50); |
| 243 DVLOG(1) << "loaded " << channel_ids->size() << " in " | 279 DVLOG(1) << "loaded " << channel_ids->size() << " in " |
| 244 << load_time.InMilliseconds() << " ms"; | 280 << load_time.InMilliseconds() << " ms"; |
| 281 if (is_new) { | |
| 282 RecordDbLoadStatus(NEW_DB); | |
| 283 } else { | |
| 284 RecordDbLoadStatus(LOADED); | |
| 285 } | |
| 245 } | 286 } |
| 246 | 287 |
| 247 bool SQLiteChannelIDStore::Backend::EnsureDatabaseVersion() { | 288 bool SQLiteChannelIDStore::Backend::EnsureDatabaseVersion() { |
| 248 // Version check. | 289 // Version check. |
| 249 if (!meta_table_.Init( | 290 if (!meta_table_.Init( |
| 250 db_.get(), kCurrentVersionNumber, kCompatibleVersionNumber)) { | 291 db_.get(), kCurrentVersionNumber, kCompatibleVersionNumber)) { |
| 251 return false; | 292 return false; |
| 252 } | 293 } |
| 253 | 294 |
| 254 if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { | 295 if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { |
| 255 LOG(WARNING) << "Server bound cert database is too new."; | 296 LOG(WARNING) << "Server bound cert database is too new."; |
| 256 return false; | 297 return false; |
| 257 } | 298 } |
| 258 | 299 |
| 259 int cur_version = meta_table_.GetVersionNumber(); | 300 int cur_version = meta_table_.GetVersionNumber(); |
| 301 UMA_HISTOGRAM_EXACT_LINEAR("DomainBoundCerts.DBVersion", cur_version, | |
| 302 kCurrentVersionNumber + 1); | |
| 260 | 303 |
| 261 sql::Transaction transaction(db_.get()); | 304 sql::Transaction transaction(db_.get()); |
| 262 if (!transaction.Begin()) | 305 if (!transaction.Begin()) |
| 263 return false; | 306 return false; |
| 264 | 307 |
| 265 // Create new table if it doesn't already exist | 308 // Create new table if it doesn't already exist |
| 266 if (!db_->DoesTableExist("channel_id")) { | 309 if (!db_->DoesTableExist("channel_id")) { |
| 267 if (!db_->Execute( | 310 if (!db_->Execute( |
| 268 "CREATE TABLE channel_id (" | 311 "CREATE TABLE channel_id (" |
| 269 "host TEXT NOT NULL UNIQUE PRIMARY KEY," | 312 "host TEXT NOT NULL UNIQUE PRIMARY KEY," |
| (...skipping 374 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 644 backend_->SetForceKeepSessionState(); | 687 backend_->SetForceKeepSessionState(); |
| 645 } | 688 } |
| 646 | 689 |
| 647 SQLiteChannelIDStore::~SQLiteChannelIDStore() { | 690 SQLiteChannelIDStore::~SQLiteChannelIDStore() { |
| 648 backend_->Close(); | 691 backend_->Close(); |
| 649 // We release our reference to the Backend, though it will probably still have | 692 // We release our reference to the Backend, though it will probably still have |
| 650 // a reference if the background task runner has not run Close() yet. | 693 // a reference if the background task runner has not run Close() yet. |
| 651 } | 694 } |
| 652 | 695 |
| 653 } // namespace net | 696 } // namespace net |
| OLD | NEW |