1 /* libsecret - GLib wrapper for Secret Service
2 *
3 * Copyright 2019 Red Hat, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published
7 * by the Free Software Foundation; either version 2.1 of the licence or (at
8 * your option) any later version.
9 *
10 * See the included COPYING file for more information.
11 *
12 * Author: Daiki Ueno
13 */
14
15 #include "config.h"
16
17 #include "secret-backend.h"
18
19 #ifdef WITH_GCRYPT
20 #include "secret-file-backend.h"
21 #endif
22
23 #include "secret-private.h"
24
25 #include "libsecret/secret-enum-types.h"
26
27 /**
28 * SECTION:secret-backend
29 * @title: SecretBackend
30 * @short_description: A backend implementation of password storage
31 *
32 * #SecretBackend represents a backend implementation of password
33 * storage.
34 *
35 * Stability: Stable
36 */
37
38 /**
39 * SecretBackend:
40 *
41 * An object representing a backend implementation of password storage.
42 *
43 * Since: 0.19.0
44 */
45
46 /**
47 * SecretBackendInterface:
48 * @parent_iface: the parent interface
49 * @ensure_for_flags: implementation of reinitialization step in constructor, optional
50 * @ensure_for_flags_finish: implementation of reinitialization step in constructor, optional
51 * @store: implementation of secret_password_store(), required
52 * @store_finish: implementation of secret_password_store_finish(), required
53 * @lookup: implementation of secret_password_lookup(), required
54 * @lookup_finish: implementation of secret_password_lookup_finish(), required
55 * @clear: implementation of secret_password_clear(), required
56 * @clear_finish: implementation of secret_password_clear_finish(), required
57 * @search: implementation of secret_password_search(), required
58 * @search_finish: implementation of secret_password_search_finish(), required
59 *
60 * The interface for #SecretBackend.
61 *
62 * Since: 0.19.0
63 */
64
65 /**
66 * SecretBackendFlags:
67 * @SECRET_BACKEND_NONE: no flags for initializing the #SecretBackend
68 * @SECRET_BACKEND_OPEN_SESSION: establish a session for transfer of secrets
69 * while initializing the #SecretBackend
70 * @SECRET_BACKEND_LOAD_COLLECTIONS: load collections while initializing the
71 * #SecretBackend
72 *
73 * Flags which determine which parts of the #SecretBackend are initialized.
74 *
75 * Since: 0.19.0
76 */
77
78 G_DEFINE_INTERFACE_WITH_CODE (SecretBackend, secret_backend, G_TYPE_OBJECT,
79 g_type_interface_add_prerequisite(g_define_type_id, G_TYPE_ASYNC_INITABLE);
80 );
81
82 static void
secret_backend_default_init(SecretBackendInterface * iface)83 secret_backend_default_init (SecretBackendInterface *iface)
84 {
85 /**
86 * SecretBackend:flags:
87 *
88 * A set of flags describing which parts of the secret backend have
89 * been initialized.
90 *
91 * Since: 0.19.0
92 */
93 g_object_interface_install_property (iface,
94 g_param_spec_flags ("flags", "Flags", "Service flags",
95 secret_service_flags_get_type (), SECRET_SERVICE_NONE,
96 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
97 }
98
99 void
_secret_backend_ensure_extension_point(void)100 _secret_backend_ensure_extension_point (void)
101 {
102 GIOExtensionPoint *ep;
103 static gboolean registered = FALSE;
104
105 if (registered)
106 return;
107
108 ep = g_io_extension_point_register (SECRET_BACKEND_EXTENSION_POINT_NAME);
109 g_io_extension_point_set_required_type (ep, SECRET_TYPE_BACKEND);
110
111 registered = TRUE;
112 }
113
114 G_LOCK_DEFINE (backend_instance);
115 static gpointer backend_instance = NULL;
116
117 static SecretBackend *
backend_get_instance(void)118 backend_get_instance (void)
119 {
120 SecretBackend *instance = NULL;
121
122 G_LOCK (backend_instance);
123 if (backend_instance != NULL)
124 instance = g_object_ref (backend_instance);
125 G_UNLOCK (backend_instance);
126
127 return instance;
128 }
129
130 void
_secret_backend_uncache_instance(void)131 _secret_backend_uncache_instance (void)
132 {
133 SecretBackend *instance = NULL;
134
135 G_LOCK (backend_instance);
136 instance = backend_instance;
137 backend_instance = NULL;
138 G_UNLOCK (backend_instance);
139
140 if (instance != NULL)
141 g_object_unref (instance);
142 }
143
144 static GType
backend_get_impl_type(void)145 backend_get_impl_type (void)
146 {
147 const gchar *envvar;
148 const gchar *extension_name;
149 GIOExtension *e;
150 GIOExtensionPoint *ep;
151
152 g_type_ensure (secret_service_get_type ());
153 #ifdef WITH_GCRYPT
154 g_type_ensure (secret_file_backend_get_type ());
155 #endif
156
157 #ifdef WITH_GCRYPT
158 if (g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS) &&
159 _secret_file_backend_check_portal_version ())
160 extension_name = "file";
161 else
162 #endif
163 {
164 envvar = g_getenv ("SECRET_BACKEND");
165 if (envvar == NULL || *envvar == '\0')
166 extension_name = "service";
167 else
168 extension_name = envvar;
169 }
170
171 ep = g_io_extension_point_lookup (SECRET_BACKEND_EXTENSION_POINT_NAME);
172 e = g_io_extension_point_get_extension_by_name (ep, extension_name);
173 if (e == NULL) {
174 g_warning ("Backend extension \"%s\" from SECRET_BACKEND_EXTENSION_POINT_NAME environment variable not found.", extension_name);
175 return G_TYPE_NONE;
176 }
177
178 return g_io_extension_get_type (e);
179 }
180
181 static void
on_ensure_for_flags(GObject * source_object,GAsyncResult * result,gpointer user_data)182 on_ensure_for_flags (GObject *source_object,
183 GAsyncResult *result,
184 gpointer user_data)
185 {
186 SecretBackendInterface *iface;
187 SecretBackend *self = SECRET_BACKEND (source_object);
188 GTask *task = G_TASK (user_data);
189 GError *error = NULL;
190
191 iface = SECRET_BACKEND_GET_IFACE (self);
192 if (iface->ensure_for_flags_finish) {
193 if (!iface->ensure_for_flags_finish (self, result, &error)) {
194 g_task_return_error (task, error);
195 g_object_unref (task);
196 return;
197 }
198 }
199
200 g_task_return_boolean (task, TRUE);
201 g_object_unref (task);
202 }
203
204 /**
205 * secret_backend_get:
206 * @flags: flags for which service functionality to ensure is initialized
207 * @cancellable: optional cancellation object
208 * @callback: called when the operation completes
209 * @user_data: data to be passed to the callback
210 *
211 * Get a #SecretBackend instance. If such a backend already exists,
212 * then the same backend is returned.
213 *
214 * If @flags contains any flags of which parts of the secret backend to
215 * ensure are initialized, then those will be initialized before completing.
216 *
217 * This method will return immediately and complete asynchronously.
218 *
219 * Since: 0.19.0
220 */
221 void
secret_backend_get(SecretBackendFlags flags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)222 secret_backend_get (SecretBackendFlags flags,
223 GCancellable *cancellable,
224 GAsyncReadyCallback callback,
225 gpointer user_data)
226 {
227 SecretBackend *backend = NULL;
228 SecretBackendInterface *iface;
229 GTask *task;
230
231 backend = backend_get_instance ();
232
233 /* Create a whole new backend */
234 if (backend == NULL) {
235 GType impl_type = backend_get_impl_type ();
236 g_return_if_fail (g_type_is_a (impl_type, G_TYPE_ASYNC_INITABLE));
237 g_async_initable_new_async (impl_type,
238 G_PRIORITY_DEFAULT,
239 cancellable, callback, user_data,
240 "flags", flags,
241 NULL);
242
243 /* Just have to ensure that the backend matches flags */
244 } else {
245 task = g_task_new (backend, cancellable, callback, user_data);
246 iface = SECRET_BACKEND_GET_IFACE (backend);
247 if (iface->ensure_for_flags) {
248 g_task_set_source_tag (task, secret_backend_get);
249 iface->ensure_for_flags (backend, flags, cancellable,
250 on_ensure_for_flags, task);
251 } else {
252 g_task_return_boolean (task, TRUE);
253 g_object_unref (task);
254 }
255 g_object_unref (backend);
256 }
257 }
258
259 /**
260 * secret_backend_get_finish:
261 * @result: the asynchronous result passed to the callback
262 * @error: location to place an error on failure
263 *
264 * Complete an asynchronous operation to get a #SecretBackend.
265 *
266 * Returns: (transfer full): a new reference to a #SecretBackend proxy, which
267 * should be released with g_object_unref().
268 *
269 * Since: 0.19.0
270 */
271 SecretBackend *
secret_backend_get_finish(GAsyncResult * result,GError ** error)272 secret_backend_get_finish (GAsyncResult *result,
273 GError **error)
274 {
275 GTask *task;
276 GObject *backend = NULL;
277 GObject *source_object;
278
279 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
280 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
281
282 task = G_TASK (result);
283 source_object = g_task_get_source_object (task);
284
285 g_return_val_if_fail (g_task_is_valid (result, source_object), NULL);
286
287 /* Just ensuring that the backend matches flags */
288 if (g_task_get_source_tag (task) == secret_backend_get) {
289 if (g_task_had_error (task)) {
290 g_task_propagate_pointer (task, error);
291 } else {
292 backend = g_object_ref (source_object);
293 }
294
295 /* Creating a whole new backend */
296 } else {
297 backend = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), result, error);
298 if (backend) {
299 G_LOCK (backend_instance);
300 if (backend_instance == NULL)
301 backend_instance = backend;
302 G_UNLOCK (backend_instance);
303 }
304 }
305
306 if (backend == NULL)
307 return NULL;
308
309 return SECRET_BACKEND (backend);
310 }
311