blob: 2e388de793af588722c857cd7b896df9734f2b6e [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"
Christos Froussios4e170cb2017-12-01 09:42:3314#include "base/sequenced_task_runner.h"
15#include "base/synchronization/waitable_event.h"
16#include "base/task_runner_util.h"
17#include "base/threading/thread_restrictions.h"
Nico Weber356b3042019-08-23 15:30:4118#include "build/branding_buildflags.h"
Christos Froussios494196d2017-07-14 10:10:0419#include "components/os_crypt/key_storage_config_linux.h"
cfroussios3ea4c692016-07-18 19:15:1420
21#if defined(USE_LIBSECRET)
cfroussios3b5a4e42016-05-31 11:02:1822#include "components/os_crypt/key_storage_libsecret.h"
cfroussios3ea4c692016-07-18 19:15:1423#endif
cfroussiosb013c15b2016-09-03 01:10:1624#if defined(USE_KEYRING)
25#include "components/os_crypt/key_storage_keyring.h"
26#endif
cfroussios2e6729a42016-07-26 09:18:1227#if defined(USE_KWALLET)
28#include "components/os_crypt/key_storage_kwallet.h"
29#endif
30
Nico Weber356b3042019-08-23 15:30:4131#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
cfroussios2e6729a42016-07-26 09:18:1232const char KeyStorageLinux::kFolderName[] = "Chrome Keys";
33const char KeyStorageLinux::kKey[] = "Chrome Safe Storage";
34#else
35const char KeyStorageLinux::kFolderName[] = "Chromium Keys";
36const char KeyStorageLinux::kKey[] = "Chromium Safe Storage";
37#endif
38
Christos Froussiosf7924a62019-11-05 14:26:5039namespace {
40
Christos Froussios85c4d882020-01-15 10:35:2941// Used for metrics. Do not rearrange.
42enum class BackendUsage {
43 // A backend was selected and used.
44 // *_FAILED means the backend was selected but couldn't be used.
45 kDefer = 0,
46 kDeferFailed = 1,
47 kBasicText = 2,
48 kBasicTextFailed = 3,
49 kGnomeAny = 4,
50 kGnomeAnyFailed = 5,
51 kGnomeKeyring = 6,
52 kGnomeKeyringFailed = 7,
53 kGnomeLibsecret = 8,
54 kGnomeLibsecretFailed = 9,
55 kKwallet = 10,
56 kKwalletFailed = 11,
57 kKwallet5 = 12,
58 kKwallet5Failed = 13,
59 kMaxValue = kKwallet5Failed,
60};
61
62constexpr BackendUsage SelectedBackendToMetric(
63 os_crypt::SelectedLinuxBackend selection,
64 bool used) {
65 switch (selection) {
66 case os_crypt::SelectedLinuxBackend::DEFER:
67 return used ? BackendUsage::kDefer : BackendUsage::kDeferFailed;
68 case os_crypt::SelectedLinuxBackend::BASIC_TEXT:
69 return used ? BackendUsage::kBasicText : BackendUsage::kBasicTextFailed;
70 case os_crypt::SelectedLinuxBackend::GNOME_ANY:
71 return used ? BackendUsage::kGnomeAny : BackendUsage::kGnomeAnyFailed;
72 case os_crypt::SelectedLinuxBackend::GNOME_KEYRING:
73 return used ? BackendUsage::kGnomeKeyring
74 : BackendUsage::kGnomeKeyringFailed;
75 case os_crypt::SelectedLinuxBackend::GNOME_LIBSECRET:
76 return used ? BackendUsage::kGnomeLibsecret
77 : BackendUsage::kGnomeLibsecretFailed;
78 case os_crypt::SelectedLinuxBackend::KWALLET:
79 return used ? BackendUsage::kKwallet : BackendUsage::kKwalletFailed;
80 case os_crypt::SelectedLinuxBackend::KWALLET5:
81 return used ? BackendUsage::kKwallet5 : BackendUsage::kKwallet5Failed;
82 }
83 NOTREACHED();
84 return BackendUsage::kDeferFailed;
85}
86
Christos Froussiosf7924a62019-11-05 14:26:5087const char* SelectedLinuxBackendToString(
88 os_crypt::SelectedLinuxBackend selection) {
89 switch (selection) {
90 case os_crypt::SelectedLinuxBackend::DEFER:
91 return "DEFER";
92 case os_crypt::SelectedLinuxBackend::BASIC_TEXT:
93 return "BASIC_TEXT";
94 case os_crypt::SelectedLinuxBackend::GNOME_ANY:
95 return "GNOME_ANY";
96 case os_crypt::SelectedLinuxBackend::GNOME_KEYRING:
97 return "GNOME_KEYRING";
98 case os_crypt::SelectedLinuxBackend::GNOME_LIBSECRET:
99 return "GNOME_LIBSECRET";
100 case os_crypt::SelectedLinuxBackend::KWALLET:
101 return "KWALLET";
102 case os_crypt::SelectedLinuxBackend::KWALLET5:
103 return "KWALLET5";
104 }
105 NOTREACHED();
106 return nullptr;
107}
108
Christos Froussios85c4d882020-01-15 10:35:29109} // namespace
110
cfroussios3ea4c692016-07-18 19:15:14111// static
Christos Froussios494196d2017-07-14 10:10:04112std::unique_ptr<KeyStorageLinux> KeyStorageLinux::CreateService(
113 const os_crypt::Config& config) {
cfroussios2f05d7f2016-08-17 15:58:50114 // Select a backend.
Christos Froussios494196d2017-07-14 10:10:04115 bool use_backend = !config.should_use_preference ||
116 os_crypt::GetBackendUse(config.user_data_path);
cfroussios2f05d7f2016-08-17 15:58:50117 std::unique_ptr<base::Environment> env(base::Environment::Create());
118 base::nix::DesktopEnvironment desktop_env =
119 base::nix::GetDesktopEnvironment(env.get());
120 os_crypt::SelectedLinuxBackend selected_backend =
Christos Froussios494196d2017-07-14 10:10:04121 os_crypt::SelectBackend(config.store, use_backend, desktop_env);
Christos Froussiosf7924a62019-11-05 14:26:50122 VLOG(1) << "Selected backend for OSCrypt: "
123 << SelectedLinuxBackendToString(selected_backend);
cfroussios3b5a4e42016-05-31 11:02:18124
Christos Froussios985d1aac2017-11-09 11:01:07125 // TODO(crbug.com/782851) Schedule the initialisation on each backend's
126 // favourite thread.
127
cfroussios2f05d7f2016-08-17 15:58:50128 // Try initializing the selected backend.
cfroussiosb013c15b2016-09-03 01:10:16129 // In case of GNOME_ANY, prefer Libsecret
cfroussios3ea4c692016-07-18 19:15:14130 std::unique_ptr<KeyStorageLinux> key_storage;
Christos Froussios85c4d882020-01-15 10:35:29131#if defined(USE_LIBSECRET) || defined(USE_KEYRING) || defined(USE_KWALLET)
132 key_storage = CreateServiceInternal(selected_backend, config);
133#endif // defined(USE_LIBSECRET) || defined(USE_KEYRING) ||
134 // defined(USE_KWALLET)
135
136 UMA_HISTOGRAM_ENUMERATION(
137 "OSCrypt.BackendUsage",
138 SelectedBackendToMetric(selected_backend, key_storage != nullptr));
139
140 // Either there are no supported backends on this platform, or we chose to
141 // use no backend, or the chosen backend failed to initialise.
142 VLOG_IF(1, !key_storage) << "OSCrypt did not initialize a backend.";
143 return key_storage;
144}
145
146#if defined(USE_LIBSECRET) || defined(USE_KEYRING) || defined(USE_KWALLET)
147std::unique_ptr<KeyStorageLinux> KeyStorageLinux::CreateServiceInternal(
148 os_crypt::SelectedLinuxBackend selected_backend,
149 const os_crypt::Config& config) {
150 std::unique_ptr<KeyStorageLinux> key_storage;
cfroussiosb013c15b2016-09-03 01:10:16151
152#if defined(USE_LIBSECRET)
cfroussios2f05d7f2016-08-17 15:58:50153 if (selected_backend == os_crypt::SelectedLinuxBackend::GNOME_ANY ||
154 selected_backend == os_crypt::SelectedLinuxBackend::GNOME_LIBSECRET) {
Peter Boström08e7ed82021-04-19 17:49:59155 key_storage = std::make_unique<KeyStorageLibsecret>();
Christos Froussios4e170cb2017-12-01 09:42:33156 if (key_storage->WaitForInitOnTaskRunner()) {
cfroussios3ea4c692016-07-18 19:15:14157 VLOG(1) << "OSCrypt using Libsecret as backend.";
158 return key_storage;
159 }
Christos Froussiosf7924a62019-11-05 14:26:50160 LOG(WARNING) << "OSCrypt tried Libsecret but couldn't initialise.";
cfroussiosb013c15b2016-09-03 01:10:16161 }
slana881a862016-09-09 21:36:07162#endif // defined(USE_LIBSECRET)
cfroussiosb013c15b2016-09-03 01:10:16163
164#if defined(USE_KEYRING)
165 if (selected_backend == os_crypt::SelectedLinuxBackend::GNOME_ANY ||
166 selected_backend == os_crypt::SelectedLinuxBackend::GNOME_KEYRING) {
Peter Boström08e7ed82021-04-19 17:49:59167 key_storage =
168 std::make_unique<KeyStorageKeyring>(config.main_thread_runner);
Christos Froussios4e170cb2017-12-01 09:42:33169 if (key_storage->WaitForInitOnTaskRunner()) {
cfroussiosb013c15b2016-09-03 01:10:16170 VLOG(1) << "OSCrypt using Keyring as backend.";
171 return key_storage;
172 }
Christos Froussiosf7924a62019-11-05 14:26:50173 LOG(WARNING) << "OSCrypt tried Keyring but couldn't initialise.";
cfroussiosb013c15b2016-09-03 01:10:16174 }
slana881a862016-09-09 21:36:07175#endif // defined(USE_KEYRING)
cfroussiosb013c15b2016-09-03 01:10:16176
cfroussios2e6729a42016-07-26 09:18:12177#if defined(USE_KWALLET)
cfroussiosb013c15b2016-09-03 01:10:16178 if (selected_backend == os_crypt::SelectedLinuxBackend::KWALLET ||
179 selected_backend == os_crypt::SelectedLinuxBackend::KWALLET5) {
Christos Froussios494196d2017-07-14 10:10:04180 DCHECK(!config.product_name.empty());
cfroussios2f05d7f2016-08-17 15:58:50181 base::nix::DesktopEnvironment used_desktop_env =
182 selected_backend == os_crypt::SelectedLinuxBackend::KWALLET
183 ? base::nix::DESKTOP_ENVIRONMENT_KDE4
184 : base::nix::DESKTOP_ENVIRONMENT_KDE5;
Peter Boström08e7ed82021-04-19 17:49:59185 key_storage = std::make_unique<KeyStorageKWallet>(used_desktop_env,
186 config.product_name);
Christos Froussios4e170cb2017-12-01 09:42:33187 if (key_storage->WaitForInitOnTaskRunner()) {
cfroussios2e6729a42016-07-26 09:18:12188 VLOG(1) << "OSCrypt using KWallet as backend.";
189 return key_storage;
190 }
Christos Froussiosf7924a62019-11-05 14:26:50191 LOG(WARNING) << "OSCrypt tried KWallet but couldn't initialise.";
cfroussios3ea4c692016-07-18 19:15:14192 }
slana881a862016-09-09 21:36:07193#endif // defined(USE_KWALLET)
cfroussios3b5a4e42016-05-31 11:02:18194
cfroussios3b5a4e42016-05-31 11:02:18195 return nullptr;
196}
Christos Froussios85c4d882020-01-15 10:35:29197#endif // defined(USE_LIBSECRET) || defined(USE_KEYRING) ||
198 // defined(USE_KWALLET)
Christos Froussios985d1aac2017-11-09 11:01:07199
Christos Froussios4e170cb2017-12-01 09:42:33200bool KeyStorageLinux::WaitForInitOnTaskRunner() {
201 base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_sync_primitives;
202 base::SequencedTaskRunner* task_runner = GetTaskRunner();
203
204 // We don't need to change threads if the backend has no preference or if we
205 // are already on the right thread.
206 if (!task_runner || task_runner->RunsTasksInCurrentSequence())
207 return Init();
208
209 base::WaitableEvent initialized(
210 base::WaitableEvent::ResetPolicy::MANUAL,
211 base::WaitableEvent::InitialState::NOT_SIGNALED);
212 bool success;
Christos Froussios57fe5742017-12-07 21:16:49213 task_runner->PostTask(
214 FROM_HERE,
215 base::BindOnce(&KeyStorageLinux::BlockOnInitThenSignal,
216 base::Unretained(this), &initialized, &success));
Christos Froussios4e170cb2017-12-01 09:42:33217 initialized.Wait();
218 return success;
219}
220
Anton Bershanskiy9fa2a352020-05-06 11:38:53221base::Optional<std::string> KeyStorageLinux::GetKey() {
Christos Froussios4e170cb2017-12-01 09:42:33222 base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_sync_primitives;
223 base::SequencedTaskRunner* task_runner = GetTaskRunner();
224
225 // We don't need to change threads if the backend has no preference or if we
226 // are already on the right thread.
227 if (!task_runner || task_runner->RunsTasksInCurrentSequence())
228 return GetKeyImpl();
229
230 base::WaitableEvent password_loaded(
231 base::WaitableEvent::ResetPolicy::MANUAL,
232 base::WaitableEvent::InitialState::NOT_SIGNALED);
Anton Bershanskiy9fa2a352020-05-06 11:38:53233 base::Optional<std::string> password;
Christos Froussios57fe5742017-12-07 21:16:49234 task_runner->PostTask(
235 FROM_HERE,
236 base::BindOnce(&KeyStorageLinux::BlockOnGetKeyImplThenSignal,
237 base::Unretained(this), &password_loaded, &password));
Christos Froussios4e170cb2017-12-01 09:42:33238 password_loaded.Wait();
239 return password;
240}
241
242base::SequencedTaskRunner* KeyStorageLinux::GetTaskRunner() {
243 return nullptr;
Christos Froussios985d1aac2017-11-09 11:01:07244}
Christos Froussios57fe5742017-12-07 21:16:49245
246void KeyStorageLinux::BlockOnGetKeyImplThenSignal(
247 base::WaitableEvent* on_password_received,
Anton Bershanskiy9fa2a352020-05-06 11:38:53248 base::Optional<std::string>* password) {
Christos Froussios57fe5742017-12-07 21:16:49249 *password = GetKeyImpl();
250 on_password_received->Signal();
251}
252
253void KeyStorageLinux::BlockOnInitThenSignal(base::WaitableEvent* on_inited,
254 bool* success) {
255 *success = Init();
256 on_inited->Signal();
257}