| OLD | NEW |
| 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/net/sqlite_origin_bound_cert_store.h" | 5 #include "chrome/browser/net/sqlite_origin_bound_cert_store.h" |
| 6 | 6 |
| 7 #include <list> | 7 #include <list> |
| 8 | 8 |
| 9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/file_path.h" | 11 #include "base/file_path.h" |
| 12 #include "base/file_util.h" | 12 #include "base/file_util.h" |
| 13 #include "base/logging.h" | 13 #include "base/logging.h" |
| 14 #include "base/memory/scoped_ptr.h" | 14 #include "base/memory/scoped_ptr.h" |
| 15 #include "base/string_util.h" | 15 #include "base/string_util.h" |
| 16 #include "base/threading/thread.h" | 16 #include "base/threading/thread.h" |
| 17 #include "base/threading/thread_restrictions.h" | 17 #include "base/threading/thread_restrictions.h" |
| 18 #include "chrome/browser/diagnostics/sqlite_diagnostics.h" | 18 #include "chrome/browser/diagnostics/sqlite_diagnostics.h" |
| 19 #include "content/public/browser/browser_thread.h" | 19 #include "content/public/browser/browser_thread.h" |
| 20 #include "net/base/ssl_client_cert_type.h" | 20 #include "net/base/ssl_client_cert_type.h" |
| 21 #include "net/base/x509_certificate.h" |
| 21 #include "sql/meta_table.h" | 22 #include "sql/meta_table.h" |
| 22 #include "sql/statement.h" | 23 #include "sql/statement.h" |
| 23 #include "sql/transaction.h" | 24 #include "sql/transaction.h" |
| 24 | 25 |
| 25 using content::BrowserThread; | 26 using content::BrowserThread; |
| 26 | 27 |
| 27 // This class is designed to be shared between any calling threads and the | 28 // This class is designed to be shared between any calling threads and the |
| 28 // database thread. It batches operations and commits them on a timer. | 29 // database thread. It batches operations and commits them on a timer. |
| 29 class SQLiteOriginBoundCertStore::Backend | 30 class SQLiteOriginBoundCertStore::Backend |
| 30 : public base::RefCountedThreadSafe<SQLiteOriginBoundCertStore::Backend> { | 31 : public base::RefCountedThreadSafe<SQLiteOriginBoundCertStore::Backend> { |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 110 PendingOperationsList::size_type num_pending_; | 111 PendingOperationsList::size_type num_pending_; |
| 111 // True if the persistent store should be deleted upon destruction. | 112 // True if the persistent store should be deleted upon destruction. |
| 112 bool clear_local_state_on_exit_; | 113 bool clear_local_state_on_exit_; |
| 113 // Guard |pending_|, |num_pending_| and |clear_local_state_on_exit_|. | 114 // Guard |pending_|, |num_pending_| and |clear_local_state_on_exit_|. |
| 114 base::Lock lock_; | 115 base::Lock lock_; |
| 115 | 116 |
| 116 DISALLOW_COPY_AND_ASSIGN(Backend); | 117 DISALLOW_COPY_AND_ASSIGN(Backend); |
| 117 }; | 118 }; |
| 118 | 119 |
| 119 // Version number of the database. | 120 // Version number of the database. |
| 120 static const int kCurrentVersionNumber = 2; | 121 static const int kCurrentVersionNumber = 3; |
| 121 static const int kCompatibleVersionNumber = 1; | 122 static const int kCompatibleVersionNumber = 1; |
| 122 | 123 |
| 123 namespace { | 124 namespace { |
| 124 | 125 |
| 125 // Initializes the certs table, returning true on success. | 126 // Initializes the certs table, returning true on success. |
| 126 bool InitTable(sql::Connection* db) { | 127 bool InitTable(sql::Connection* db) { |
| 127 if (!db->DoesTableExist("origin_bound_certs")) { | 128 if (!db->DoesTableExist("origin_bound_certs")) { |
| 128 if (!db->Execute("CREATE TABLE origin_bound_certs (" | 129 if (!db->Execute("CREATE TABLE origin_bound_certs (" |
| 129 "origin TEXT NOT NULL UNIQUE PRIMARY KEY," | 130 "origin TEXT NOT NULL UNIQUE PRIMARY KEY," |
| 130 "private_key BLOB NOT NULL," | 131 "private_key BLOB NOT NULL," |
| 131 "cert BLOB NOT NULL," | 132 "cert BLOB NOT NULL," |
| 132 "cert_type INTEGER)")) | 133 "cert_type INTEGER," |
| 134 "expiration_time INTEGER)")) |
| 133 return false; | 135 return false; |
| 134 } | 136 } |
| 135 | 137 |
| 136 return true; | 138 return true; |
| 137 } | 139 } |
| 138 | 140 |
| 139 } // namespace | 141 } // namespace |
| 140 | 142 |
| 141 bool SQLiteOriginBoundCertStore::Backend::Load( | 143 bool SQLiteOriginBoundCertStore::Backend::Load( |
| 142 std::vector<net::DefaultOriginBoundCertStore::OriginBoundCert*>* certs) { | 144 std::vector<net::DefaultOriginBoundCertStore::OriginBoundCert*>* certs) { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 164 if (!EnsureDatabaseVersion() || !InitTable(db_.get())) { | 166 if (!EnsureDatabaseVersion() || !InitTable(db_.get())) { |
| 165 NOTREACHED() << "Unable to open cert DB."; | 167 NOTREACHED() << "Unable to open cert DB."; |
| 166 db_.reset(); | 168 db_.reset(); |
| 167 return false; | 169 return false; |
| 168 } | 170 } |
| 169 | 171 |
| 170 db_->Preload(); | 172 db_->Preload(); |
| 171 | 173 |
| 172 // Slurp all the certs into the out-vector. | 174 // Slurp all the certs into the out-vector. |
| 173 sql::Statement smt(db_->GetUniqueStatement( | 175 sql::Statement smt(db_->GetUniqueStatement( |
| 174 "SELECT origin, private_key, cert, cert_type FROM origin_bound_certs")); | 176 "SELECT origin, private_key, cert, cert_type, expiration_time " |
| 177 "FROM origin_bound_certs")); |
| 175 if (!smt) { | 178 if (!smt) { |
| 176 NOTREACHED() << "select statement prep failed"; | 179 NOTREACHED() << "select statement prep failed"; |
| 177 db_.reset(); | 180 db_.reset(); |
| 178 return false; | 181 return false; |
| 179 } | 182 } |
| 180 | 183 |
| 181 while (smt.Step()) { | 184 while (smt.Step()) { |
| 182 std::string private_key_from_db, cert_from_db; | 185 std::string private_key_from_db, cert_from_db; |
| 183 smt.ColumnBlobAsString(1, &private_key_from_db); | 186 smt.ColumnBlobAsString(1, &private_key_from_db); |
| 184 smt.ColumnBlobAsString(2, &cert_from_db); | 187 smt.ColumnBlobAsString(2, &cert_from_db); |
| 185 scoped_ptr<net::DefaultOriginBoundCertStore::OriginBoundCert> cert( | 188 scoped_ptr<net::DefaultOriginBoundCertStore::OriginBoundCert> cert( |
| 186 new net::DefaultOriginBoundCertStore::OriginBoundCert( | 189 new net::DefaultOriginBoundCertStore::OriginBoundCert( |
| 187 smt.ColumnString(0), // origin | 190 smt.ColumnString(0), // origin |
| 188 static_cast<net::SSLClientCertType>(smt.ColumnInt(3)), | 191 static_cast<net::SSLClientCertType>(smt.ColumnInt(3)), |
| 192 base::Time::FromInternalValue(smt.ColumnInt64(4)), |
| 189 private_key_from_db, | 193 private_key_from_db, |
| 190 cert_from_db)); | 194 cert_from_db)); |
| 191 certs->push_back(cert.release()); | 195 certs->push_back(cert.release()); |
| 192 } | 196 } |
| 193 | 197 |
| 194 return true; | 198 return true; |
| 195 } | 199 } |
| 196 | 200 |
| 197 bool SQLiteOriginBoundCertStore::Backend::EnsureDatabaseVersion() { | 201 bool SQLiteOriginBoundCertStore::Backend::EnsureDatabaseVersion() { |
| 198 // Version check. | 202 // Version check. |
| (...skipping 24 matching lines...) Expand all Loading... |
| 223 << "version 2."; | 227 << "version 2."; |
| 224 return false; | 228 return false; |
| 225 } | 229 } |
| 226 ++cur_version; | 230 ++cur_version; |
| 227 meta_table_.SetVersionNumber(cur_version); | 231 meta_table_.SetVersionNumber(cur_version); |
| 228 meta_table_.SetCompatibleVersionNumber( | 232 meta_table_.SetCompatibleVersionNumber( |
| 229 std::min(cur_version, kCompatibleVersionNumber)); | 233 std::min(cur_version, kCompatibleVersionNumber)); |
| 230 transaction.Commit(); | 234 transaction.Commit(); |
| 231 } | 235 } |
| 232 | 236 |
| 237 if (cur_version == 2) { |
| 238 sql::Transaction transaction(db_.get()); |
| 239 if (!transaction.Begin()) |
| 240 return false; |
| 241 if (!db_->Execute("ALTER TABLE origin_bound_certs ADD COLUMN " |
| 242 "expiration_time INTEGER")) { |
| 243 LOG(WARNING) << "Unable to update origin bound cert database to " |
| 244 << "version 3."; |
| 245 return false; |
| 246 } |
| 247 |
| 248 sql::Statement smt(db_->GetUniqueStatement( |
| 249 "SELECT origin, cert FROM origin_bound_certs")); |
| 250 if (!smt) { |
| 251 LOG(WARNING) << "Unable to update origin bound cert database to " |
| 252 << "version 3."; |
| 253 return false; |
| 254 } |
| 255 sql::Statement update_expires_smt(db_->GetUniqueStatement( |
| 256 "UPDATE origin_bound_certs SET expiration_time = ? WHERE origin = ?")); |
| 257 if (!update_expires_smt) { |
| 258 LOG(WARNING) << "Unable to update origin bound cert database to " |
| 259 << "version 3."; |
| 260 return false; |
| 261 } |
| 262 while (smt.Step()) { |
| 263 std::string origin = smt.ColumnString(0); |
| 264 std::string cert_from_db; |
| 265 smt.ColumnBlobAsString(1, &cert_from_db); |
| 266 // Parse the cert and extract the real value and then update the DB. |
| 267 scoped_refptr<net::X509Certificate> cert( |
| 268 net::X509Certificate::CreateFromBytes( |
| 269 cert_from_db.data(), cert_from_db.size())); |
| 270 if (cert) { |
| 271 update_expires_smt.Reset(); |
| 272 update_expires_smt.BindInt64(0, cert->valid_expiry().ToInternalValue()); |
| 273 update_expires_smt.BindString(1, origin); |
| 274 if (!update_expires_smt.Run()) { |
| 275 LOG(WARNING) << "Unable to update origin bound cert database to " |
| 276 << "version 3."; |
| 277 return false; |
| 278 } |
| 279 } else { |
| 280 // If there's a cert we can't parse, just leave it. It'll get replaced |
| 281 // with a new one if we ever try to use it. |
| 282 LOG(WARNING) << "Error parsing cert for database upgrade for origin " |
| 283 << smt.ColumnString(0); |
| 284 } |
| 285 } |
| 286 |
| 287 ++cur_version; |
| 288 meta_table_.SetVersionNumber(cur_version); |
| 289 meta_table_.SetCompatibleVersionNumber( |
| 290 std::min(cur_version, kCompatibleVersionNumber)); |
| 291 transaction.Commit(); |
| 292 } |
| 293 |
| 233 // Put future migration cases here. | 294 // Put future migration cases here. |
| 234 | 295 |
| 235 // When the version is too old, we just try to continue anyway, there should | 296 // When the version is too old, we just try to continue anyway, there should |
| 236 // not be a released product that makes a database too old for us to handle. | 297 // not be a released product that makes a database too old for us to handle. |
| 237 LOG_IF(WARNING, cur_version < kCurrentVersionNumber) << | 298 LOG_IF(WARNING, cur_version < kCurrentVersionNumber) << |
| 238 "Origin bound cert database version " << cur_version << | 299 "Origin bound cert database version " << cur_version << |
| 239 " is too old to handle."; | 300 " is too old to handle."; |
| 240 | 301 |
| 241 return true; | 302 return true; |
| 242 } | 303 } |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 291 base::AutoLock locked(lock_); | 352 base::AutoLock locked(lock_); |
| 292 pending_.swap(ops); | 353 pending_.swap(ops); |
| 293 num_pending_ = 0; | 354 num_pending_ = 0; |
| 294 } | 355 } |
| 295 | 356 |
| 296 // Maybe an old timer fired or we are already Close()'ed. | 357 // Maybe an old timer fired or we are already Close()'ed. |
| 297 if (!db_.get() || ops.empty()) | 358 if (!db_.get() || ops.empty()) |
| 298 return; | 359 return; |
| 299 | 360 |
| 300 sql::Statement add_smt(db_->GetCachedStatement(SQL_FROM_HERE, | 361 sql::Statement add_smt(db_->GetCachedStatement(SQL_FROM_HERE, |
| 301 "INSERT INTO origin_bound_certs (origin, private_key, cert, cert_type) " | 362 "INSERT INTO origin_bound_certs (origin, private_key, cert, cert_type, " |
| 302 "VALUES (?,?,?,?)")); | 363 "expiration_time) VALUES (?,?,?,?,?)")); |
| 303 if (!add_smt) { | 364 if (!add_smt) { |
| 304 NOTREACHED(); | 365 NOTREACHED(); |
| 305 return; | 366 return; |
| 306 } | 367 } |
| 307 | 368 |
| 308 sql::Statement del_smt(db_->GetCachedStatement(SQL_FROM_HERE, | 369 sql::Statement del_smt(db_->GetCachedStatement(SQL_FROM_HERE, |
| 309 "DELETE FROM origin_bound_certs WHERE origin=?")); | 370 "DELETE FROM origin_bound_certs WHERE origin=?")); |
| 310 if (!del_smt) { | 371 if (!del_smt) { |
| 311 NOTREACHED(); | 372 NOTREACHED(); |
| 312 return; | 373 return; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 323 scoped_ptr<PendingOperation> po(*it); | 384 scoped_ptr<PendingOperation> po(*it); |
| 324 switch (po->op()) { | 385 switch (po->op()) { |
| 325 case PendingOperation::CERT_ADD: { | 386 case PendingOperation::CERT_ADD: { |
| 326 add_smt.Reset(); | 387 add_smt.Reset(); |
| 327 add_smt.BindString(0, po->cert().origin()); | 388 add_smt.BindString(0, po->cert().origin()); |
| 328 const std::string& private_key = po->cert().private_key(); | 389 const std::string& private_key = po->cert().private_key(); |
| 329 add_smt.BindBlob(1, private_key.data(), private_key.size()); | 390 add_smt.BindBlob(1, private_key.data(), private_key.size()); |
| 330 const std::string& cert = po->cert().cert(); | 391 const std::string& cert = po->cert().cert(); |
| 331 add_smt.BindBlob(2, cert.data(), cert.size()); | 392 add_smt.BindBlob(2, cert.data(), cert.size()); |
| 332 add_smt.BindInt(3, po->cert().type()); | 393 add_smt.BindInt(3, po->cert().type()); |
| 394 add_smt.BindInt64(4, po->cert().expiration_time().ToInternalValue()); |
| 333 if (!add_smt.Run()) | 395 if (!add_smt.Run()) |
| 334 NOTREACHED() << "Could not add an origin bound cert to the DB."; | 396 NOTREACHED() << "Could not add an origin bound cert to the DB."; |
| 335 break; | 397 break; |
| 336 } | 398 } |
| 337 case PendingOperation::CERT_DELETE: | 399 case PendingOperation::CERT_DELETE: |
| 338 del_smt.Reset(); | 400 del_smt.Reset(); |
| 339 del_smt.BindString(0, po->cert().origin()); | 401 del_smt.BindString(0, po->cert().origin()); |
| 340 if (!del_smt.Run()) | 402 if (!del_smt.Run()) |
| 341 NOTREACHED() << "Could not delete an origin bound cert from the DB."; | 403 NOTREACHED() << "Could not delete an origin bound cert from the DB."; |
| 342 break; | 404 break; |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 425 if (backend_.get()) | 487 if (backend_.get()) |
| 426 backend_->SetClearLocalStateOnExit(clear_local_state); | 488 backend_->SetClearLocalStateOnExit(clear_local_state); |
| 427 } | 489 } |
| 428 | 490 |
| 429 void SQLiteOriginBoundCertStore::Flush(const base::Closure& completion_task) { | 491 void SQLiteOriginBoundCertStore::Flush(const base::Closure& completion_task) { |
| 430 if (backend_.get()) | 492 if (backend_.get()) |
| 431 backend_->Flush(completion_task); | 493 backend_->Flush(completion_task); |
| 432 else if (!completion_task.is_null()) | 494 else if (!completion_task.is_null()) |
| 433 MessageLoop::current()->PostTask(FROM_HERE, completion_task); | 495 MessageLoop::current()->PostTask(FROM_HERE, completion_task); |
| 434 } | 496 } |
| OLD | NEW |