123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- // Copyright 2016 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.
- #include <string>
- #include <unordered_map>
- #include "base/lazy_instance.h"
- #include "base/memory/raw_ptr.h"
- #include "components/os_crypt/key_storage_libsecret.h"
- #include "components/os_crypt/libsecret_util_linux.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace {
- const SecretSchema kKeystoreSchemaV2 = {
- "chrome_libsecret_os_crypt_password_v2",
- SECRET_SCHEMA_DONT_MATCH_NAME,
- {
- {"application", SECRET_SCHEMA_ATTRIBUTE_STRING},
- {nullptr, SECRET_SCHEMA_ATTRIBUTE_STRING},
- }};
- // This test mocks C-functions used by Libsecret. In order to present a
- // consistent world view, we need a single backing instance that contains all
- // the relevant data.
- class MockPasswordStore {
- // The functions that interact with the password store expect to return
- // gobjects. These C-style objects are hard to work with. Rather than finagle
- // with the type system, we always use objects with type G_TYPE_OBJECT. We
- // then keep a local map from G_TYPE_OBJECT to std::string, and all relevant
- // translation just look up entries in this map.
- public:
- void Reset() {
- mapping_.clear();
- ClearPassword();
- for (GObject* o : objects_returned_to_caller_) {
- ASSERT_EQ(o->ref_count, 1u);
- g_object_unref(o);
- }
- objects_returned_to_caller_.clear();
- }
- void ClearPassword() {
- if (password_) {
- ASSERT_EQ(password_->ref_count, 1u);
- g_object_unref(password_);
- password_ = nullptr;
- }
- }
- void SetPassword(const std::string& password) {
- ASSERT_FALSE(password_);
- password_ = static_cast<GObject*>(g_object_new(G_TYPE_OBJECT, nullptr));
- mapping_[password_] = password;
- }
- GObject* MakeTempObject(const std::string& value) {
- GObject* temp = static_cast<GObject*>(g_object_new(G_TYPE_OBJECT, nullptr));
- // The returned object has a ref count of 2. This way, after the client
- // deletes the object, it isn't destroyed, and we can check that all these
- // objects have ref count of 1 at the end of the test.
- g_object_ref(temp);
- objects_returned_to_caller_.push_back(temp);
- mapping_[temp] = value;
- return temp;
- }
- const gchar* GetString(void* opaque_id) {
- return mapping_[static_cast<GObject*>(opaque_id)].c_str();
- }
- GObject* password() { return password_; }
- std::unordered_map<GObject*, std::string> mapping_;
- std::vector<GObject*> objects_returned_to_caller_;
- raw_ptr<GObject> password_ = nullptr;
- };
- base::LazyInstance<MockPasswordStore>::Leaky g_password_store =
- LAZY_INSTANCE_INITIALIZER;
- // Replaces some of LibsecretLoader's methods with mocked ones.
- class MockLibsecretLoader : public LibsecretLoader {
- public:
- // Sets up the minimum mock implementation necessary for OSCrypt to work
- // with Libsecret. Also resets the state to mock a clean database.
- static bool ResetForOSCrypt();
- // Sets OSCrypt's password in the libsecret mock to a specific value
- static void SetOSCryptPassword(const char*);
- // Releases memory and restores LibsecretLoader to an uninitialized state.
- static void TearDown();
- private:
- // These methods are used to redirect calls through LibsecretLoader
- static const gchar* mock_secret_value_get_text(SecretValue* value);
- static gboolean mock_secret_password_store_sync(const SecretSchema* schema,
- const gchar* collection,
- const gchar* label,
- const gchar* password,
- GCancellable* cancellable,
- GError** error,
- ...);
- static void mock_secret_value_unref(gpointer value);
- static GList* mock_secret_service_search_sync(SecretService* service,
- const SecretSchema* schema,
- GHashTable* attributes,
- SecretSearchFlags flags,
- GCancellable* cancellable,
- GError** error);
- static SecretValue* mock_secret_item_get_secret(SecretItem* item);
- static guint64 mock_secret_item_get_created(SecretItem* item);
- static guint64 mock_secret_item_get_modified(SecretItem* item);
- };
- const gchar* MockLibsecretLoader::mock_secret_value_get_text(
- SecretValue* value) {
- return g_password_store.Pointer()->GetString(value);
- }
- // static
- gboolean MockLibsecretLoader::mock_secret_password_store_sync(
- const SecretSchema* schema,
- const gchar* collection,
- const gchar* label,
- const gchar* password,
- GCancellable* cancellable,
- GError** error,
- ...) {
- // TODO(crbug.com/660005) We don't read the dummy we store to unlock keyring.
- if (strcmp("_chrome_dummy_schema_for_unlocking", schema->name) == 0)
- return true;
- EXPECT_STREQ(kKeystoreSchemaV2.name, schema->name);
- g_password_store.Pointer()->SetPassword(password);
- return true;
- }
- // static
- void MockLibsecretLoader::mock_secret_value_unref(gpointer value) {
- g_object_unref(value);
- }
- // static
- GList* MockLibsecretLoader::mock_secret_service_search_sync(
- SecretService* service,
- const SecretSchema* schema,
- GHashTable* attributes,
- SecretSearchFlags flags,
- GCancellable* cancellable,
- GError** error) {
- EXPECT_STREQ(kKeystoreSchemaV2.name, schema->name);
- EXPECT_TRUE(flags & SECRET_SEARCH_UNLOCK);
- EXPECT_TRUE(flags & SECRET_SEARCH_LOAD_SECRETS);
- GObject* item = nullptr;
- MockPasswordStore* store = g_password_store.Pointer();
- GObject* password = store->password();
- if (password)
- item = store->MakeTempObject(store->GetString(password));
- if (!item) {
- return nullptr;
- }
- GList* result = nullptr;
- result = g_list_append(result, item);
- g_clear_error(error);
- return result;
- }
- // static
- SecretValue* MockLibsecretLoader::mock_secret_item_get_secret(
- SecretItem* item) {
- // Add a ref to make sure that the caller unrefs with secret_value_unref.
- g_object_ref(item);
- return reinterpret_cast<SecretValue*>(item);
- }
- // static
- guint64 MockLibsecretLoader::mock_secret_item_get_created(SecretItem* item) {
- return 0;
- }
- // static
- guint64 MockLibsecretLoader::mock_secret_item_get_modified(SecretItem* item) {
- return 0;
- }
- // static
- bool MockLibsecretLoader::ResetForOSCrypt() {
- // Methods used by KeyStorageLibsecret
- secret_password_store_sync =
- &MockLibsecretLoader::mock_secret_password_store_sync;
- secret_value_get_text = &MockLibsecretLoader::mock_secret_value_get_text;
- secret_value_unref = &MockLibsecretLoader::mock_secret_value_unref;
- secret_service_search_sync =
- &MockLibsecretLoader::mock_secret_service_search_sync;
- secret_item_get_secret = &MockLibsecretLoader::mock_secret_item_get_secret;
- secret_item_get_created = &MockLibsecretLoader::mock_secret_item_get_created;
- secret_item_get_modified =
- &MockLibsecretLoader::mock_secret_item_get_modified;
- g_password_store.Pointer()->Reset();
- libsecret_loaded_ = true;
- return true;
- }
- // static
- void MockLibsecretLoader::TearDown() {
- g_password_store.Pointer()->Reset();
- libsecret_loaded_ =
- false; // Function pointers will be restored when loading.
- }
- class LibsecretTest : public testing::Test {
- public:
- LibsecretTest() = default;
- LibsecretTest(const LibsecretTest&) = delete;
- LibsecretTest& operator=(const LibsecretTest&) = delete;
- ~LibsecretTest() override = default;
- void SetUp() override { MockLibsecretLoader::ResetForOSCrypt(); }
- void TearDown() override { MockLibsecretLoader::TearDown(); }
- };
- TEST_F(LibsecretTest, LibsecretRepeats) {
- KeyStorageLibsecret libsecret("chromium");
- MockLibsecretLoader::ResetForOSCrypt();
- g_password_store.Pointer()->SetPassword("initial password");
- absl::optional<std::string> password = libsecret.GetKey();
- EXPECT_TRUE(password.has_value());
- EXPECT_FALSE(password.value().empty());
- absl::optional<std::string> password_repeat = libsecret.GetKey();
- EXPECT_TRUE(password_repeat.has_value());
- EXPECT_EQ(password.value(), password_repeat.value());
- }
- TEST_F(LibsecretTest, LibsecretCreatesRandomised) {
- KeyStorageLibsecret libsecret("chromium");
- MockLibsecretLoader::ResetForOSCrypt();
- absl::optional<std::string> password = libsecret.GetKey();
- MockLibsecretLoader::ResetForOSCrypt();
- absl::optional<std::string> password_new = libsecret.GetKey();
- EXPECT_TRUE(password.has_value());
- EXPECT_TRUE(password_new.has_value());
- EXPECT_NE(password.value(), password_new.value());
- }
- } // namespace
|