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 
5 #include "components/os_crypt/libsecret_util_linux.h"
6 
7 #include <dlfcn.h>
8 
9 #include "base/logging.h"
10 #include "base/strings/string_number_conversions.h"
11 
12 //
13 // LibsecretLoader
14 //
15 
16 namespace {
17 
18 // TODO(crbug.com/660005) A message that is attached to useless entries that we
19 // create, to explain its existence.
20 const char kExplanationMessage[] =
21     "Because of quirks in the gnome libsecret API, Chrome needs to store a "
22     "dummy entry to guarantee that this keyring was properly unlocked. More "
23     "details at http://crbug.com/660005.";
24 
25 }  // namespace
26 
27 decltype(
28     &::secret_password_store_sync) LibsecretLoader::secret_password_store_sync =
29     nullptr;
30 decltype(
31     &::secret_service_search_sync) LibsecretLoader::secret_service_search_sync =
32     nullptr;
33 decltype(
34     &::secret_password_clear_sync) LibsecretLoader::secret_password_clear_sync =
35     nullptr;
36 decltype(&::secret_item_get_secret) LibsecretLoader::secret_item_get_secret =
37     nullptr;
38 decltype(&::secret_item_get_created) LibsecretLoader::secret_item_get_created =
39     nullptr;
40 decltype(
41     &::secret_item_get_modified) LibsecretLoader::secret_item_get_modified =
42     nullptr;
43 decltype(&::secret_value_get_text) LibsecretLoader::secret_value_get_text =
44     nullptr;
45 decltype(
46     &::secret_item_get_attributes) LibsecretLoader::secret_item_get_attributes =
47     nullptr;
48 decltype(&::secret_item_load_secret_sync)
49     LibsecretLoader::secret_item_load_secret_sync = nullptr;
50 decltype(&::secret_value_unref) LibsecretLoader::secret_value_unref = nullptr;
51 
52 bool LibsecretLoader::libsecret_loaded_ = false;
53 
54 const LibsecretLoader::FunctionInfo LibsecretLoader::kFunctions[] = {
55     {"secret_item_get_secret",
56      reinterpret_cast<void**>(&secret_item_get_secret)},
57     {"secret_item_get_attributes",
58      reinterpret_cast<void**>(&secret_item_get_attributes)},
59     {"secret_item_get_created",
60      reinterpret_cast<void**>(&secret_item_get_created)},
61     {"secret_item_get_modified",
62      reinterpret_cast<void**>(&secret_item_get_modified)},
63     {"secret_item_load_secret_sync",
64      reinterpret_cast<void**>(&secret_item_load_secret_sync)},
65     {"secret_password_clear_sync",
66      reinterpret_cast<void**>(&secret_password_clear_sync)},
67     {"secret_password_store_sync",
68      reinterpret_cast<void**>(&secret_password_store_sync)},
69     {"secret_service_search_sync",
70      reinterpret_cast<void**>(&secret_service_search_sync)},
71     {"secret_value_get_text", reinterpret_cast<void**>(&secret_value_get_text)},
72     {"secret_value_unref", reinterpret_cast<void**>(&secret_value_unref)},
73 };
74 
75 LibsecretLoader::SearchHelper::SearchHelper() = default;
~SearchHelper()76 LibsecretLoader::SearchHelper::~SearchHelper() {
77   if (error_)
78     g_error_free(error_);
79   if (results_)
80     g_list_free_full(results_, &g_object_unref);
81 }
82 
Search(const SecretSchema * schema,GHashTable * attrs,int flags)83 void LibsecretLoader::SearchHelper::Search(const SecretSchema* schema,
84                                            GHashTable* attrs,
85                                            int flags) {
86   DCHECK(!results_);
87   results_ = LibsecretLoader::secret_service_search_sync(
88       nullptr,  // default secret service
89       schema, attrs, static_cast<SecretSearchFlags>(flags),
90       nullptr,  // no cancellable object
91       &error_);
92 }
93 
94 // static
EnsureLibsecretLoaded()95 bool LibsecretLoader::EnsureLibsecretLoaded() {
96   return LoadLibsecret() && LibsecretIsAvailable();
97 }
98 
99 // static
LoadLibsecret()100 bool LibsecretLoader::LoadLibsecret() {
101   if (libsecret_loaded_)
102     return true;
103 
104   static void* handle = dlopen("libsecret-1.so.0", RTLD_NOW | RTLD_GLOBAL);
105   if (!handle) {
106     // We wanted to use libsecret, but we couldn't load it. Warn, because
107     // either the user asked for this, or we autodetected it incorrectly. (Or
108     // the system has broken libraries, which is also good to warn about.)
109     // TODO(crbug.com/607435): Channel this message to the user-facing log
110     VLOG(1) << "Could not load libsecret-1.so.0: " << dlerror();
111     return false;
112   }
113 
114   for (const auto& function : kFunctions) {
115     dlerror();
116     *function.pointer = dlsym(handle, function.name);
117     const char* error = dlerror();
118     if (error) {
119       VLOG(1) << "Unable to load symbol " << function.name << ": " << error;
120       dlclose(handle);
121       return false;
122     }
123   }
124 
125   libsecret_loaded_ = true;
126   // We leak the library handle. That's OK: this function is called only once.
127   return true;
128 }
129 
130 // static
LibsecretIsAvailable()131 bool LibsecretLoader::LibsecretIsAvailable() {
132   if (!libsecret_loaded_)
133     return false;
134   // A dummy query is made to check for availability, because libsecret doesn't
135   // have a dedicated availability function. For performance reasons, the query
136   // is meant to return an empty result.
137   LibsecretAttributesBuilder attrs;
138   attrs.Append("application", "chrome-string_to_get_empty_result");
139   const SecretSchema kDummySchema = {
140       "_chrome_dummy_schema",
141       SECRET_SCHEMA_DONT_MATCH_NAME,
142       {{"application", SECRET_SCHEMA_ATTRIBUTE_STRING},
143        {nullptr, SECRET_SCHEMA_ATTRIBUTE_STRING}}};
144 
145   SearchHelper helper;
146   helper.Search(&kDummySchema, attrs.Get(),
147                 SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK);
148   return helper.success();
149 }
150 
151 // TODO(crbug.com/660005) This is needed to properly unlock the default keyring.
152 // We don't need to ever read it.
EnsureKeyringUnlocked()153 void LibsecretLoader::EnsureKeyringUnlocked() {
154   const SecretSchema kDummySchema = {
155       "_chrome_dummy_schema_for_unlocking",
156       SECRET_SCHEMA_NONE,
157       {{"explanation", SECRET_SCHEMA_ATTRIBUTE_STRING},
158        {nullptr, SECRET_SCHEMA_ATTRIBUTE_STRING}}};
159 
160   GError* error = nullptr;
161   bool success = LibsecretLoader::secret_password_store_sync(
162       &kDummySchema, nullptr /* default keyring */,
163       "Chrome Safe Storage Control" /* entry title */,
164       "The meaning of life" /* password */, nullptr, &error, "explanation",
165       kExplanationMessage,
166       nullptr /* null-terminated variable argument list */);
167   if (error) {
168     VLOG(1) << "Dummy store to unlock the default keyring failed: "
169             << error->message;
170     g_error_free(error);
171   } else if (!success) {
172     VLOG(1) << "Dummy store to unlock the default keyring failed.";
173   }
174 }
175 
176 //
177 // LibsecretAttributesBuilder
178 //
179 
LibsecretAttributesBuilder()180 LibsecretAttributesBuilder::LibsecretAttributesBuilder() {
181   attrs_ = g_hash_table_new_full(g_str_hash, g_str_equal,
182                                  nullptr,   // no deleter for keys
183                                  nullptr);  // no deleter for values
184 }
185 
~LibsecretAttributesBuilder()186 LibsecretAttributesBuilder::~LibsecretAttributesBuilder() {
187   g_hash_table_destroy(attrs_);
188 }
189 
Append(const std::string & name,const std::string & value)190 void LibsecretAttributesBuilder::Append(const std::string& name,
191                                         const std::string& value) {
192   name_values_.push_back(name);
193   gpointer name_str =
194       static_cast<gpointer>(const_cast<char*>(name_values_.back().c_str()));
195   name_values_.push_back(value);
196   gpointer value_str =
197       static_cast<gpointer>(const_cast<char*>(name_values_.back().c_str()));
198   g_hash_table_insert(attrs_, name_str, value_str);
199 }
200 
Append(const std::string & name,int64_t value)201 void LibsecretAttributesBuilder::Append(const std::string& name,
202                                         int64_t value) {
203   Append(name, base::NumberToString(value));
204 }
205