diff options
Diffstat (limited to 'content')
6 files changed, 270 insertions, 63 deletions
diff --git a/content/browser/net/sqlite_persistent_cookie_store.cc b/content/browser/net/sqlite_persistent_cookie_store.cc index 1c0a8e7..dd77f85 100644 --- a/content/browser/net/sqlite_persistent_cookie_store.cc +++ b/content/browser/net/sqlite_persistent_cookie_store.cc @@ -26,6 +26,7 @@ #include "base/threading/sequenced_worker_pool.h" #include "base/time/time.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/cookie_crypto_delegate.h" #include "content/public/browser/cookie_store_factory.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "net/cookies/canonical_cookie.h" @@ -73,7 +74,8 @@ class SQLitePersistentCookieStore::Backend const scoped_refptr<base::SequencedTaskRunner>& client_task_runner, const scoped_refptr<base::SequencedTaskRunner>& background_task_runner, bool restore_old_session_cookies, - quota::SpecialStoragePolicy* special_storage_policy) + quota::SpecialStoragePolicy* special_storage_policy, + scoped_ptr<CookieCryptoDelegate> crypto_delegate) : path_(path), num_pending_(0), force_keep_session_state_(false), @@ -85,7 +87,8 @@ class SQLitePersistentCookieStore::Backend client_task_runner_(client_task_runner), background_task_runner_(background_task_runner), num_priority_waiting_(0), - total_priority_requests_(0) {} + total_priority_requests_(0), + crypto_(crypto_delegate.Pass()) {} // Creates or loads the SQLite database. void Load(const LoadedCallback& loaded_callback); @@ -268,6 +271,9 @@ class SQLitePersistentCookieStore::Backend // The cumulative duration of time when |num_priority_waiting_| was greater // than 1. base::TimeDelta priority_wait_duration_; + // Class with functions that do cryptographic operations (for protecting + // cookies stored persistently). + scoped_ptr<CookieCryptoDelegate> crypto_; DISALLOW_COPY_AND_ASSIGN(Backend); }; @@ -276,6 +282,13 @@ namespace { // Version number of the database. // +// Version 7 adds encrypted values. Old values will continue to be used but +// all new values written will be encrypted on selected operating systems. New +// records read by old clients will simply get an empty cookie value while old +// records read by new clients will continue to operate with the unencrypted +// version. New and old clients alike will always write/update records with +// what they support. +// // Version 6 adds cookie priorities. This allows developers to influence the // order in which cookies are evicted in order to meet domain cookie limits. // @@ -292,7 +305,7 @@ namespace { // Version 3 updated the database to include the last access time, so we can // expire them in decreasing order of use when we've reached the maximum // number of cookies. -const int kCurrentVersionNumber = 6; +const int kCurrentVersionNumber = 7; const int kCompatibleVersionNumber = 5; // Possible values for the 'priority' column. @@ -369,7 +382,8 @@ bool InitTable(sql::Connection* db) { "last_access_utc INTEGER NOT NULL, " "has_expires INTEGER NOT NULL DEFAULT 1, " "persistent INTEGER NOT NULL DEFAULT 1," - "priority INTEGER NOT NULL DEFAULT %d)", + "priority INTEGER NOT NULL DEFAULT %d," + "encrypted_value BLOB DEFAULT '')", CookiePriorityToDBCookiePriority(net::COOKIE_PRIORITY_DEFAULT))); if (!db->Execute(stmt.c_str())) return false; @@ -678,15 +692,16 @@ bool SQLitePersistentCookieStore::Backend::LoadCookiesForDomains( if (restore_old_session_cookies_) { smt.Assign(db_->GetCachedStatement( SQL_FROM_HERE, - "SELECT creation_utc, host_key, name, value, path, expires_utc, " - "secure, httponly, last_access_utc, has_expires, persistent, priority " - "FROM cookies WHERE host_key = ?")); + "SELECT creation_utc, host_key, name, value, encrypted_value, path, " + "expires_utc, secure, httponly, last_access_utc, has_expires, " + "persistent, priority FROM cookies WHERE host_key = ?")); } else { smt.Assign(db_->GetCachedStatement( SQL_FROM_HERE, - "SELECT creation_utc, host_key, name, value, path, expires_utc, " - "secure, httponly, last_access_utc, has_expires, persistent, priority " - "FROM cookies WHERE host_key = ? AND persistent = 1")); + "SELECT creation_utc, host_key, name, value, encrypted_value, path, " + "expires_utc, secure, httponly, last_access_utc, has_expires, " + "persistent, priority FROM cookies WHERE host_key = ? " + "AND persistent = 1")); } if (!smt.is_valid()) { smt.Clear(); // Disconnect smt_ref from db_. @@ -700,20 +715,28 @@ bool SQLitePersistentCookieStore::Backend::LoadCookiesForDomains( for (; it != domains.end(); ++it) { smt.BindString(0, *it); while (smt.Step()) { + std::string value; + std::string encrypted_value = smt.ColumnString(4); + if (!encrypted_value.empty() && crypto_.get()) { + crypto_->DecryptString(encrypted_value, &value); + } else { + DCHECK(encrypted_value.empty()); + value = smt.ColumnString(3); + } scoped_ptr<net::CanonicalCookie> cc(new net::CanonicalCookie( // The "source" URL is not used with persisted cookies. GURL(), // Source smt.ColumnString(2), // name - smt.ColumnString(3), // value + value, // value smt.ColumnString(1), // domain - smt.ColumnString(4), // path + smt.ColumnString(5), // path Time::FromInternalValue(smt.ColumnInt64(0)), // creation_utc - Time::FromInternalValue(smt.ColumnInt64(5)), // expires_utc - Time::FromInternalValue(smt.ColumnInt64(8)), // last_access_utc - smt.ColumnInt(6) != 0, // secure - smt.ColumnInt(7) != 0, // httponly + Time::FromInternalValue(smt.ColumnInt64(6)), // expires_utc + Time::FromInternalValue(smt.ColumnInt64(9)), // last_access_utc + smt.ColumnInt(7) != 0, // secure + smt.ColumnInt(8) != 0, // httponly DBCookiePriorityToCookiePriority( - static_cast<DBCookiePriority>(smt.ColumnInt(11))))); // priority + static_cast<DBCookiePriority>(smt.ColumnInt(12))))); // priority DLOG_IF(WARNING, cc->CreationDate() > Time::Now()) << L"CreationDate too recent"; cookies_per_origin_[CookieOrigin(cc->Domain(), cc->IsSecure())]++; @@ -836,6 +859,26 @@ bool SQLitePersistentCookieStore::Backend::EnsureDatabaseVersion() { base::TimeTicks::Now() - start_time); } + if (cur_version == 6) { + const base::TimeTicks start_time = base::TimeTicks::Now(); + sql::Transaction transaction(db_.get()); + if (!transaction.Begin()) + return false; + // Alter the table to add empty "encrypted value" column. + if (!db_->Execute("ALTER TABLE cookies " + "ADD COLUMN encrypted_value BLOB DEFAULT ''")) { + LOG(WARNING) << "Unable to update cookie database to version 7."; + return false; + } + ++cur_version; + meta_table_.SetVersionNumber(cur_version); + meta_table_.SetCompatibleVersionNumber( + std::min(cur_version, kCompatibleVersionNumber)); + transaction.Commit(); + UMA_HISTOGRAM_TIMES("Cookie.TimeDatabaseMigrationToV7", + base::TimeTicks::Now() - start_time); + } + // Put future migration cases here. if (cur_version < kCurrentVersionNumber) { @@ -920,10 +963,10 @@ void SQLitePersistentCookieStore::Backend::Commit() { return; sql::Statement add_smt(db_->GetCachedStatement(SQL_FROM_HERE, - "INSERT INTO cookies (creation_utc, host_key, name, value, path, " - "expires_utc, secure, httponly, last_access_utc, has_expires, " - "persistent, priority) " - "VALUES (?,?,?,?,?,?,?,?,?,?,?,?)")); + "INSERT INTO cookies (creation_utc, host_key, name, value, " + "encrypted_value, path, expires_utc, secure, httponly, last_access_utc, " + "has_expires, persistent, priority) " + "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)")); if (!add_smt.is_valid()) return; @@ -953,16 +996,26 @@ void SQLitePersistentCookieStore::Backend::Commit() { add_smt.BindInt64(0, po->cc().CreationDate().ToInternalValue()); add_smt.BindString(1, po->cc().Domain()); add_smt.BindString(2, po->cc().Name()); - add_smt.BindString(3, po->cc().Value()); - add_smt.BindString(4, po->cc().Path()); - add_smt.BindInt64(5, po->cc().ExpiryDate().ToInternalValue()); - add_smt.BindInt(6, po->cc().IsSecure()); - add_smt.BindInt(7, po->cc().IsHttpOnly()); - add_smt.BindInt64(8, po->cc().LastAccessDate().ToInternalValue()); - add_smt.BindInt(9, po->cc().IsPersistent()); + if (crypto_.get()) { + std::string encrypted_value; + add_smt.BindCString(3, ""); // value + crypto_->EncryptString(po->cc().Value(), &encrypted_value); + // BindBlob() immediately makes an internal copy of the data. + add_smt.BindBlob(4, encrypted_value.data(), + static_cast<int>(encrypted_value.length())); + } else { + add_smt.BindString(3, po->cc().Value()); + add_smt.BindBlob(4, "", 0); // encrypted_value + } + add_smt.BindString(5, po->cc().Path()); + add_smt.BindInt64(6, po->cc().ExpiryDate().ToInternalValue()); + add_smt.BindInt(7, po->cc().IsSecure()); + add_smt.BindInt(8, po->cc().IsHttpOnly()); + add_smt.BindInt64(9, po->cc().LastAccessDate().ToInternalValue()); add_smt.BindInt(10, po->cc().IsPersistent()); + add_smt.BindInt(11, po->cc().IsPersistent()); add_smt.BindInt( - 11, CookiePriorityToDBCookiePriority(po->cc().Priority())); + 12, CookiePriorityToDBCookiePriority(po->cc().Priority())); if (!add_smt.Run()) NOTREACHED() << "Could not add a cookie to the DB."; break; @@ -1148,12 +1201,14 @@ SQLitePersistentCookieStore::SQLitePersistentCookieStore( const scoped_refptr<base::SequencedTaskRunner>& client_task_runner, const scoped_refptr<base::SequencedTaskRunner>& background_task_runner, bool restore_old_session_cookies, - quota::SpecialStoragePolicy* special_storage_policy) + quota::SpecialStoragePolicy* special_storage_policy, + scoped_ptr<CookieCryptoDelegate> crypto_delegate) : backend_(new Backend(path, client_task_runner, background_task_runner, restore_old_session_cookies, - special_storage_policy)) { + special_storage_policy, + crypto_delegate.Pass())) { } void SQLitePersistentCookieStore::Load(const LoadedCallback& loaded_callback) { @@ -1199,14 +1254,16 @@ net::CookieStore* CreatePersistentCookieStore( quota::SpecialStoragePolicy* storage_policy, net::CookieMonster::Delegate* cookie_monster_delegate, const scoped_refptr<base::SequencedTaskRunner>& client_task_runner, - const scoped_refptr<base::SequencedTaskRunner>& background_task_runner) { + const scoped_refptr<base::SequencedTaskRunner>& background_task_runner, + scoped_ptr<CookieCryptoDelegate> crypto_delegate) { SQLitePersistentCookieStore* persistent_store = new SQLitePersistentCookieStore( path, client_task_runner, background_task_runner, restore_old_session_cookies, - storage_policy); + storage_policy, + crypto_delegate.Pass()); return new net::CookieMonster(persistent_store, cookie_monster_delegate); } @@ -1214,7 +1271,8 @@ net::CookieStore* CreatePersistentCookieStore( const base::FilePath& path, bool restore_old_session_cookies, quota::SpecialStoragePolicy* storage_policy, - net::CookieMonster::Delegate* cookie_monster_delegate) { + net::CookieMonster::Delegate* cookie_monster_delegate, + scoped_ptr<CookieCryptoDelegate> crypto_delegate) { return CreatePersistentCookieStore( path, restore_old_session_cookies, @@ -1222,7 +1280,8 @@ net::CookieStore* CreatePersistentCookieStore( cookie_monster_delegate, BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO), BrowserThread::GetBlockingPool()->GetSequencedTaskRunner( - BrowserThread::GetBlockingPool()->GetSequenceToken())); + BrowserThread::GetBlockingPool()->GetSequenceToken()), + crypto_delegate.Pass()); } } // namespace content diff --git a/content/browser/net/sqlite_persistent_cookie_store.h b/content/browser/net/sqlite_persistent_cookie_store.h index 65e3982..e94327e 100644 --- a/content/browser/net/sqlite_persistent_cookie_store.h +++ b/content/browser/net/sqlite_persistent_cookie_store.h @@ -32,6 +32,7 @@ class SpecialStoragePolicy; } namespace content { +class CookieCryptoDelegate; // Implements the PersistentCookieStore interface in terms of a SQLite database. // For documentation about the actual member functions consult the documentation @@ -49,7 +50,8 @@ class CONTENT_EXPORT SQLitePersistentCookieStore const scoped_refptr<base::SequencedTaskRunner>& client_task_runner, const scoped_refptr<base::SequencedTaskRunner>& background_task_runner, bool restore_old_session_cookies, - quota::SpecialStoragePolicy* special_storage_policy); + quota::SpecialStoragePolicy* special_storage_policy, + scoped_ptr<CookieCryptoDelegate> crypto_delegate); // net::CookieMonster::PersistentCookieStore: virtual void Load(const LoadedCallback& loaded_callback) OVERRIDE; diff --git a/content/browser/net/sqlite_persistent_cookie_store_perftest.cc b/content/browser/net/sqlite_persistent_cookie_store_perftest.cc index e71b56b..980a0c1 100644 --- a/content/browser/net/sqlite_persistent_cookie_store_perftest.cc +++ b/content/browser/net/sqlite_persistent_cookie_store_perftest.cc @@ -13,6 +13,7 @@ #include "base/test/perf_time_logger.h" #include "base/test/sequenced_worker_pool_owner.h" #include "base/threading/sequenced_worker_pool.h" +#include "content/public/browser/cookie_crypto_delegate.h" #include "net/cookies/canonical_cookie.h" #include "net/cookies/cookie_constants.h" #include "testing/gtest/include/gtest/gtest.h" @@ -66,7 +67,8 @@ class SQLitePersistentCookieStorePerfTest : public testing::Test { temp_dir_.path().Append(cookie_filename), client_task_runner(), background_task_runner(), - false, NULL); + false, NULL, + scoped_ptr<content::CookieCryptoDelegate>()); std::vector<net::CanonicalCookie*> cookies; Load(); ASSERT_EQ(0u, cookies_.size()); @@ -97,7 +99,8 @@ class SQLitePersistentCookieStorePerfTest : public testing::Test { temp_dir_.path().Append(cookie_filename), client_task_runner(), background_task_runner(), - false, NULL); + false, NULL, + scoped_ptr<content::CookieCryptoDelegate>()); } virtual void TearDown() OVERRIDE { diff --git a/content/browser/net/sqlite_persistent_cookie_store_unittest.cc b/content/browser/net/sqlite_persistent_cookie_store_unittest.cc index 4adb790..3869896 100644 --- a/content/browser/net/sqlite_persistent_cookie_store_unittest.cc +++ b/content/browser/net/sqlite_persistent_cookie_store_unittest.cc @@ -19,10 +19,15 @@ #include "base/test/sequenced_worker_pool_owner.h" #include "base/threading/sequenced_worker_pool.h" #include "base/time/time.h" +#include "content/public/browser/cookie_crypto_delegate.h" +#include "content/public/browser/cookie_store_factory.h" +#include "crypto/encryptor.h" +#include "crypto/symmetric_key.h" #include "net/cookies/canonical_cookie.h" #include "net/cookies/cookie_constants.h" #include "sql/connection.h" #include "sql/meta_table.h" +#include "sql/statement.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" @@ -32,6 +37,36 @@ namespace { const base::FilePath::CharType kCookieFilename[] = FILE_PATH_LITERAL("Cookies"); +class CookieCryptor : public content::CookieCryptoDelegate { + public: + CookieCryptor(); + virtual bool EncryptString(const std::string& plaintext, + std::string* ciphertext) OVERRIDE; + virtual bool DecryptString(const std::string& ciphertext, + std::string* plaintext) OVERRIDE; + + private: + scoped_ptr<crypto::SymmetricKey> key_; + crypto::Encryptor encryptor_; +}; + +CookieCryptor::CookieCryptor() : key_( + crypto::SymmetricKey::DeriveKeyFromPassword( + crypto::SymmetricKey::AES, "password", "saltiest", 1000, 256)) { + std::string iv("the iv: 16 bytes"); + encryptor_.Init(key_.get(), crypto::Encryptor::CBC, iv); +} + +bool CookieCryptor::EncryptString(const std::string& plaintext, + std::string* ciphertext) { + return encryptor_.Encrypt(plaintext, ciphertext); +} + +bool CookieCryptor::DecryptString(const std::string& ciphertext, + std::string* plaintext) { + return encryptor_.Decrypt(ciphertext, plaintext); +} + } // namespace typedef std::vector<net::CanonicalCookie*> CanonicalCookieVector; @@ -90,20 +125,24 @@ class SQLitePersistentCookieStoreTest : public testing::Test { pool_owner_.reset(new base::SequencedWorkerPoolOwner(3, "Background Pool")); } - void CreateAndLoad(bool restore_old_session_cookies, + void CreateAndLoad(bool crypt_cookies, + bool restore_old_session_cookies, CanonicalCookieVector* cookies) { store_ = new SQLitePersistentCookieStore( temp_dir_.path().Append(kCookieFilename), client_task_runner(), background_task_runner(), restore_old_session_cookies, - NULL); + NULL, + crypt_cookies ? + scoped_ptr<content::CookieCryptoDelegate>(new CookieCryptor) : + scoped_ptr<content::CookieCryptoDelegate>()); Load(cookies); } - void InitializeStore(bool restore_old_session_cookies) { + void InitializeStore(bool crypt, bool restore_old_session_cookies) { CanonicalCookieVector cookies; - CreateAndLoad(restore_old_session_cookies, &cookies); + CreateAndLoad(crypt, restore_old_session_cookies, &cookies); EXPECT_EQ(0U, cookies.size()); } @@ -125,6 +164,14 @@ class SQLitePersistentCookieStoreTest : public testing::Test { net::COOKIE_PRIORITY_DEFAULT)); } + std::string ReadRawDBContents() { + std::string contents; + if (!base::ReadFileToString(temp_dir_.path().Append(kCookieFilename), + &contents)) + return std::string(); + return contents; + } + virtual void SetUp() OVERRIDE { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); } @@ -146,13 +193,13 @@ class SQLitePersistentCookieStoreTest : public testing::Test { }; TEST_F(SQLitePersistentCookieStoreTest, TestInvalidMetaTableRecovery) { - InitializeStore(false); + InitializeStore(false, false); AddCookie("A", "B", "foo.bar", "/", base::Time::Now()); DestroyStore(); // Load up the store and verify that it has good data in it. CanonicalCookieVector cookies; - CreateAndLoad(false, &cookies); + CreateAndLoad(false, false, &cookies); ASSERT_EQ(1U, cookies.size()); ASSERT_STREQ("foo.bar", cookies[0]->Domain().c_str()); ASSERT_STREQ("A", cookies[0]->Name().c_str()); @@ -171,13 +218,13 @@ TEST_F(SQLitePersistentCookieStoreTest, TestInvalidMetaTableRecovery) { } // Upon loading, the database should be reset to a good, blank state. - CreateAndLoad(false, &cookies); + CreateAndLoad(false, false, &cookies); ASSERT_EQ(0U, cookies.size()); // Verify that, after, recovery, the database persists properly. AddCookie("X", "Y", "foo.bar", "/", base::Time::Now()); DestroyStore(); - CreateAndLoad(false, &cookies); + CreateAndLoad(false, false, &cookies); ASSERT_EQ(1U, cookies.size()); ASSERT_STREQ("foo.bar", cookies[0]->Domain().c_str()); ASSERT_STREQ("X", cookies[0]->Name().c_str()); @@ -187,7 +234,7 @@ TEST_F(SQLitePersistentCookieStoreTest, TestInvalidMetaTableRecovery) { // Test if data is stored as expected in the SQLite database. TEST_F(SQLitePersistentCookieStoreTest, TestPersistance) { - InitializeStore(false); + InitializeStore(false, false); AddCookie("A", "B", "foo.bar", "/", base::Time::Now()); // Replace the store effectively destroying the current one and forcing it // to write its data to disk. Then we can see if after loading it again it @@ -195,7 +242,7 @@ TEST_F(SQLitePersistentCookieStoreTest, TestPersistance) { DestroyStore(); // Reload and test for persistence CanonicalCookieVector cookies; - CreateAndLoad(false, &cookies); + CreateAndLoad(false, false, &cookies); ASSERT_EQ(1U, cookies.size()); ASSERT_STREQ("foo.bar", cookies[0]->Domain().c_str()); ASSERT_STREQ("A", cookies[0]->Name().c_str()); @@ -207,14 +254,14 @@ TEST_F(SQLitePersistentCookieStoreTest, TestPersistance) { STLDeleteElements(&cookies); // Reload and check if the cookie has been removed. - CreateAndLoad(false, &cookies); + CreateAndLoad(false, false, &cookies); ASSERT_EQ(0U, cookies.size()); } // Test that priority load of cookies for a specfic domain key could be // completed before the entire store is loaded TEST_F(SQLitePersistentCookieStoreTest, TestLoadCookiesForKey) { - InitializeStore(false); + InitializeStore(false, false); base::Time t = base::Time::Now(); AddCookie("A", "B", "foo.bar", "/", t); t += base::TimeDelta::FromInternalValue(10); @@ -229,7 +276,9 @@ TEST_F(SQLitePersistentCookieStoreTest, TestLoadCookiesForKey) { temp_dir_.path().Append(kCookieFilename), client_task_runner(), background_task_runner(), - false, NULL); + false, NULL, + scoped_ptr<content::CookieCryptoDelegate>()); + // Posting a blocking task to db_thread_ makes sure that the DB thread waits // until both Load and LoadCookiesForKey have been posted to its task queue. background_task_runner()->PostTask( @@ -284,7 +333,7 @@ TEST_F(SQLitePersistentCookieStoreTest, TestLoadCookiesForKey) { // Test that we can force the database to be written by calling Flush(). TEST_F(SQLitePersistentCookieStoreTest, TestFlush) { - InitializeStore(false); + InitializeStore(false, false); // File timestamps don't work well on all platforms, so we'll determine // whether the DB file has been modified by checking its size. base::FilePath path = temp_dir_.path().Append(kCookieFilename); @@ -310,7 +359,7 @@ TEST_F(SQLitePersistentCookieStoreTest, TestFlush) { // Test loading old session cookies from the disk. TEST_F(SQLitePersistentCookieStoreTest, TestLoadOldSessionCookies) { - InitializeStore(true); + InitializeStore(false, true); // Add a session cookie. store_->AddCookie( @@ -325,7 +374,7 @@ TEST_F(SQLitePersistentCookieStoreTest, TestLoadOldSessionCookies) { // Create a store that loads session cookies and test that the session cookie // was loaded. CanonicalCookieVector cookies; - CreateAndLoad(true, &cookies); + CreateAndLoad(false, true, &cookies); ASSERT_EQ(1U, cookies.size()); ASSERT_STREQ("sessioncookie.com", cookies[0]->Domain().c_str()); @@ -338,7 +387,7 @@ TEST_F(SQLitePersistentCookieStoreTest, TestLoadOldSessionCookies) { // Test loading old session cookies from the disk. TEST_F(SQLitePersistentCookieStoreTest, TestDontLoadOldSessionCookies) { - InitializeStore(true); + InitializeStore(false, true); // Add a session cookie. store_->AddCookie( @@ -353,7 +402,7 @@ TEST_F(SQLitePersistentCookieStoreTest, TestDontLoadOldSessionCookies) { // Create a store that doesn't load old session cookies and test that the // session cookie was not loaded. CanonicalCookieVector cookies; - CreateAndLoad(false, &cookies); + CreateAndLoad(false, false, &cookies); ASSERT_EQ(0U, cookies.size()); // The store should also delete the session cookie. Wait until that has been @@ -362,12 +411,12 @@ TEST_F(SQLitePersistentCookieStoreTest, TestDontLoadOldSessionCookies) { // Create a store that loads old session cookies and test that the session // cookie is gone. - CreateAndLoad(true, &cookies); + CreateAndLoad(false, true, &cookies); ASSERT_EQ(0U, cookies.size()); } TEST_F(SQLitePersistentCookieStoreTest, PersistIsPersistent) { - InitializeStore(true); + InitializeStore(false, true); static const char kSessionName[] = "session"; static const char kPersistentName[] = "persistent"; @@ -392,7 +441,7 @@ TEST_F(SQLitePersistentCookieStoreTest, PersistIsPersistent) { // Create a store that loads session cookie and test that the IsPersistent // attribute is restored. CanonicalCookieVector cookies; - CreateAndLoad(true, &cookies); + CreateAndLoad(false, true, &cookies); ASSERT_EQ(2U, cookies.size()); std::map<std::string, net::CanonicalCookie*> cookie_map; @@ -422,7 +471,7 @@ TEST_F(SQLitePersistentCookieStoreTest, PriorityIsPersistent) { static const char kCookieValue[] = "value"; static const char kCookiePath[] = "/"; - InitializeStore(true); + InitializeStore(false, true); // Add a low-priority persistent cookie. store_->AddCookie( @@ -457,7 +506,7 @@ TEST_F(SQLitePersistentCookieStoreTest, PriorityIsPersistent) { // Create a store that loads session cookie and test that the priority // attribute values are restored. CanonicalCookieVector cookies; - CreateAndLoad(true, &cookies); + CreateAndLoad(false, true, &cookies); ASSERT_EQ(3U, cookies.size()); // Put the cookies into a map, by name, so we can easily find them. @@ -485,4 +534,73 @@ TEST_F(SQLitePersistentCookieStoreTest, PriorityIsPersistent) { STLDeleteElements(&cookies); } +TEST_F(SQLitePersistentCookieStoreTest, UpdateToEncryption) { + CanonicalCookieVector cookies; + + // Create unencrypted cookie store and write something to it. + InitializeStore(false, false); + AddCookie("name", "value123XYZ", "foo.bar", "/", base::Time::Now()); + DestroyStore(); + + // Verify that "value" is visible in the file. This is necessary in order to + // have confidence in a later test that "encrypted_value" is not visible. + std::string contents = ReadRawDBContents(); + EXPECT_NE(0U, contents.length()); + EXPECT_NE(contents.find("value123XYZ"), std::string::npos); + + // Create encrypted cookie store and ensure old cookie still reads. + STLDeleteElements(&cookies_); + EXPECT_EQ(0U, cookies_.size()); + CreateAndLoad(true, false, &cookies); + EXPECT_EQ(1U, cookies_.size()); + EXPECT_EQ("name", cookies_[0]->Name()); + EXPECT_EQ("value123XYZ", cookies_[0]->Value()); + + // Make sure we can update existing cookie and add new cookie as encrypted. + store_->DeleteCookie(*(cookies_[0])); + AddCookie("name", "encrypted_value123XYZ", "foo.bar", "/", base::Time::Now()); + AddCookie("other", "something456ABC", "foo.bar", "/", + base::Time::Now() + base::TimeDelta::FromInternalValue(10)); + DestroyStore(); + STLDeleteElements(&cookies_); + CreateAndLoad(true, false, &cookies); + EXPECT_EQ(2U, cookies_.size()); + net::CanonicalCookie* cookie_name = NULL; + net::CanonicalCookie* cookie_other = NULL; + if (cookies_[0]->Name() == "name") { + cookie_name = cookies_[0]; + cookie_other = cookies_[1]; + } else { + cookie_name = cookies_[1]; + cookie_other = cookies_[0]; + } + EXPECT_EQ("encrypted_value123XYZ", cookie_name->Value()); + EXPECT_EQ("something456ABC", cookie_other->Value()); + DestroyStore(); + + // Examine the real record to make sure plaintext version doesn't exist. + sql::Connection db; + sql::Statement smt; + int resultcount = 0; + ASSERT_TRUE(db.Open(temp_dir_.path().Append(kCookieFilename))); + smt.Assign(db.GetCachedStatement(SQL_FROM_HERE, + "SELECT * " + "FROM cookies " + "WHERE host_key = 'foo.bar'")); + while (smt.Step()) { + resultcount++; + for (int i=0; i < smt.ColumnCount(); i++) { + EXPECT_EQ(smt.ColumnString(i).find("value"), std::string::npos); + EXPECT_EQ(smt.ColumnString(i).find("something"), std::string::npos); + } + } + EXPECT_EQ(2, resultcount); + + // Verify that "encrypted_value" is NOT visible in the file. + contents = ReadRawDBContents(); + EXPECT_NE(0U, contents.length()); + EXPECT_EQ(contents.find("encrypted_value123XYZ"), std::string::npos); + EXPECT_EQ(contents.find("something456ABC"), std::string::npos); +} + } // namespace content diff --git a/content/public/browser/cookie_crypto_delegate.h b/content/public/browser/cookie_crypto_delegate.h new file mode 100644 index 0000000..92a9a6a --- /dev/null +++ b/content/public/browser/cookie_crypto_delegate.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_PUBLIC_BROWSER_COOKIE_CRYPTO_DELEGATE_H_ +#define CONTENT_PUBLIC_BROWSER_COOKIE_CRYPTO_DELEGATE_H_ + +namespace content { + +// Implements encryption and decryption for the persistent cookie store. +class CookieCryptoDelegate { + public: + virtual ~CookieCryptoDelegate() {} + virtual bool EncryptString(const std::string& plaintext, + std::string* ciphertext) = 0; + virtual bool DecryptString(const std::string& ciphertext, + std::string* plaintext) = 0; +}; + +} // namespace content + +#endif // CONTENT_PUBLIC_BROWSER_COOKIE_CRYPTO_DELEGATE_H_ diff --git a/content/public/browser/cookie_store_factory.h b/content/public/browser/cookie_store_factory.h index c70d09e..300178e 100644 --- a/content/public/browser/cookie_store_factory.h +++ b/content/public/browser/cookie_store_factory.h @@ -17,6 +17,7 @@ class SpecialStoragePolicy; } namespace content { +class CookieCryptoDelegate; // All blocking database accesses will be performed on |background_task_runner|. // Callbacks for data load events will be performed on |client_task_runner|. @@ -26,14 +27,16 @@ CONTENT_EXPORT net::CookieStore* CreatePersistentCookieStore( quota::SpecialStoragePolicy* storage_policy, net::CookieMonster::Delegate* cookie_monster_delegate, const scoped_refptr<base::SequencedTaskRunner>& client_task_runner, - const scoped_refptr<base::SequencedTaskRunner>& background_task_runner); + const scoped_refptr<base::SequencedTaskRunner>& background_task_runner, + scoped_ptr<CookieCryptoDelegate> crypto_delegate); // Uses the default client_task_runner and background_task_runner. CONTENT_EXPORT net::CookieStore* CreatePersistentCookieStore( const base::FilePath& path, bool restore_old_session_cookies, quota::SpecialStoragePolicy* storage_policy, - net::CookieMonster::Delegate* cookie_monster_delegate); + net::CookieMonster::Delegate* cookie_monster_delegate, + scoped_ptr<CookieCryptoDelegate> crypto_delegate); } // namespace content |