| 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 // Database loaded, but one or more keys were skipped. |
| 58 LOADED_WITH_ERRORS = 6, |
| 59 DB_LOAD_STATUS_MAX |
| 60 }; |
| 61 |
| 62 void RecordDbLoadStatus(DbLoadStatus status) { |
| 63 UMA_HISTOGRAM_ENUMERATION("DomainBoundCerts.DBLoadStatus", status, |
| 64 DB_LOAD_STATUS_MAX); |
| 65 } |
| 66 |
| 39 } // namespace | 67 } // namespace |
| 40 | 68 |
| 41 namespace net { | 69 namespace net { |
| 42 | 70 |
| 43 // This class is designed to be shared between any calling threads and the | 71 // 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. | 72 // background task runner. It batches operations and commits them on a timer. |
| 45 class SQLiteChannelIDStore::Backend | 73 class SQLiteChannelIDStore::Backend |
| 46 : public base::RefCountedThreadSafe<SQLiteChannelIDStore::Backend> { | 74 : public base::RefCountedThreadSafe<SQLiteChannelIDStore::Backend> { |
| 47 public: | 75 public: |
| 48 Backend( | 76 Backend( |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 169 DCHECK(background_task_runner_->RunsTasksOnCurrentThread()); | 197 DCHECK(background_task_runner_->RunsTasksOnCurrentThread()); |
| 170 | 198 |
| 171 // This method should be called only once per instance. | 199 // This method should be called only once per instance. |
| 172 DCHECK(!db_.get()); | 200 DCHECK(!db_.get()); |
| 173 | 201 |
| 174 base::TimeTicks start = base::TimeTicks::Now(); | 202 base::TimeTicks start = base::TimeTicks::Now(); |
| 175 | 203 |
| 176 // Ensure the parent directory for storing certs is created before reading | 204 // Ensure the parent directory for storing certs is created before reading |
| 177 // from it. | 205 // from it. |
| 178 const base::FilePath dir = path_.DirName(); | 206 const base::FilePath dir = path_.DirName(); |
| 179 if (!base::PathExists(dir) && !base::CreateDirectory(dir)) | 207 if (!base::PathExists(dir) && !base::CreateDirectory(dir)) { |
| 208 RecordDbLoadStatus(PATH_DOES_NOT_EXIST); |
| 180 return; | 209 return; |
| 210 } |
| 181 | 211 |
| 182 db_.reset(new sql::Connection); | 212 db_.reset(new sql::Connection); |
| 183 db_->set_histogram_tag("DomainBoundCerts"); | 213 db_->set_histogram_tag("DomainBoundCerts"); |
| 184 | 214 |
| 185 // Unretained to avoid a ref loop with db_. | 215 // Unretained to avoid a ref loop with db_. |
| 186 db_->set_error_callback( | 216 db_->set_error_callback( |
| 187 base::Bind(&SQLiteChannelIDStore::Backend::DatabaseErrorCallback, | 217 base::Bind(&SQLiteChannelIDStore::Backend::DatabaseErrorCallback, |
| 188 base::Unretained(this))); | 218 base::Unretained(this))); |
| 189 | 219 |
| 220 DbLoadStatus load_result = LOADED; |
| 221 if (!base::PathExists(path_)) { |
| 222 load_result = NEW_DB; |
| 223 } |
| 224 |
| 190 if (!db_->Open(path_)) { | 225 if (!db_->Open(path_)) { |
| 191 NOTREACHED() << "Unable to open cert DB."; | 226 NOTREACHED() << "Unable to open cert DB."; |
| 192 if (corruption_detected_) | 227 if (corruption_detected_) |
| 193 KillDatabase(); | 228 KillDatabase(); |
| 194 db_.reset(); | 229 db_.reset(); |
| 230 RecordDbLoadStatus(FAILED_TO_OPEN); |
| 195 return; | 231 return; |
| 196 } | 232 } |
| 197 | 233 |
| 198 if (!EnsureDatabaseVersion()) { | 234 if (!EnsureDatabaseVersion()) { |
| 199 NOTREACHED() << "Unable to open cert DB."; | 235 NOTREACHED() << "Unable to open cert DB."; |
| 200 if (corruption_detected_) | 236 if (corruption_detected_) |
| 201 KillDatabase(); | 237 KillDatabase(); |
| 202 meta_table_.Reset(); | 238 meta_table_.Reset(); |
| 203 db_.reset(); | 239 db_.reset(); |
| 240 RecordDbLoadStatus(MIGRATION_FAILED); |
| 204 return; | 241 return; |
| 205 } | 242 } |
| 206 | 243 |
| 207 db_->Preload(); | 244 db_->Preload(); |
| 208 | 245 |
| 209 // Slurp all the certs into the out-vector. | 246 // Slurp all the certs into the out-vector. |
| 210 sql::Statement smt(db_->GetUniqueStatement( | 247 sql::Statement smt(db_->GetUniqueStatement( |
| 211 "SELECT host, private_key, creation_time FROM channel_id")); | 248 "SELECT host, private_key, creation_time FROM channel_id")); |
| 212 if (!smt.is_valid()) { | 249 if (!smt.is_valid()) { |
| 213 if (corruption_detected_) | 250 if (corruption_detected_) |
| 214 KillDatabase(); | 251 KillDatabase(); |
| 215 meta_table_.Reset(); | 252 meta_table_.Reset(); |
| 216 db_.reset(); | 253 db_.reset(); |
| 254 RecordDbLoadStatus(INVALID_SELECT_STATEMENT); |
| 217 return; | 255 return; |
| 218 } | 256 } |
| 219 | 257 |
| 220 while (smt.Step()) { | 258 while (smt.Step()) { |
| 221 std::vector<uint8_t> private_key_from_db; | 259 std::vector<uint8_t> private_key_from_db; |
| 222 smt.ColumnBlobAsVector(1, &private_key_from_db); | 260 smt.ColumnBlobAsVector(1, &private_key_from_db); |
| 223 std::unique_ptr<crypto::ECPrivateKey> key( | 261 std::unique_ptr<crypto::ECPrivateKey> key( |
| 224 crypto::ECPrivateKey::CreateFromPrivateKeyInfo(private_key_from_db)); | 262 crypto::ECPrivateKey::CreateFromPrivateKeyInfo(private_key_from_db)); |
| 225 if (!key) | 263 if (!key) { |
| 264 load_result = LOADED_WITH_ERRORS; |
| 226 continue; | 265 continue; |
| 266 } |
| 227 std::unique_ptr<DefaultChannelIDStore::ChannelID> channel_id( | 267 std::unique_ptr<DefaultChannelIDStore::ChannelID> channel_id( |
| 228 new DefaultChannelIDStore::ChannelID( | 268 new DefaultChannelIDStore::ChannelID( |
| 229 smt.ColumnString(0), // host | 269 smt.ColumnString(0), // host |
| 230 base::Time::FromInternalValue(smt.ColumnInt64(2)), std::move(key))); | 270 base::Time::FromInternalValue(smt.ColumnInt64(2)), std::move(key))); |
| 231 channel_ids->push_back(std::move(channel_id)); | 271 channel_ids->push_back(std::move(channel_id)); |
| 232 } | 272 } |
| 233 | 273 |
| 234 UMA_HISTOGRAM_COUNTS_10000( | 274 UMA_HISTOGRAM_COUNTS_10000( |
| 235 "DomainBoundCerts.DBLoadedCount", | 275 "DomainBoundCerts.DBLoadedCount", |
| 236 static_cast<base::HistogramBase::Sample>(channel_ids->size())); | 276 static_cast<base::HistogramBase::Sample>(channel_ids->size())); |
| 237 base::TimeDelta load_time = base::TimeTicks::Now() - start; | 277 base::TimeDelta load_time = base::TimeTicks::Now() - start; |
| 238 UMA_HISTOGRAM_CUSTOM_TIMES("DomainBoundCerts.DBLoadTime", | 278 UMA_HISTOGRAM_CUSTOM_TIMES("DomainBoundCerts.DBLoadTime", |
| 239 load_time, | 279 load_time, |
| 240 base::TimeDelta::FromMilliseconds(1), | 280 base::TimeDelta::FromMilliseconds(1), |
| 241 base::TimeDelta::FromMinutes(1), | 281 base::TimeDelta::FromMinutes(1), |
| 242 50); | 282 50); |
| 243 DVLOG(1) << "loaded " << channel_ids->size() << " in " | 283 DVLOG(1) << "loaded " << channel_ids->size() << " in " |
| 244 << load_time.InMilliseconds() << " ms"; | 284 << load_time.InMilliseconds() << " ms"; |
| 285 RecordDbLoadStatus(load_result); |
| 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 |