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/. */
13 #include "mozilla/Base64.h"
14 #include "mozilla/GUniquePtr.h"
15 #include "mozilla/Logging.h"
16 #include "MainThreadUtils.h"
19 // This is the implementation of LibSecret, an instantiation of OSKeyStore for
22 using namespace mozilla
;
24 LazyLogModule
gLibSecretLog("libsecret");
26 static PRLibrary
* libsecret
= nullptr;
29 SECRET_SCHEMA_NONE
= 0,
30 SECRET_SCHEMA_DONT_MATCH_NAME
= 1 << 1
34 SECRET_SCHEMA_ATTRIBUTE_STRING
= 0,
35 SECRET_SCHEMA_ATTRIBUTE_INTEGER
= 1,
36 SECRET_SCHEMA_ATTRIBUTE_BOOLEAN
= 2,
37 } SecretSchemaAttributeType
;
41 SecretSchemaAttributeType type
;
42 } SecretSchemaAttribute
;
46 SecretSchemaFlags flags
;
47 SecretSchemaAttribute attributes
[32];
61 SECRET_ERROR_PROTOCOL
= 1,
62 SECRET_ERROR_IS_LOCKED
= 2,
63 SECRET_ERROR_NO_SUCH_OBJECT
= 3,
64 SECRET_ERROR_ALREADY_EXISTS
= 4,
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
*,
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
;
93 libsecret
= PR_LoadLibrary("libsecret-1.so.0");
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.
102 # define UNLOAD_LIBSECRET(x) \
106 # define UNLOAD_LIBSECRET(x) PR_UnloadLibrary(x)
109 #define FIND_FUNCTION_SYMBOL(function) \
110 function = (function##_fn)PR_FindFunctionSymbol(libsecret, #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
127 struct ScopedDelete
{
128 void operator()(char* val
) {
129 if (val
) secret_password_free(val
);
134 struct ScopedMaybeDelete
{
135 void operator()(T
* 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()) {
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
);
163 static const SecretSchema kSchema
= {
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
);
180 MOZ_LOG(gLibSecretLog
, LogLevel::Debug
, ("Error base64-encoding secret"));
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(),
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(),
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
;
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
;
227 secret_password_lookup_sync(&kSchema
,
228 nullptr, // GCancellable
229 getter_Transfers(error
), "string",
230 PromiseFlatCString(aLabel
).get(), nullptr));
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
);
242 MOZ_LOG(gLibSecretLog
, LogLevel::Debug
, ("Error base64-decoding secret"));