Bug 1928997: Update tabs icon in Unified Search popup r=desktop-theme-reviewers,daleh...
[gecko.git] / security / manager / ssl / LibSecret.cpp
blobbed7c7a08e9e74647b8d045c341b8551f5e4b6a0
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "LibSecret.h"
9 #include <gio/gio.h>
10 #include <gmodule.h>
11 #include <memory>
13 #include "mozilla/Base64.h"
14 #include "mozilla/GUniquePtr.h"
15 #include "mozilla/Logging.h"
16 #include "MainThreadUtils.h"
17 #include "prlink.h"
19 // This is the implementation of LibSecret, an instantiation of OSKeyStore for
20 // Linux.
22 using namespace mozilla;
24 LazyLogModule gLibSecretLog("libsecret");
26 static PRLibrary* libsecret = nullptr;
28 typedef enum {
29 SECRET_SCHEMA_NONE = 0,
30 SECRET_SCHEMA_DONT_MATCH_NAME = 1 << 1
31 } SecretSchemaFlags;
33 typedef enum {
34 SECRET_SCHEMA_ATTRIBUTE_STRING = 0,
35 SECRET_SCHEMA_ATTRIBUTE_INTEGER = 1,
36 SECRET_SCHEMA_ATTRIBUTE_BOOLEAN = 2,
37 } SecretSchemaAttributeType;
39 typedef struct {
40 const gchar* name;
41 SecretSchemaAttributeType type;
42 } SecretSchemaAttribute;
44 typedef struct {
45 const gchar* name;
46 SecretSchemaFlags flags;
47 SecretSchemaAttribute attributes[32];
49 /* <private> */
50 gint reserved;
51 gpointer reserved1;
52 gpointer reserved2;
53 gpointer reserved3;
54 gpointer reserved4;
55 gpointer reserved5;
56 gpointer reserved6;
57 gpointer reserved7;
58 } SecretSchema;
60 typedef enum {
61 SECRET_ERROR_PROTOCOL = 1,
62 SECRET_ERROR_IS_LOCKED = 2,
63 SECRET_ERROR_NO_SUCH_OBJECT = 3,
64 SECRET_ERROR_ALREADY_EXISTS = 4,
65 } SecretError;
67 #define SECRET_COLLECTION_DEFAULT "default"
69 typedef gboolean (*secret_password_clear_sync_fn)(const SecretSchema*,
70 GCancellable*, GError**, ...);
71 typedef gchar* (*secret_password_lookup_sync_fn)(const SecretSchema*,
72 GCancellable*, GError**, ...);
73 typedef gboolean (*secret_password_store_sync_fn)(const SecretSchema*,
74 const gchar*, const gchar*,
75 const gchar*, GCancellable*,
76 GError**, ...);
77 typedef void (*secret_password_free_fn)(const gchar*);
78 typedef GQuark (*secret_error_get_quark_fn)();
80 static secret_password_clear_sync_fn secret_password_clear_sync = nullptr;
81 static secret_password_lookup_sync_fn secret_password_lookup_sync = nullptr;
82 static secret_password_store_sync_fn secret_password_store_sync = nullptr;
83 static secret_password_free_fn secret_password_free = nullptr;
84 static secret_error_get_quark_fn secret_error_get_quark = nullptr;
86 nsresult MaybeLoadLibSecret() {
87 MOZ_ASSERT(NS_IsMainThread());
88 if (!NS_IsMainThread()) {
89 return NS_ERROR_NOT_SAME_THREAD;
92 if (!libsecret) {
93 libsecret = PR_LoadLibrary("libsecret-1.so.0");
94 if (!libsecret) {
95 return NS_ERROR_NOT_AVAILABLE;
98 // With TSan, we cannot unload libsecret once we have loaded it because
99 // TSan does not support unloading libraries that are matched from its
100 // suppression list. Hence we just keep the library loaded in TSan builds.
101 #ifdef MOZ_TSAN
102 # define UNLOAD_LIBSECRET(x) \
103 do { \
104 } while (0)
105 #else
106 # define UNLOAD_LIBSECRET(x) PR_UnloadLibrary(x)
107 #endif
109 #define FIND_FUNCTION_SYMBOL(function) \
110 function = (function##_fn)PR_FindFunctionSymbol(libsecret, #function); \
111 if (!(function)) { \
112 UNLOAD_LIBSECRET(libsecret); \
113 libsecret = nullptr; \
114 return NS_ERROR_NOT_AVAILABLE; \
116 FIND_FUNCTION_SYMBOL(secret_password_clear_sync);
117 FIND_FUNCTION_SYMBOL(secret_password_lookup_sync);
118 FIND_FUNCTION_SYMBOL(secret_password_store_sync);
119 FIND_FUNCTION_SYMBOL(secret_password_free);
120 FIND_FUNCTION_SYMBOL(secret_error_get_quark);
121 #undef FIND_FUNCTION_SYMBOL
124 return NS_OK;
127 struct ScopedDelete {
128 void operator()(char* val) {
129 if (val) secret_password_free(val);
133 template <class T>
134 struct ScopedMaybeDelete {
135 void operator()(T* ptr) {
136 if (ptr) {
137 ScopedDelete del;
138 del(ptr);
143 typedef std::unique_ptr<char, ScopedMaybeDelete<char>> ScopedPassword;
145 LibSecret::LibSecret() = default;
147 LibSecret::~LibSecret() {
148 MOZ_ASSERT(NS_IsMainThread());
149 if (!NS_IsMainThread()) {
150 return;
152 if (libsecret) {
153 secret_password_clear_sync = nullptr;
154 secret_password_lookup_sync = nullptr;
155 secret_password_store_sync = nullptr;
156 secret_password_free = nullptr;
157 secret_error_get_quark = nullptr;
158 UNLOAD_LIBSECRET(libsecret);
159 libsecret = nullptr;
163 static const SecretSchema kSchema = {
164 "mozilla.firefox",
165 SECRET_SCHEMA_NONE,
166 {{"string", SECRET_SCHEMA_ATTRIBUTE_STRING}, /* the label */
167 {"NULL", SECRET_SCHEMA_ATTRIBUTE_STRING}}};
169 nsresult LibSecret::StoreSecret(const nsACString& aSecret,
170 const nsACString& aLabel) {
171 MOZ_ASSERT(secret_password_store_sync);
172 if (!secret_password_store_sync) {
173 return NS_ERROR_FAILURE;
175 // libsecret expects a null-terminated string, so to be safe we store the
176 // secret (which could be arbitrary bytes) base64-encoded.
177 nsAutoCString base64;
178 nsresult rv = Base64Encode(aSecret, base64);
179 if (NS_FAILED(rv)) {
180 MOZ_LOG(gLibSecretLog, LogLevel::Debug, ("Error base64-encoding secret"));
181 return rv;
183 GUniquePtr<GError> error;
184 bool stored = secret_password_store_sync(
185 &kSchema, SECRET_COLLECTION_DEFAULT, PromiseFlatCString(aLabel).get(),
186 PromiseFlatCString(base64).get(),
187 nullptr, // GCancellable
188 getter_Transfers(error), "string", PromiseFlatCString(aLabel).get(),
189 nullptr);
190 if (error) {
191 MOZ_LOG(gLibSecretLog, LogLevel::Debug, ("Error storing secret"));
192 return NS_ERROR_FAILURE;
195 return stored ? NS_OK : NS_ERROR_FAILURE;
198 nsresult LibSecret::DeleteSecret(const nsACString& aLabel) {
199 MOZ_ASSERT(secret_password_clear_sync && secret_error_get_quark);
200 if (!secret_password_clear_sync || !secret_error_get_quark) {
201 return NS_ERROR_FAILURE;
203 GUniquePtr<GError> error;
204 Unused << secret_password_clear_sync(&kSchema,
205 nullptr, // GCancellable
206 getter_Transfers(error), "string",
207 PromiseFlatCString(aLabel).get(),
208 nullptr);
209 if (error && !(error->domain == secret_error_get_quark() &&
210 error->code == SECRET_ERROR_NO_SUCH_OBJECT)) {
211 MOZ_LOG(gLibSecretLog, LogLevel::Debug, ("Error deleting secret"));
212 return NS_ERROR_FAILURE;
215 return NS_OK;
218 nsresult LibSecret::RetrieveSecret(const nsACString& aLabel,
219 /* out */ nsACString& aSecret) {
220 MOZ_ASSERT(secret_password_lookup_sync && secret_password_free);
221 if (!secret_password_lookup_sync || !secret_password_free) {
222 return NS_ERROR_FAILURE;
224 GUniquePtr<GError> error;
225 aSecret.Truncate();
226 ScopedPassword s(
227 secret_password_lookup_sync(&kSchema,
228 nullptr, // GCancellable
229 getter_Transfers(error), "string",
230 PromiseFlatCString(aLabel).get(), nullptr));
231 if (error || !s) {
232 MOZ_LOG(gLibSecretLog, LogLevel::Debug,
233 ("Error retrieving secret or didn't find it"));
234 return NS_ERROR_FAILURE;
236 // libsecret expects a null-terminated string, so to be safe we store the
237 // secret (which could be arbitrary bytes) base64-encoded, which means we have
238 // to base64-decode it here.
239 nsAutoCString base64Encoded(s.get());
240 nsresult rv = Base64Decode(base64Encoded, aSecret);
241 if (NS_FAILED(rv)) {
242 MOZ_LOG(gLibSecretLog, LogLevel::Debug, ("Error base64-decoding secret"));
243 return rv;
246 return NS_OK;