key_storage_libsecret_unittest.cc 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // Copyright 2016 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <string>
  5. #include <unordered_map>
  6. #include "base/lazy_instance.h"
  7. #include "base/memory/raw_ptr.h"
  8. #include "components/os_crypt/key_storage_libsecret.h"
  9. #include "components/os_crypt/libsecret_util_linux.h"
  10. #include "testing/gtest/include/gtest/gtest.h"
  11. namespace {
  12. const SecretSchema kKeystoreSchemaV2 = {
  13. "chrome_libsecret_os_crypt_password_v2",
  14. SECRET_SCHEMA_DONT_MATCH_NAME,
  15. {
  16. {"application", SECRET_SCHEMA_ATTRIBUTE_STRING},
  17. {nullptr, SECRET_SCHEMA_ATTRIBUTE_STRING},
  18. }};
  19. // This test mocks C-functions used by Libsecret. In order to present a
  20. // consistent world view, we need a single backing instance that contains all
  21. // the relevant data.
  22. class MockPasswordStore {
  23. // The functions that interact with the password store expect to return
  24. // gobjects. These C-style objects are hard to work with. Rather than finagle
  25. // with the type system, we always use objects with type G_TYPE_OBJECT. We
  26. // then keep a local map from G_TYPE_OBJECT to std::string, and all relevant
  27. // translation just look up entries in this map.
  28. public:
  29. void Reset() {
  30. mapping_.clear();
  31. ClearPassword();
  32. for (GObject* o : objects_returned_to_caller_) {
  33. ASSERT_EQ(o->ref_count, 1u);
  34. g_object_unref(o);
  35. }
  36. objects_returned_to_caller_.clear();
  37. }
  38. void ClearPassword() {
  39. if (password_) {
  40. ASSERT_EQ(password_->ref_count, 1u);
  41. g_object_unref(password_);
  42. password_ = nullptr;
  43. }
  44. }
  45. void SetPassword(const std::string& password) {
  46. ASSERT_FALSE(password_);
  47. password_ = static_cast<GObject*>(g_object_new(G_TYPE_OBJECT, nullptr));
  48. mapping_[password_] = password;
  49. }
  50. GObject* MakeTempObject(const std::string& value) {
  51. GObject* temp = static_cast<GObject*>(g_object_new(G_TYPE_OBJECT, nullptr));
  52. // The returned object has a ref count of 2. This way, after the client
  53. // deletes the object, it isn't destroyed, and we can check that all these
  54. // objects have ref count of 1 at the end of the test.
  55. g_object_ref(temp);
  56. objects_returned_to_caller_.push_back(temp);
  57. mapping_[temp] = value;
  58. return temp;
  59. }
  60. const gchar* GetString(void* opaque_id) {
  61. return mapping_[static_cast<GObject*>(opaque_id)].c_str();
  62. }
  63. GObject* password() { return password_; }
  64. std::unordered_map<GObject*, std::string> mapping_;
  65. std::vector<GObject*> objects_returned_to_caller_;
  66. raw_ptr<GObject> password_ = nullptr;
  67. };
  68. base::LazyInstance<MockPasswordStore>::Leaky g_password_store =
  69. LAZY_INSTANCE_INITIALIZER;
  70. // Replaces some of LibsecretLoader's methods with mocked ones.
  71. class MockLibsecretLoader : public LibsecretLoader {
  72. public:
  73. // Sets up the minimum mock implementation necessary for OSCrypt to work
  74. // with Libsecret. Also resets the state to mock a clean database.
  75. static bool ResetForOSCrypt();
  76. // Sets OSCrypt's password in the libsecret mock to a specific value
  77. static void SetOSCryptPassword(const char*);
  78. // Releases memory and restores LibsecretLoader to an uninitialized state.
  79. static void TearDown();
  80. private:
  81. // These methods are used to redirect calls through LibsecretLoader
  82. static const gchar* mock_secret_value_get_text(SecretValue* value);
  83. static gboolean mock_secret_password_store_sync(const SecretSchema* schema,
  84. const gchar* collection,
  85. const gchar* label,
  86. const gchar* password,
  87. GCancellable* cancellable,
  88. GError** error,
  89. ...);
  90. static void mock_secret_value_unref(gpointer value);
  91. static GList* mock_secret_service_search_sync(SecretService* service,
  92. const SecretSchema* schema,
  93. GHashTable* attributes,
  94. SecretSearchFlags flags,
  95. GCancellable* cancellable,
  96. GError** error);
  97. static SecretValue* mock_secret_item_get_secret(SecretItem* item);
  98. static guint64 mock_secret_item_get_created(SecretItem* item);
  99. static guint64 mock_secret_item_get_modified(SecretItem* item);
  100. };
  101. const gchar* MockLibsecretLoader::mock_secret_value_get_text(
  102. SecretValue* value) {
  103. return g_password_store.Pointer()->GetString(value);
  104. }
  105. // static
  106. gboolean MockLibsecretLoader::mock_secret_password_store_sync(
  107. const SecretSchema* schema,
  108. const gchar* collection,
  109. const gchar* label,
  110. const gchar* password,
  111. GCancellable* cancellable,
  112. GError** error,
  113. ...) {
  114. // TODO(crbug.com/660005) We don't read the dummy we store to unlock keyring.
  115. if (strcmp("_chrome_dummy_schema_for_unlocking", schema->name) == 0)
  116. return true;
  117. EXPECT_STREQ(kKeystoreSchemaV2.name, schema->name);
  118. g_password_store.Pointer()->SetPassword(password);
  119. return true;
  120. }
  121. // static
  122. void MockLibsecretLoader::mock_secret_value_unref(gpointer value) {
  123. g_object_unref(value);
  124. }
  125. // static
  126. GList* MockLibsecretLoader::mock_secret_service_search_sync(
  127. SecretService* service,
  128. const SecretSchema* schema,
  129. GHashTable* attributes,
  130. SecretSearchFlags flags,
  131. GCancellable* cancellable,
  132. GError** error) {
  133. EXPECT_STREQ(kKeystoreSchemaV2.name, schema->name);
  134. EXPECT_TRUE(flags & SECRET_SEARCH_UNLOCK);
  135. EXPECT_TRUE(flags & SECRET_SEARCH_LOAD_SECRETS);
  136. GObject* item = nullptr;
  137. MockPasswordStore* store = g_password_store.Pointer();
  138. GObject* password = store->password();
  139. if (password)
  140. item = store->MakeTempObject(store->GetString(password));
  141. if (!item) {
  142. return nullptr;
  143. }
  144. GList* result = nullptr;
  145. result = g_list_append(result, item);
  146. g_clear_error(error);
  147. return result;
  148. }
  149. // static
  150. SecretValue* MockLibsecretLoader::mock_secret_item_get_secret(
  151. SecretItem* item) {
  152. // Add a ref to make sure that the caller unrefs with secret_value_unref.
  153. g_object_ref(item);
  154. return reinterpret_cast<SecretValue*>(item);
  155. }
  156. // static
  157. guint64 MockLibsecretLoader::mock_secret_item_get_created(SecretItem* item) {
  158. return 0;
  159. }
  160. // static
  161. guint64 MockLibsecretLoader::mock_secret_item_get_modified(SecretItem* item) {
  162. return 0;
  163. }
  164. // static
  165. bool MockLibsecretLoader::ResetForOSCrypt() {
  166. // Methods used by KeyStorageLibsecret
  167. secret_password_store_sync =
  168. &MockLibsecretLoader::mock_secret_password_store_sync;
  169. secret_value_get_text = &MockLibsecretLoader::mock_secret_value_get_text;
  170. secret_value_unref = &MockLibsecretLoader::mock_secret_value_unref;
  171. secret_service_search_sync =
  172. &MockLibsecretLoader::mock_secret_service_search_sync;
  173. secret_item_get_secret = &MockLibsecretLoader::mock_secret_item_get_secret;
  174. secret_item_get_created = &MockLibsecretLoader::mock_secret_item_get_created;
  175. secret_item_get_modified =
  176. &MockLibsecretLoader::mock_secret_item_get_modified;
  177. g_password_store.Pointer()->Reset();
  178. libsecret_loaded_ = true;
  179. return true;
  180. }
  181. // static
  182. void MockLibsecretLoader::TearDown() {
  183. g_password_store.Pointer()->Reset();
  184. libsecret_loaded_ =
  185. false; // Function pointers will be restored when loading.
  186. }
  187. class LibsecretTest : public testing::Test {
  188. public:
  189. LibsecretTest() = default;
  190. LibsecretTest(const LibsecretTest&) = delete;
  191. LibsecretTest& operator=(const LibsecretTest&) = delete;
  192. ~LibsecretTest() override = default;
  193. void SetUp() override { MockLibsecretLoader::ResetForOSCrypt(); }
  194. void TearDown() override { MockLibsecretLoader::TearDown(); }
  195. };
  196. TEST_F(LibsecretTest, LibsecretRepeats) {
  197. KeyStorageLibsecret libsecret("chromium");
  198. MockLibsecretLoader::ResetForOSCrypt();
  199. g_password_store.Pointer()->SetPassword("initial password");
  200. absl::optional<std::string> password = libsecret.GetKey();
  201. EXPECT_TRUE(password.has_value());
  202. EXPECT_FALSE(password.value().empty());
  203. absl::optional<std::string> password_repeat = libsecret.GetKey();
  204. EXPECT_TRUE(password_repeat.has_value());
  205. EXPECT_EQ(password.value(), password_repeat.value());
  206. }
  207. TEST_F(LibsecretTest, LibsecretCreatesRandomised) {
  208. KeyStorageLibsecret libsecret("chromium");
  209. MockLibsecretLoader::ResetForOSCrypt();
  210. absl::optional<std::string> password = libsecret.GetKey();
  211. MockLibsecretLoader::ResetForOSCrypt();
  212. absl::optional<std::string> password_new = libsecret.GetKey();
  213. EXPECT_TRUE(password.has_value());
  214. EXPECT_TRUE(password_new.has_value());
  215. EXPECT_NE(password.value(), password_new.value());
  216. }
  217. } // namespace