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