blob: b42b31b0948e4e29e6159985bf1b3bc0251e731f [file] [log] [blame]
cfroussios3b5a4e42016-05-31 11:02:181// 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/key_storage_linux.h"
6
Peter Boström08e7ed82021-04-19 17:49:597#include <memory>
8
Christos Froussios4e170cb2017-12-01 09:42:339#include "base/bind.h"
cfroussios3ea4c692016-07-18 19:15:1410#include "base/environment.h"
cfroussios3ea4c692016-07-18 19:15:1411#include "base/logging.h"
Christos Froussios85c4d882020-01-15 10:35:2912#include "base/metrics/histogram_macros.h"
cfroussios3ea4c692016-07-18 19:15:1413#include "base/nix/xdg_util.h"
Samuel Attard670187f2021-05-19 23:15:4514#include "base/no_destructor.h"
Christos Froussios4e170cb2017-12-01 09:42:3315#include "base/synchronization/waitable_event.h"
Patrick Monette643cdf62021-10-15 19:13:4216#include "base/task/sequenced_task_runner.h"
17#include "base/task/task_runner_util.h"
Christos Froussios4e170cb2017-12-01 09:42:3318#include "base/threading/thread_restrictions.h"
Nico Weber356b3042019-08-23 15:30:4119#include "build/branding_buildflags.h"
Christos Froussios494196d2017-07-14 10:10:0420#include "components/os_crypt/key_storage_config_linux.h"
cfroussios3ea4c692016-07-18 19:15:1421
22#if defined(USE_LIBSECRET)
cfroussios3b5a4e42016-05-31 11:02:1823#include "components/os_crypt/key_storage_libsecret.h"
cfroussios3ea4c692016-07-18 19:15:1424#endif
cfroussiosb013c15b2016-09-03 01:10:1625#if defined(USE_KEYRING)
26#include "components/os_crypt/key_storage_keyring.h"
27#endif
cfroussios2e6729a42016-07-26 09:18:1228#if defined(USE_KWALLET)
29#include "components/os_crypt/key_storage_kwallet.h"
30#endif
31
Nico Weber356b3042019-08-23 15:30:4132#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
cfroussios2e6729a42016-07-26 09:18:1233const char KeyStorageLinux::kFolderName[] = "Chrome Keys";
34const char KeyStorageLinux::kKey[] = "Chrome Safe Storage";
35#else
36const char KeyStorageLinux::kFolderName[] = "Chromium Keys";
37const char KeyStorageLinux::kKey[] = "Chromium Safe Storage";
38#endif
39
Christos Froussiosf7924a62019-11-05 14:26:5040namespace {
41
Christos Froussios85c4d882020-01-15 10:35:2942// Used for metrics. Do not rearrange.
43enum class BackendUsage {
44 // A backend was selected and used.
45 // *_FAILED means the backend was selected but couldn't be used.
46 kDefer = 0,
47 kDeferFailed = 1,
48 kBasicText = 2,
49 kBasicTextFailed = 3,
50 kGnomeAny = 4,
51 kGnomeAnyFailed = 5,
52 kGnomeKeyring = 6,
53 kGnomeKeyringFailed = 7,
54 kGnomeLibsecret = 8,
55 kGnomeLibsecretFailed = 9,
56 kKwallet = 10,
57 kKwalletFailed = 11,
58 kKwallet5 = 12,
59 kKwallet5Failed = 13,
60 kMaxValue = kKwallet5Failed,
61};
62
63constexpr BackendUsage SelectedBackendToMetric(
64 os_crypt::SelectedLinuxBackend selection,
65 bool used) {
66 switch (selection) {
67 case os_crypt::SelectedLinuxBackend::DEFER:
68 return used ? BackendUsage::kDefer : BackendUsage::kDeferFailed;
69 case os_crypt::SelectedLinuxBackend::BASIC_TEXT:
70 return used ? BackendUsage::kBasicText : BackendUsage::kBasicTextFailed;
71 case os_crypt::SelectedLinuxBackend::GNOME_ANY:
72 return used ? BackendUsage::kGnomeAny : BackendUsage::kGnomeAnyFailed;
73 case os_crypt::SelectedLinuxBackend::GNOME_KEYRING:
74 return used ? BackendUsage::kGnomeKeyring
75 : BackendUsage::kGnomeKeyringFailed;
76 case os_crypt::SelectedLinuxBackend::GNOME_LIBSECRET:
77 return used ? BackendUsage::kGnomeLibsecret
78 : BackendUsage::kGnomeLibsecretFailed;
79 case os_crypt::SelectedLinuxBackend::KWALLET:
80 return used ? BackendUsage::kKwallet : BackendUsage::kKwalletFailed;
81 case os_crypt::SelectedLinuxBackend::KWALLET5:
82 return used ? BackendUsage::kKwallet5 : BackendUsage::kKwallet5Failed;
83 }
84 NOTREACHED();
85 return BackendUsage::kDeferFailed;
86}
87
Christos Froussiosf7924a62019-11-05 14:26:5088const char* SelectedLinuxBackendToString(
89 os_crypt::SelectedLinuxBackend selection) {
90 switch (selection) {
91 case os_crypt::SelectedLinuxBackend::DEFER:
92 return "DEFER";
93 case os_crypt::SelectedLinuxBackend::BASIC_TEXT:
94 return "BASIC_TEXT";
95 case os_crypt::SelectedLinuxBackend::GNOME_ANY:
96 return "GNOME_ANY";
97 case os_crypt::SelectedLinuxBackend::GNOME_KEYRING:
98 return "GNOME_KEYRING";
99 case os_crypt::SelectedLinuxBackend::GNOME_LIBSECRET:
100 return "GNOME_LIBSECRET";
101 case os_crypt::SelectedLinuxBackend::KWALLET:
102 return "KWALLET";
103 case os_crypt::SelectedLinuxBackend::KWALLET5:
104 return "KWALLET5";
105 }
106 NOTREACHED();
107 return nullptr;
108}
109
Christos Froussios85c4d882020-01-15 10:35:29110} // namespace
111
cfroussios3ea4c692016-07-18 19:15:14112// static
Christos Froussios494196d2017-07-14 10:10:04113std::unique_ptr<KeyStorageLinux> KeyStorageLinux::CreateService(
114 const os_crypt::Config& config) {
cfroussios2f05d7f2016-08-17 15:58:50115 // Select a backend.
Christos Froussios494196d2017-07-14 10:10:04116 bool use_backend = !config.should_use_preference ||
117 os_crypt::GetBackendUse(config.user_data_path);
cfroussios2f05d7f2016-08-17 15:58:50118 std::unique_ptr<base::Environment> env(base::Environment::Create());
119 base::nix::DesktopEnvironment desktop_env =
120 base::nix::GetDesktopEnvironment(env.get());
121 os_crypt::SelectedLinuxBackend selected_backend =
Christos Froussios494196d2017-07-14 10:10:04122 os_crypt::SelectBackend(config.store, use_backend, desktop_env);
Christos Froussiosf7924a62019-11-05 14:26:50123 VLOG(1) << "Selected backend for OSCrypt: "
124 << SelectedLinuxBackendToString(selected_backend);
cfroussios3b5a4e42016-05-31 11:02:18125
Christos Froussios985d1aac2017-11-09 11:01:07126 // TODO(crbug.com/782851) Schedule the initialisation on each backend's
127 // favourite thread.
128
cfroussios2f05d7f2016-08-17 15:58:50129 // Try initializing the selected backend.
cfroussiosb013c15b2016-09-03 01:10:16130 // In case of GNOME_ANY, prefer Libsecret
cfroussios3ea4c692016-07-18 19:15:14131 std::unique_ptr<KeyStorageLinux> key_storage;
Christos Froussios85c4d882020-01-15 10:35:29132#if defined(USE_LIBSECRET) || defined(USE_KEYRING) || defined(USE_KWALLET)
133 key_storage = CreateServiceInternal(selected_backend, config);
134#endif // defined(USE_LIBSECRET) || defined(USE_KEYRING) ||
135 // defined(USE_KWALLET)
136
137 UMA_HISTOGRAM_ENUMERATION(
138 "OSCrypt.BackendUsage",
139 SelectedBackendToMetric(selected_backend, key_storage != nullptr));
140
141 // Either there are no supported backends on this platform, or we chose to
142 // use no backend, or the chosen backend failed to initialise.
143 VLOG_IF(1, !key_storage) << "OSCrypt did not initialize a backend.";
144 return key_storage;
145}
146
147#if defined(USE_LIBSECRET) || defined(USE_KEYRING) || defined(USE_KWALLET)
148std::unique_ptr<KeyStorageLinux> KeyStorageLinux::CreateServiceInternal(
149 os_crypt::SelectedLinuxBackend selected_backend,
150 const os_crypt::Config& config) {
Samuel Attard670187f2021-05-19 23:15:45151#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
152 static const base::NoDestructor<std::string> kDefaultApplicationName("chrome");
153#else
154 static const base::NoDestructor<std::string> kDefaultApplicationName("chromium");
155#endif
156
Christos Froussios85c4d882020-01-15 10:35:29157 std::unique_ptr<KeyStorageLinux> key_storage;
cfroussiosb013c15b2016-09-03 01:10:16158
Samuel Attard670187f2021-05-19 23:15:45159#if defined(USE_LIBSECRET) || defined(USE_KEYRING)
160#if defined(ALLOW_RUNTIME_CONFIGURABLE_KEY_STORAGE)
161 std::string application_name = config.application_name;
162 if (application_name.empty()) {
163 application_name = *kDefaultApplicationName;
164 }
165#else
166 std::string application_name = *kDefaultApplicationName;
167#endif
168#endif
169
cfroussiosb013c15b2016-09-03 01:10:16170#if defined(USE_LIBSECRET)
cfroussios2f05d7f2016-08-17 15:58:50171 if (selected_backend == os_crypt::SelectedLinuxBackend::GNOME_ANY ||
172 selected_backend == os_crypt::SelectedLinuxBackend::GNOME_LIBSECRET) {
Maksim Ivanov8bfa91a2021-06-01 14:08:01173 key_storage = std::make_unique<KeyStorageLibsecret>(application_name);
Christos Froussios4e170cb2017-12-01 09:42:33174 if (key_storage->WaitForInitOnTaskRunner()) {
cfroussios3ea4c692016-07-18 19:15:14175 VLOG(1) << "OSCrypt using Libsecret as backend.";
176 return key_storage;
177 }
Christos Froussiosf7924a62019-11-05 14:26:50178 LOG(WARNING) << "OSCrypt tried Libsecret but couldn't initialise.";
cfroussiosb013c15b2016-09-03 01:10:16179 }
slana881a862016-09-09 21:36:07180#endif // defined(USE_LIBSECRET)
cfroussiosb013c15b2016-09-03 01:10:16181
182#if defined(USE_KEYRING)
183 if (selected_backend == os_crypt::SelectedLinuxBackend::GNOME_ANY ||
184 selected_backend == os_crypt::SelectedLinuxBackend::GNOME_KEYRING) {
Samuel Attard670187f2021-05-19 23:15:45185 key_storage = std::make_unique<KeyStorageKeyring>(config.main_thread_runner,
Maksim Ivanov8bfa91a2021-06-01 14:08:01186 application_name);
Christos Froussios4e170cb2017-12-01 09:42:33187 if (key_storage->WaitForInitOnTaskRunner()) {
cfroussiosb013c15b2016-09-03 01:10:16188 VLOG(1) << "OSCrypt using Keyring as backend.";
189 return key_storage;
190 }
Christos Froussiosf7924a62019-11-05 14:26:50191 LOG(WARNING) << "OSCrypt tried Keyring but couldn't initialise.";
cfroussiosb013c15b2016-09-03 01:10:16192 }
slana881a862016-09-09 21:36:07193#endif // defined(USE_KEYRING)
cfroussiosb013c15b2016-09-03 01:10:16194
cfroussios2e6729a42016-07-26 09:18:12195#if defined(USE_KWALLET)
cfroussiosb013c15b2016-09-03 01:10:16196 if (selected_backend == os_crypt::SelectedLinuxBackend::KWALLET ||
197 selected_backend == os_crypt::SelectedLinuxBackend::KWALLET5) {
Christos Froussios494196d2017-07-14 10:10:04198 DCHECK(!config.product_name.empty());
cfroussios2f05d7f2016-08-17 15:58:50199 base::nix::DesktopEnvironment used_desktop_env =
200 selected_backend == os_crypt::SelectedLinuxBackend::KWALLET
201 ? base::nix::DESKTOP_ENVIRONMENT_KDE4
202 : base::nix::DESKTOP_ENVIRONMENT_KDE5;
Peter Boström08e7ed82021-04-19 17:49:59203 key_storage = std::make_unique<KeyStorageKWallet>(used_desktop_env,
204 config.product_name);
Christos Froussios4e170cb2017-12-01 09:42:33205 if (key_storage->WaitForInitOnTaskRunner()) {
cfroussios2e6729a42016-07-26 09:18:12206 VLOG(1) << "OSCrypt using KWallet as backend.";
207 return key_storage;
208 }
Christos Froussiosf7924a62019-11-05 14:26:50209 LOG(WARNING) << "OSCrypt tried KWallet but couldn't initialise.";
cfroussios3ea4c692016-07-18 19:15:14210 }
slana881a862016-09-09 21:36:07211#endif // defined(USE_KWALLET)
cfroussios3b5a4e42016-05-31 11:02:18212
cfroussios3b5a4e42016-05-31 11:02:18213 return nullptr;
214}
Christos Froussios85c4d882020-01-15 10:35:29215#endif // defined(USE_LIBSECRET) || defined(USE_KEYRING) ||
216 // defined(USE_KWALLET)
Christos Froussios985d1aac2017-11-09 11:01:07217
Christos Froussios4e170cb2017-12-01 09:42:33218bool KeyStorageLinux::WaitForInitOnTaskRunner() {
219 base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_sync_primitives;
220 base::SequencedTaskRunner* task_runner = GetTaskRunner();
221
222 // We don't need to change threads if the backend has no preference or if we
223 // are already on the right thread.
224 if (!task_runner || task_runner->RunsTasksInCurrentSequence())
225 return Init();
226
227 base::WaitableEvent initialized(
228 base::WaitableEvent::ResetPolicy::MANUAL,
229 base::WaitableEvent::InitialState::NOT_SIGNALED);
230 bool success;
Christos Froussios57fe5742017-12-07 21:16:49231 task_runner->PostTask(
232 FROM_HERE,
233 base::BindOnce(&KeyStorageLinux::BlockOnInitThenSignal,
234 base::Unretained(this), &initialized, &success));
Christos Froussios4e170cb2017-12-01 09:42:33235 initialized.Wait();
236 return success;
237}
238
Anton Bikineev1156b5f2021-05-15 22:35:36239absl::optional<std::string> KeyStorageLinux::GetKey() {
Christos Froussios4e170cb2017-12-01 09:42:33240 base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_sync_primitives;
241 base::SequencedTaskRunner* task_runner = GetTaskRunner();
242
243 // We don't need to change threads if the backend has no preference or if we
244 // are already on the right thread.
245 if (!task_runner || task_runner->RunsTasksInCurrentSequence())
246 return GetKeyImpl();
247
248 base::WaitableEvent password_loaded(
249 base::WaitableEvent::ResetPolicy::MANUAL,
250 base::WaitableEvent::InitialState::NOT_SIGNALED);
Anton Bikineev1156b5f2021-05-15 22:35:36251 absl::optional<std::string> password;
Christos Froussios57fe5742017-12-07 21:16:49252 task_runner->PostTask(
253 FROM_HERE,
254 base::BindOnce(&KeyStorageLinux::BlockOnGetKeyImplThenSignal,
255 base::Unretained(this), &password_loaded, &password));
Christos Froussios4e170cb2017-12-01 09:42:33256 password_loaded.Wait();
257 return password;
258}
259
260base::SequencedTaskRunner* KeyStorageLinux::GetTaskRunner() {
261 return nullptr;
Christos Froussios985d1aac2017-11-09 11:01:07262}
Christos Froussios57fe5742017-12-07 21:16:49263
264void KeyStorageLinux::BlockOnGetKeyImplThenSignal(
265 base::WaitableEvent* on_password_received,
Anton Bikineev1156b5f2021-05-15 22:35:36266 absl::optional<std::string>* password) {
Christos Froussios57fe5742017-12-07 21:16:49267 *password = GetKeyImpl();
268 on_password_received->Signal();
269}
270
271void KeyStorageLinux::BlockOnInitThenSignal(base::WaitableEvent* on_inited,
272 bool* success) {
273 *success = Init();
274 on_inited->Signal();
275}