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