blob: 33fed0fa438776e3da4de6a3589745ad8b8304e9 [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
Christos Froussios4e170cb2017-12-01 09:42:337#include "base/bind.h"
cfroussios3ea4c692016-07-18 19:15:148#include "base/environment.h"
cfroussios3ea4c692016-07-18 19:15:149#include "base/logging.h"
Christos Froussios85c4d882020-01-15 10:35:2910#include "base/metrics/histogram_macros.h"
cfroussios3ea4c692016-07-18 19:15:1411#include "base/nix/xdg_util.h"
Christos Froussios4e170cb2017-12-01 09:42:3312#include "base/sequenced_task_runner.h"
13#include "base/synchronization/waitable_event.h"
14#include "base/task_runner_util.h"
15#include "base/threading/thread_restrictions.h"
Nico Weber356b3042019-08-23 15:30:4116#include "build/branding_buildflags.h"
Christos Froussios494196d2017-07-14 10:10:0417#include "components/os_crypt/key_storage_config_linux.h"
cfroussios3ea4c692016-07-18 19:15:1418
19#if defined(USE_LIBSECRET)
cfroussios3b5a4e42016-05-31 11:02:1820#include "components/os_crypt/key_storage_libsecret.h"
cfroussios3ea4c692016-07-18 19:15:1421#endif
cfroussiosb013c15b2016-09-03 01:10:1622#if defined(USE_KEYRING)
23#include "components/os_crypt/key_storage_keyring.h"
24#endif
cfroussios2e6729a42016-07-26 09:18:1225#if defined(USE_KWALLET)
26#include "components/os_crypt/key_storage_kwallet.h"
27#endif
28
Nico Weber356b3042019-08-23 15:30:4129#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
cfroussios2e6729a42016-07-26 09:18:1230const char KeyStorageLinux::kFolderName[] = "Chrome Keys";
31const char KeyStorageLinux::kKey[] = "Chrome Safe Storage";
32#else
33const char KeyStorageLinux::kFolderName[] = "Chromium Keys";
34const char KeyStorageLinux::kKey[] = "Chromium Safe Storage";
35#endif
36
Christos Froussiosf7924a62019-11-05 14:26:5037namespace {
38
Christos Froussios85c4d882020-01-15 10:35:2939// Used for metrics. Do not rearrange.
40enum class BackendUsage {
41 // A backend was selected and used.
42 // *_FAILED means the backend was selected but couldn't be used.
43 kDefer = 0,
44 kDeferFailed = 1,
45 kBasicText = 2,
46 kBasicTextFailed = 3,
47 kGnomeAny = 4,
48 kGnomeAnyFailed = 5,
49 kGnomeKeyring = 6,
50 kGnomeKeyringFailed = 7,
51 kGnomeLibsecret = 8,
52 kGnomeLibsecretFailed = 9,
53 kKwallet = 10,
54 kKwalletFailed = 11,
55 kKwallet5 = 12,
56 kKwallet5Failed = 13,
57 kMaxValue = kKwallet5Failed,
58};
59
60constexpr BackendUsage SelectedBackendToMetric(
61 os_crypt::SelectedLinuxBackend selection,
62 bool used) {
63 switch (selection) {
64 case os_crypt::SelectedLinuxBackend::DEFER:
65 return used ? BackendUsage::kDefer : BackendUsage::kDeferFailed;
66 case os_crypt::SelectedLinuxBackend::BASIC_TEXT:
67 return used ? BackendUsage::kBasicText : BackendUsage::kBasicTextFailed;
68 case os_crypt::SelectedLinuxBackend::GNOME_ANY:
69 return used ? BackendUsage::kGnomeAny : BackendUsage::kGnomeAnyFailed;
70 case os_crypt::SelectedLinuxBackend::GNOME_KEYRING:
71 return used ? BackendUsage::kGnomeKeyring
72 : BackendUsage::kGnomeKeyringFailed;
73 case os_crypt::SelectedLinuxBackend::GNOME_LIBSECRET:
74 return used ? BackendUsage::kGnomeLibsecret
75 : BackendUsage::kGnomeLibsecretFailed;
76 case os_crypt::SelectedLinuxBackend::KWALLET:
77 return used ? BackendUsage::kKwallet : BackendUsage::kKwalletFailed;
78 case os_crypt::SelectedLinuxBackend::KWALLET5:
79 return used ? BackendUsage::kKwallet5 : BackendUsage::kKwallet5Failed;
80 }
81 NOTREACHED();
82 return BackendUsage::kDeferFailed;
83}
84
Christos Froussiosf7924a62019-11-05 14:26:5085const char* SelectedLinuxBackendToString(
86 os_crypt::SelectedLinuxBackend selection) {
87 switch (selection) {
88 case os_crypt::SelectedLinuxBackend::DEFER:
89 return "DEFER";
90 case os_crypt::SelectedLinuxBackend::BASIC_TEXT:
91 return "BASIC_TEXT";
92 case os_crypt::SelectedLinuxBackend::GNOME_ANY:
93 return "GNOME_ANY";
94 case os_crypt::SelectedLinuxBackend::GNOME_KEYRING:
95 return "GNOME_KEYRING";
96 case os_crypt::SelectedLinuxBackend::GNOME_LIBSECRET:
97 return "GNOME_LIBSECRET";
98 case os_crypt::SelectedLinuxBackend::KWALLET:
99 return "KWALLET";
100 case os_crypt::SelectedLinuxBackend::KWALLET5:
101 return "KWALLET5";
102 }
103 NOTREACHED();
104 return nullptr;
105}
106
Christos Froussios85c4d882020-01-15 10:35:29107} // namespace
108
cfroussios3ea4c692016-07-18 19:15:14109// static
Christos Froussios494196d2017-07-14 10:10:04110std::unique_ptr<KeyStorageLinux> KeyStorageLinux::CreateService(
111 const os_crypt::Config& config) {
cfroussios2f05d7f2016-08-17 15:58:50112 // Select a backend.
Christos Froussios494196d2017-07-14 10:10:04113 bool use_backend = !config.should_use_preference ||
114 os_crypt::GetBackendUse(config.user_data_path);
cfroussios2f05d7f2016-08-17 15:58:50115 std::unique_ptr<base::Environment> env(base::Environment::Create());
116 base::nix::DesktopEnvironment desktop_env =
117 base::nix::GetDesktopEnvironment(env.get());
118 os_crypt::SelectedLinuxBackend selected_backend =
Christos Froussios494196d2017-07-14 10:10:04119 os_crypt::SelectBackend(config.store, use_backend, desktop_env);
Christos Froussiosf7924a62019-11-05 14:26:50120 VLOG(1) << "Selected backend for OSCrypt: "
121 << SelectedLinuxBackendToString(selected_backend);
cfroussios3b5a4e42016-05-31 11:02:18122
Christos Froussios985d1aac2017-11-09 11:01:07123 // TODO(crbug.com/782851) Schedule the initialisation on each backend's
124 // favourite thread.
125
cfroussios2f05d7f2016-08-17 15:58:50126 // Try initializing the selected backend.
cfroussiosb013c15b2016-09-03 01:10:16127 // In case of GNOME_ANY, prefer Libsecret
cfroussios3ea4c692016-07-18 19:15:14128 std::unique_ptr<KeyStorageLinux> key_storage;
Christos Froussios85c4d882020-01-15 10:35:29129#if defined(USE_LIBSECRET) || defined(USE_KEYRING) || defined(USE_KWALLET)
130 key_storage = CreateServiceInternal(selected_backend, config);
131#endif // defined(USE_LIBSECRET) || defined(USE_KEYRING) ||
132 // defined(USE_KWALLET)
133
134 UMA_HISTOGRAM_ENUMERATION(
135 "OSCrypt.BackendUsage",
136 SelectedBackendToMetric(selected_backend, key_storage != nullptr));
137
138 // Either there are no supported backends on this platform, or we chose to
139 // use no backend, or the chosen backend failed to initialise.
140 VLOG_IF(1, !key_storage) << "OSCrypt did not initialize a backend.";
141 return key_storage;
142}
143
144#if defined(USE_LIBSECRET) || defined(USE_KEYRING) || defined(USE_KWALLET)
145std::unique_ptr<KeyStorageLinux> KeyStorageLinux::CreateServiceInternal(
146 os_crypt::SelectedLinuxBackend selected_backend,
147 const os_crypt::Config& config) {
148 std::unique_ptr<KeyStorageLinux> key_storage;
cfroussiosb013c15b2016-09-03 01:10:16149
150#if defined(USE_LIBSECRET)
cfroussios2f05d7f2016-08-17 15:58:50151 if (selected_backend == os_crypt::SelectedLinuxBackend::GNOME_ANY ||
152 selected_backend == os_crypt::SelectedLinuxBackend::GNOME_LIBSECRET) {
Christos Froussios70cc95b2017-12-14 21:13:23153 key_storage.reset(new KeyStorageLibsecret());
Christos Froussios4e170cb2017-12-01 09:42:33154 if (key_storage->WaitForInitOnTaskRunner()) {
cfroussios3ea4c692016-07-18 19:15:14155 VLOG(1) << "OSCrypt using Libsecret as backend.";
156 return key_storage;
157 }
Christos Froussiosf7924a62019-11-05 14:26:50158 LOG(WARNING) << "OSCrypt tried Libsecret but couldn't initialise.";
cfroussiosb013c15b2016-09-03 01:10:16159 }
slana881a862016-09-09 21:36:07160#endif // defined(USE_LIBSECRET)
cfroussiosb013c15b2016-09-03 01:10:16161
162#if defined(USE_KEYRING)
163 if (selected_backend == os_crypt::SelectedLinuxBackend::GNOME_ANY ||
164 selected_backend == os_crypt::SelectedLinuxBackend::GNOME_KEYRING) {
Christos Froussios70cc95b2017-12-14 21:13:23165 key_storage.reset(new KeyStorageKeyring(config.main_thread_runner));
Christos Froussios4e170cb2017-12-01 09:42:33166 if (key_storage->WaitForInitOnTaskRunner()) {
cfroussiosb013c15b2016-09-03 01:10:16167 VLOG(1) << "OSCrypt using Keyring as backend.";
168 return key_storage;
169 }
Christos Froussiosf7924a62019-11-05 14:26:50170 LOG(WARNING) << "OSCrypt tried Keyring but couldn't initialise.";
cfroussiosb013c15b2016-09-03 01:10:16171 }
slana881a862016-09-09 21:36:07172#endif // defined(USE_KEYRING)
cfroussiosb013c15b2016-09-03 01:10:16173
cfroussios2e6729a42016-07-26 09:18:12174#if defined(USE_KWALLET)
cfroussiosb013c15b2016-09-03 01:10:16175 if (selected_backend == os_crypt::SelectedLinuxBackend::KWALLET ||
176 selected_backend == os_crypt::SelectedLinuxBackend::KWALLET5) {
Christos Froussios494196d2017-07-14 10:10:04177 DCHECK(!config.product_name.empty());
cfroussios2f05d7f2016-08-17 15:58:50178 base::nix::DesktopEnvironment used_desktop_env =
179 selected_backend == os_crypt::SelectedLinuxBackend::KWALLET
180 ? base::nix::DESKTOP_ENVIRONMENT_KDE4
181 : base::nix::DESKTOP_ENVIRONMENT_KDE5;
Christos Froussios70cc95b2017-12-14 21:13:23182 key_storage.reset(
183 new KeyStorageKWallet(used_desktop_env, config.product_name));
Christos Froussios4e170cb2017-12-01 09:42:33184 if (key_storage->WaitForInitOnTaskRunner()) {
cfroussios2e6729a42016-07-26 09:18:12185 VLOG(1) << "OSCrypt using KWallet as backend.";
186 return key_storage;
187 }
Christos Froussiosf7924a62019-11-05 14:26:50188 LOG(WARNING) << "OSCrypt tried KWallet but couldn't initialise.";
cfroussios3ea4c692016-07-18 19:15:14189 }
slana881a862016-09-09 21:36:07190#endif // defined(USE_KWALLET)
cfroussios3b5a4e42016-05-31 11:02:18191
cfroussios3b5a4e42016-05-31 11:02:18192 return nullptr;
193}
Christos Froussios85c4d882020-01-15 10:35:29194#endif // defined(USE_LIBSECRET) || defined(USE_KEYRING) ||
195 // defined(USE_KWALLET)
Christos Froussios985d1aac2017-11-09 11:01:07196
Christos Froussios4e170cb2017-12-01 09:42:33197bool KeyStorageLinux::WaitForInitOnTaskRunner() {
198 base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_sync_primitives;
199 base::SequencedTaskRunner* task_runner = GetTaskRunner();
200
201 // We don't need to change threads if the backend has no preference or if we
202 // are already on the right thread.
203 if (!task_runner || task_runner->RunsTasksInCurrentSequence())
204 return Init();
205
206 base::WaitableEvent initialized(
207 base::WaitableEvent::ResetPolicy::MANUAL,
208 base::WaitableEvent::InitialState::NOT_SIGNALED);
209 bool success;
Christos Froussios57fe5742017-12-07 21:16:49210 task_runner->PostTask(
211 FROM_HERE,
212 base::BindOnce(&KeyStorageLinux::BlockOnInitThenSignal,
213 base::Unretained(this), &initialized, &success));
Christos Froussios4e170cb2017-12-01 09:42:33214 initialized.Wait();
215 return success;
216}
217
Anton Bershanskiy9fa2a352020-05-06 11:38:53218base::Optional<std::string> KeyStorageLinux::GetKey() {
Christos Froussios4e170cb2017-12-01 09:42:33219 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 GetKeyImpl();
226
227 base::WaitableEvent password_loaded(
228 base::WaitableEvent::ResetPolicy::MANUAL,
229 base::WaitableEvent::InitialState::NOT_SIGNALED);
Anton Bershanskiy9fa2a352020-05-06 11:38:53230 base::Optional<std::string> password;
Christos Froussios57fe5742017-12-07 21:16:49231 task_runner->PostTask(
232 FROM_HERE,
233 base::BindOnce(&KeyStorageLinux::BlockOnGetKeyImplThenSignal,
234 base::Unretained(this), &password_loaded, &password));
Christos Froussios4e170cb2017-12-01 09:42:33235 password_loaded.Wait();
236 return password;
237}
238
239base::SequencedTaskRunner* KeyStorageLinux::GetTaskRunner() {
240 return nullptr;
Christos Froussios985d1aac2017-11-09 11:01:07241}
Christos Froussios57fe5742017-12-07 21:16:49242
243void KeyStorageLinux::BlockOnGetKeyImplThenSignal(
244 base::WaitableEvent* on_password_received,
Anton Bershanskiy9fa2a352020-05-06 11:38:53245 base::Optional<std::string>* password) {
Christos Froussios57fe5742017-12-07 21:16:49246 *password = GetKeyImpl();
247 on_password_received->Signal();
248}
249
250void KeyStorageLinux::BlockOnInitThenSignal(base::WaitableEvent* on_inited,
251 bool* success) {
252 *success = Init();
253 on_inited->Signal();
254}