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 : : #include "secret-file-backend.h"
19 : : #include "secret-file-collection.h"
20 : : #include "secret-file-item.h"
21 : : #include "secret-private.h"
22 : : #include "secret-retrievable.h"
23 : :
24 : : #include "egg/egg-secure-memory.h"
25 : : #include "egg/egg-tpm2.h"
26 : :
27 : 0 : EGG_SECURE_DECLARE (secret_file_backend);
28 : :
29 : : #include <gio/gunixfdlist.h>
30 : : #include <gio/gunixinputstream.h>
31 : : #include <glib-unix.h>
32 : :
33 : : #define PORTAL_BUS_NAME "org.freedesktop.portal.Desktop"
34 : : #define PORTAL_OBJECT_PATH "/org/freedesktop/portal/desktop"
35 : : #define PORTAL_REQUEST_INTERFACE "org.freedesktop.portal.Request"
36 : : #define PORTAL_SECRET_INTERFACE "org.freedesktop.portal.Secret"
37 : : #define PORTAL_SECRET_VERSION 1
38 : :
39 : : static void secret_file_backend_async_initable_iface (GAsyncInitableIface *iface);
40 : : static void secret_file_backend_backend_iface (SecretBackendInterface *iface);
41 : :
42 : : struct _SecretFileBackend {
43 : : GObject parent;
44 : : SecretFileCollection *collection;
45 : : SecretServiceFlags init_flags;
46 : : };
47 : :
48 [ + + + - : 88 : G_DEFINE_TYPE_WITH_CODE (SecretFileBackend, secret_file_backend, G_TYPE_OBJECT,
+ + ]
49 : : G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, secret_file_backend_async_initable_iface);
50 : : G_IMPLEMENT_INTERFACE (SECRET_TYPE_BACKEND, secret_file_backend_backend_iface);
51 : : _secret_backend_ensure_extension_point ();
52 : : g_io_extension_point_implement (SECRET_BACKEND_EXTENSION_POINT_NAME,
53 : : g_define_type_id,
54 : : "file",
55 : : 0)
56 : : );
57 : :
58 : : enum {
59 : : PROP_0,
60 : : PROP_FLAGS
61 : : };
62 : :
63 : : /* Gets the GFile for this backend and makes sure the parent dirs exist */
64 : : static GFile *
65 : 12 : get_secret_file (GCancellable *cancellable, GError **error)
66 : : {
67 : 12 : const char *envvar = NULL;
68 : 12 : char *path = NULL;
69 : 12 : GFile *file = NULL;
70 : 12 : GFile *dir = NULL;
71 : : gboolean ret;
72 : :
73 : 12 : envvar = g_getenv ("SECRET_FILE_TEST_PATH");
74 [ + - + - ]: 12 : if (envvar != NULL && *envvar != '\0') {
75 : 12 : path = g_strdup (envvar);
76 : : } else {
77 : 0 : path = g_build_filename (g_get_user_data_dir (),
78 : : "keyrings",
79 : : SECRET_COLLECTION_DEFAULT ".keyring",
80 : : NULL);
81 : : }
82 : :
83 : 12 : file = g_file_new_for_path (path);
84 : 12 : g_free (path);
85 : :
86 : 12 : dir = g_file_get_parent (file);
87 [ - + ]: 12 : if (dir == NULL) {
88 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
89 : : "not a valid path");
90 : 0 : g_object_unref (file);
91 : 0 : return NULL;
92 : : }
93 : :
94 : 12 : ret = g_file_make_directory_with_parents (dir, cancellable, error);
95 : 12 : g_object_unref (dir);
96 [ + - ]: 12 : if (!ret) {
97 [ - + ]: 12 : if (!g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
98 : 0 : g_object_unref (file);
99 : 0 : return NULL;
100 : : }
101 : :
102 : 12 : g_clear_error (error);
103 : : }
104 : :
105 : 12 : return file;
106 : : }
107 : :
108 : : static void
109 : 12 : secret_file_backend_init (SecretFileBackend *self)
110 : : {
111 : 12 : }
112 : :
113 : : static void
114 : 12 : secret_file_backend_set_property (GObject *object,
115 : : guint prop_id,
116 : : const GValue *value,
117 : : GParamSpec *pspec)
118 : : {
119 : 12 : SecretFileBackend *self = SECRET_FILE_BACKEND (object);
120 : :
121 [ + - ]: 12 : switch (prop_id) {
122 : 12 : case PROP_FLAGS:
123 : 12 : self->init_flags = g_value_get_flags (value);
124 : 12 : break;
125 : 0 : default:
126 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
127 : 0 : break;
128 : : }
129 : 12 : }
130 : :
131 : : static void
132 : 0 : secret_file_backend_get_property (GObject *object,
133 : : guint prop_id,
134 : : GValue *value,
135 : : GParamSpec *pspec)
136 : : {
137 : 0 : SecretFileBackend *self = SECRET_FILE_BACKEND (object);
138 : :
139 [ # # ]: 0 : switch (prop_id) {
140 : 0 : case PROP_FLAGS:
141 : 0 : g_value_set_flags (value, self->init_flags);
142 : 0 : break;
143 : 0 : default:
144 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
145 : 0 : break;
146 : : }
147 : 0 : }
148 : :
149 : : static void
150 : 0 : secret_file_backend_finalize (GObject *object)
151 : : {
152 : 0 : SecretFileBackend *self = SECRET_FILE_BACKEND (object);
153 : :
154 [ # # ]: 0 : g_clear_object (&self->collection);
155 : :
156 : 0 : G_OBJECT_CLASS (secret_file_backend_parent_class)->finalize (object);
157 : 0 : }
158 : :
159 : : static void
160 : 12 : secret_file_backend_class_init (SecretFileBackendClass *klass)
161 : : {
162 : 12 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
163 : :
164 : 12 : object_class->set_property = secret_file_backend_set_property;
165 : 12 : object_class->get_property = secret_file_backend_get_property;
166 : 12 : object_class->finalize = secret_file_backend_finalize;
167 : :
168 : : /**
169 : : * SecretFileBackend:flags:
170 : : *
171 : : * A set of flags describing which parts of the secret file have
172 : : * been initialized.
173 : : */
174 : 12 : g_object_class_override_property (object_class, PROP_FLAGS, "flags");
175 : 12 : }
176 : :
177 : : static void
178 : 12 : on_collection_new_async (GObject *source_object,
179 : : GAsyncResult *result,
180 : : gpointer user_data)
181 : : {
182 : 12 : GTask *task = G_TASK (user_data);
183 : 12 : SecretFileBackend *self = g_task_get_source_object (task);
184 : : GObject *object;
185 : 12 : GError *error = NULL;
186 : :
187 : 12 : object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
188 : : result,
189 : : &error);
190 [ - + ]: 12 : if (object == NULL) {
191 : 0 : g_task_return_error (task, error);
192 : 0 : g_object_unref (task);
193 : 0 : return;
194 : : }
195 : :
196 : 12 : self->collection = SECRET_FILE_COLLECTION (object);
197 : 12 : g_task_return_boolean (task, TRUE);
198 : 12 : g_object_unref (task);
199 : : }
200 : :
201 : : typedef struct {
202 : : gint io_priority;
203 : : GFile *file;
204 : : GInputStream *stream;
205 : : gchar *buffer;
206 : : GDBusConnection *connection;
207 : : gchar *request_path;
208 : : guint portal_signal_id;
209 : : GCancellable *cancellable;
210 : : gulong cancellable_signal_id;
211 : : } InitClosure;
212 : :
213 : : static void
214 : 0 : init_closure_free (gpointer data)
215 : : {
216 : 0 : InitClosure *init = data;
217 : 0 : g_object_unref (init->file);
218 [ # # ]: 0 : g_clear_object (&init->stream);
219 [ # # ]: 0 : g_clear_pointer (&init->buffer, egg_secure_free);
220 [ # # ]: 0 : g_clear_object (&init->connection);
221 [ # # ]: 0 : g_clear_pointer (&init->request_path, g_free);
222 [ # # ]: 0 : if (init->cancellable_signal_id) {
223 : 0 : g_cancellable_disconnect (init->cancellable, init->cancellable_signal_id);
224 : 0 : init->cancellable_signal_id = 0;
225 : : }
226 : : /* Note: do not cancel the cancellable here! That's for the API user to
227 : : * do. We're just keeping track of it here so we can disconnect.
228 : : */
229 [ # # ]: 0 : g_clear_object (&init->cancellable);
230 : 0 : g_free (init);
231 : 0 : }
232 : :
233 : : #define PASSWORD_SIZE 64
234 : :
235 : : static void
236 : 0 : on_read_all (GObject *source_object,
237 : : GAsyncResult *result,
238 : : gpointer user_data)
239 : : {
240 : 0 : GInputStream *stream = G_INPUT_STREAM (source_object);
241 : 0 : GTask *task = G_TASK (user_data);
242 : 0 : InitClosure *init = g_task_get_task_data (task);
243 : : gsize bytes_read;
244 : : SecretValue *password;
245 : 0 : GError *error = NULL;
246 : :
247 [ # # ]: 0 : if (!g_input_stream_read_all_finish (stream, result, &bytes_read,
248 : : &error)) {
249 : 0 : g_task_return_error (task, error);
250 : 0 : g_object_unref (task);
251 : 0 : return;
252 : : }
253 : :
254 [ # # ]: 0 : if (bytes_read != PASSWORD_SIZE) {
255 : 0 : g_task_return_new_error (task,
256 : : SECRET_ERROR,
257 : : SECRET_ERROR_PROTOCOL,
258 : : "invalid password returned from portal");
259 : 0 : g_object_unref (task);
260 : 0 : return;
261 : : }
262 : :
263 : 0 : password = secret_value_new (init->buffer, bytes_read, "text/plain");
264 : 0 : g_async_initable_new_async (SECRET_TYPE_FILE_COLLECTION,
265 : : init->io_priority,
266 : : g_task_get_cancellable (task),
267 : : on_collection_new_async,
268 : : task,
269 : 0 : "file", g_object_ref (init->file),
270 : : "password", password,
271 : : NULL);
272 : 0 : g_object_unref (init->file);
273 : 0 : secret_value_unref (password);
274 : : }
275 : :
276 : : static void
277 : 0 : on_portal_response (GDBusConnection *connection,
278 : : const gchar *sender_name,
279 : : const gchar *object_path,
280 : : const gchar *interface_name,
281 : : const gchar *signal_name,
282 : : GVariant *parameters,
283 : : gpointer user_data)
284 : : {
285 : 0 : GTask *task = G_TASK (user_data);
286 : 0 : InitClosure *init = g_task_get_task_data (task);
287 : : guint32 response;
288 : :
289 [ # # ]: 0 : if (init->cancellable_signal_id) {
290 : 0 : g_cancellable_disconnect (g_task_get_cancellable (task), init->cancellable_signal_id);
291 : 0 : init->cancellable_signal_id = 0;
292 : : }
293 : :
294 : 0 : g_dbus_connection_signal_unsubscribe (connection,
295 : : init->portal_signal_id);
296 : :
297 : 0 : g_variant_get (parameters, "(ua{sv})", &response, NULL);
298 : :
299 [ # # # # ]: 0 : switch (response) {
300 : 0 : case 0:
301 : 0 : init->buffer = egg_secure_alloc (PASSWORD_SIZE);
302 : 0 : g_input_stream_read_all_async (init->stream,
303 : 0 : init->buffer, PASSWORD_SIZE,
304 : : G_PRIORITY_DEFAULT,
305 : : g_task_get_cancellable (task),
306 : : on_read_all,
307 : : task);
308 : 0 : break;
309 : 0 : case 1:
310 : 0 : g_task_return_new_error (task,
311 : : G_IO_ERROR,
312 : : G_IO_ERROR_CANCELLED,
313 : : "user interaction cancelled");
314 : 0 : g_object_unref (task);
315 : 0 : break;
316 : 0 : case 2:
317 : 0 : g_task_return_new_error (task,
318 : : G_IO_ERROR,
319 : : G_IO_ERROR_FAILED,
320 : : "user interaction failed");
321 : 0 : g_object_unref (task);
322 : 0 : break;
323 : : }
324 : 0 : }
325 : :
326 : : static void
327 : 0 : on_portal_request_close (GObject *source_object,
328 : : GAsyncResult *result,
329 : : gpointer user_data)
330 : : {
331 : 0 : GDBusConnection *connection = G_DBUS_CONNECTION (source_object);
332 : 0 : GTask *task = G_TASK (user_data);
333 : 0 : GError *error = NULL;
334 : :
335 [ # # ]: 0 : if (!g_dbus_connection_call_finish (connection, result, &error)) {
336 : 0 : g_task_return_error (task, error);
337 : 0 : g_object_unref (task);
338 : 0 : return;
339 : : }
340 : :
341 : 0 : g_task_return_boolean (task, TRUE);
342 : 0 : g_object_unref (task);
343 : : }
344 : :
345 : : static void
346 : 0 : on_portal_cancel (GCancellable *cancellable,
347 : : gpointer user_data)
348 : : {
349 : 0 : GTask *task = G_TASK (user_data);
350 : 0 : InitClosure *init = g_task_get_task_data (task);
351 : :
352 : 0 : g_dbus_connection_call (init->connection,
353 : : PORTAL_BUS_NAME,
354 : 0 : init->request_path,
355 : : PORTAL_REQUEST_INTERFACE,
356 : : "Close",
357 : : NULL,
358 : : NULL,
359 : : G_DBUS_CALL_FLAGS_NONE,
360 : : -1,
361 : : cancellable,
362 : : on_portal_request_close,
363 : : task);
364 : 0 : }
365 : :
366 : : static void
367 : 0 : on_portal_retrieve_secret (GObject *source_object,
368 : : GAsyncResult *result,
369 : : gpointer user_data)
370 : : {
371 : 0 : GDBusConnection *connection = G_DBUS_CONNECTION (source_object);
372 : 0 : GTask *task = G_TASK (user_data);
373 : 0 : InitClosure *init = g_task_get_task_data (task);
374 : 0 : GCancellable *cancellable = g_task_get_cancellable (task);
375 : : GVariant *reply;
376 : 0 : GError *error = NULL;
377 : :
378 : 0 : reply = g_dbus_connection_call_with_unix_fd_list_finish (connection,
379 : : NULL,
380 : : result,
381 : : &error);
382 [ # # ]: 0 : if (reply == NULL) {
383 : 0 : g_task_return_error (task, error);
384 : 0 : g_object_unref (task);
385 : 0 : return;
386 : : }
387 : :
388 : 0 : g_variant_get (reply, "(o)", &init->request_path);
389 : 0 : g_variant_unref (reply);
390 : :
391 : 0 : init->portal_signal_id =
392 : 0 : g_dbus_connection_signal_subscribe (connection,
393 : : PORTAL_BUS_NAME,
394 : : PORTAL_REQUEST_INTERFACE,
395 : : "Response",
396 : 0 : init->request_path,
397 : : NULL,
398 : : G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
399 : : on_portal_response,
400 : : task,
401 : : NULL);
402 : :
403 [ # # ]: 0 : if (cancellable != NULL)
404 : 0 : init->cancellable_signal_id =
405 : 0 : g_cancellable_connect (cancellable,
406 : : G_CALLBACK (on_portal_cancel),
407 : : task,
408 : : NULL);
409 : : }
410 : :
411 : : static void
412 : 0 : on_bus_get (GObject *source_object,
413 : : GAsyncResult *result,
414 : : gpointer user_data)
415 : : {
416 : : GDBusConnection *connection;
417 : 0 : GTask *task = G_TASK (user_data);
418 : 0 : InitClosure *init = g_task_get_task_data (task);
419 : : GUnixFDList *fd_list;
420 : : gint fds[2];
421 : : gint fd_index;
422 : : GVariantBuilder options;
423 : 0 : GError *error = NULL;
424 : :
425 : 0 : connection = g_bus_get_finish (result, &error);
426 [ # # ]: 0 : if (connection == NULL) {
427 : 0 : g_task_return_error (task, error);
428 : 0 : g_object_unref (task);
429 : 0 : return;
430 : : }
431 : 0 : init->connection = connection;
432 : :
433 [ # # ]: 0 : if (!g_unix_open_pipe (fds, FD_CLOEXEC, &error)) {
434 : 0 : g_object_unref (connection);
435 : 0 : g_task_return_error (task, error);
436 : 0 : g_object_unref (task);
437 : 0 : return;
438 : : }
439 : :
440 : 0 : fd_list = g_unix_fd_list_new ();
441 : 0 : fd_index = g_unix_fd_list_append (fd_list, fds[1], &error);
442 : 0 : close (fds[1]);
443 [ # # ]: 0 : if (fd_index < 0) {
444 : 0 : close (fds[0]);
445 : 0 : g_object_unref (fd_list);
446 : 0 : g_object_unref (connection);
447 : 0 : g_task_return_error (task, error);
448 : 0 : g_object_unref (task);
449 : 0 : return;
450 : : }
451 : :
452 : 0 : init->stream = g_unix_input_stream_new (fds[0], TRUE);
453 : :
454 : 0 : g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
455 : 0 : g_dbus_connection_call_with_unix_fd_list (connection,
456 : : PORTAL_BUS_NAME,
457 : : PORTAL_OBJECT_PATH,
458 : : PORTAL_SECRET_INTERFACE,
459 : : "RetrieveSecret",
460 : : g_variant_new ("(h@a{sv})",
461 : : fd_index,
462 : : g_variant_builder_end (&options)),
463 : : G_VARIANT_TYPE ("(o)"),
464 : : G_DBUS_CALL_FLAGS_NONE,
465 : : -1,
466 : : fd_list,
467 : : g_task_get_cancellable (task),
468 : : on_portal_retrieve_secret,
469 : : task);
470 : 0 : g_object_unref (fd_list);
471 : : }
472 : :
473 : : #ifdef WITH_TPM
474 : : static GBytes *
475 : 6 : load_password_from_tpm (GFile *file, GCancellable *cancellable, GError **error)
476 : : {
477 : 6 : EggTpm2Context *context = NULL;
478 : 6 : char *path = NULL;
479 : 6 : char *tpm2_file_path = NULL;
480 : 6 : GFile *tpm2_file = NULL;
481 : : gboolean status;
482 : 6 : GBytes *encrypted = NULL;
483 : 6 : GBytes *decrypted = NULL;
484 : :
485 : 6 : context = egg_tpm2_initialize (error);
486 [ - + ]: 6 : if (!context)
487 : 0 : return NULL;
488 : :
489 : 6 : path = g_file_get_path (file);
490 : 6 : tpm2_file_path = g_strdup_printf ("%s.tpm2", path);
491 : 6 : g_free (path);
492 : :
493 : 6 : tpm2_file = g_file_new_for_path (tpm2_file_path);
494 : 6 : status = g_file_test (tpm2_file_path, G_FILE_TEST_EXISTS);
495 : 6 : g_free (tpm2_file_path);
496 : :
497 [ + + ]: 6 : if (!status) {
498 : : gconstpointer contents;
499 : : gsize size;
500 : :
501 : 1 : encrypted = egg_tpm2_generate_master_password (context, error);
502 [ - + ]: 1 : if (!encrypted)
503 : 0 : return NULL;
504 : :
505 : 1 : contents = g_bytes_get_data (encrypted, &size);
506 : 1 : status = g_file_replace_contents (tpm2_file,
507 : : contents,
508 : : size,
509 : : NULL,
510 : : FALSE,
511 : : G_FILE_CREATE_PRIVATE,
512 : : NULL,
513 : : cancellable,
514 : : error);
515 [ - + ]: 1 : if (!status)
516 : 0 : goto out;
517 : : } else {
518 : : char *contents;
519 : : gsize length;
520 : :
521 : 5 : status = g_file_load_contents (tpm2_file,
522 : : cancellable,
523 : : &contents,
524 : : &length,
525 : : NULL,
526 : : error);
527 [ - + ]: 5 : if (!status)
528 : 0 : goto out;
529 : :
530 : 5 : encrypted = g_bytes_new_take (contents, length);
531 : : }
532 : :
533 : 6 : decrypted = egg_tpm2_decrypt_master_password (context, encrypted, error);
534 : :
535 : 6 : out:
536 [ + - ]: 6 : g_clear_object (&tpm2_file);
537 [ + - ]: 6 : g_clear_pointer (&encrypted, g_bytes_unref);
538 : 6 : egg_tpm2_finalize (context);
539 : :
540 : 6 : return decrypted;
541 : : }
542 : : #endif /* WITH_TPM */
543 : :
544 : : static void
545 : 12 : secret_file_backend_real_init_async (GAsyncInitable *initable,
546 : : int io_priority,
547 : : GCancellable *cancellable,
548 : : GAsyncReadyCallback callback,
549 : : gpointer user_data)
550 : : {
551 : 12 : const char *envvar = NULL;
552 : 12 : GFile *file = NULL;
553 : : SecretValue *password;
554 : : GTask *task;
555 : 12 : GError *error = NULL;
556 : : InitClosure *init;
557 : :
558 : 12 : task = g_task_new (initable, cancellable, callback, user_data);
559 : :
560 : 12 : file = get_secret_file (cancellable, &error);
561 [ - + ]: 12 : if (file == NULL) {
562 : 0 : g_task_return_error (task, g_steal_pointer (&error));
563 : 0 : g_object_unref (task);
564 : 6 : return;
565 : : }
566 : :
567 : 12 : envvar = g_getenv ("SECRET_FILE_TEST_PASSWORD");
568 [ + + + - ]: 12 : if (envvar != NULL && *envvar != '\0') {
569 : 6 : password = secret_value_new (envvar, -1, "text/plain");
570 : 6 : g_async_initable_new_async (SECRET_TYPE_FILE_COLLECTION,
571 : : io_priority,
572 : : cancellable,
573 : : on_collection_new_async,
574 : : task,
575 : : "file", file,
576 : : "password", password,
577 : : NULL);
578 : 6 : g_object_unref (file);
579 : 6 : secret_value_unref (password);
580 [ + - - + ]: 6 : } else if (g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS) || g_getenv ("SNAP_NAME") != NULL) {
581 : 0 : init = g_new0 (InitClosure, 1);
582 : 0 : init->io_priority = io_priority;
583 : 0 : init->file = file;
584 [ # # ]: 0 : if (cancellable)
585 : 0 : init->cancellable = g_object_ref (cancellable);
586 : 0 : g_task_set_task_data (task, init, init_closure_free);
587 : 0 : g_bus_get (G_BUS_TYPE_SESSION, cancellable, on_bus_get, task);
588 : : } else {
589 : : #ifdef WITH_TPM
590 : 6 : GBytes *decrypted = NULL;
591 : : gconstpointer data;
592 : : gsize size;
593 : :
594 : 6 : decrypted = load_password_from_tpm (file, cancellable, &error);
595 [ - + ]: 6 : if (!decrypted) {
596 : 0 : g_task_return_error (task, error);
597 : 0 : g_object_unref (task);
598 : 0 : return;
599 : : }
600 : :
601 : 6 : data = g_bytes_get_data (decrypted, &size);
602 : 6 : password = secret_value_new (data,size, "text/plain");
603 : 6 : g_bytes_unref (decrypted);
604 : 6 : g_async_initable_new_async (SECRET_TYPE_FILE_COLLECTION,
605 : : io_priority,
606 : : cancellable,
607 : : on_collection_new_async,
608 : : task,
609 : : "file", file,
610 : : "password", password,
611 : : NULL);
612 : :
613 : 6 : g_object_unref (file);
614 : 6 : secret_value_unref (password);
615 : 6 : return;
616 : : #else
617 : : g_task_return_new_error (task,
618 : : G_IO_ERROR,
619 : : G_IO_ERROR_INVALID_ARGUMENT,
620 : : "master password is not retrievable");
621 : : g_object_unref (task);
622 : : #endif
623 : : }
624 : : }
625 : :
626 : : static gboolean
627 : 12 : secret_file_backend_real_init_finish (GAsyncInitable *initable,
628 : : GAsyncResult *result,
629 : : GError **error)
630 : : {
631 [ - + ]: 12 : g_return_val_if_fail (g_task_is_valid (result, initable), FALSE);
632 : :
633 : 12 : return g_task_propagate_boolean (G_TASK (result), error);
634 : : }
635 : :
636 : : static void
637 : 12 : secret_file_backend_async_initable_iface (GAsyncInitableIface *iface)
638 : : {
639 : 12 : iface->init_async = secret_file_backend_real_init_async;
640 : 12 : iface->init_finish = secret_file_backend_real_init_finish;
641 : 12 : }
642 : :
643 : : static void
644 : 6 : on_collection_write (GObject *source_object,
645 : : GAsyncResult *result,
646 : : gpointer user_data)
647 : : {
648 : : SecretFileCollection *collection =
649 : 6 : SECRET_FILE_COLLECTION (source_object);
650 : 6 : GTask *task = G_TASK (user_data);
651 : 6 : GError *error = NULL;
652 : :
653 [ - + ]: 6 : if (!secret_file_collection_write_finish (collection, result, &error)) {
654 : 0 : g_task_return_error (task, error);
655 : 0 : g_object_unref (task);
656 : 0 : return;
657 : : }
658 : :
659 : 6 : g_task_return_boolean (task, TRUE);
660 : 6 : g_object_unref (task);
661 : : }
662 : :
663 : : static void
664 : 4 : secret_file_backend_real_store (SecretBackend *backend,
665 : : const SecretSchema *schema,
666 : : GHashTable *attributes,
667 : : const gchar *collection,
668 : : const gchar *label,
669 : : SecretValue *value,
670 : : GCancellable *cancellable,
671 : : GAsyncReadyCallback callback,
672 : : gpointer user_data)
673 : : {
674 : 4 : SecretFileBackend *self = SECRET_FILE_BACKEND (backend);
675 : : GTask *task;
676 : 4 : GError *error = NULL;
677 : :
678 : : /* Warnings raised already */
679 [ - + - - ]: 4 : if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, FALSE))
680 : 0 : return;
681 : :
682 : 4 : task = g_task_new (self, cancellable, callback, user_data);
683 : :
684 [ - + ]: 4 : if (!secret_file_collection_replace (self->collection,
685 : : attributes,
686 : : label,
687 : : value,
688 : : &error)) {
689 : 0 : g_task_return_error (task, error);
690 : 0 : g_object_unref (task);
691 : 0 : return;
692 : : }
693 : :
694 : 4 : secret_file_collection_write (self->collection,
695 : : cancellable,
696 : : on_collection_write,
697 : : task);
698 : : }
699 : :
700 : : static gboolean
701 : 4 : secret_file_backend_real_store_finish (SecretBackend *backend,
702 : : GAsyncResult *result,
703 : : GError **error)
704 : : {
705 [ - + ]: 4 : g_return_val_if_fail (g_task_is_valid (result, backend), FALSE);
706 : :
707 : 4 : return g_task_propagate_boolean (G_TASK (result), error);
708 : : }
709 : :
710 : : static void
711 : 2 : on_retrieve_secret (GObject *source_object,
712 : : GAsyncResult *result,
713 : : gpointer user_data)
714 : : {
715 : 2 : SecretRetrievable *retrievable = SECRET_RETRIEVABLE (source_object);
716 : 2 : GTask *task = G_TASK (user_data);
717 : : SecretValue *value;
718 : : GError *error;
719 : :
720 : 2 : value = secret_retrievable_retrieve_secret_finish (retrievable,
721 : : result,
722 : : &error);
723 : 2 : g_object_unref (retrievable);
724 [ - + ]: 2 : if (value == NULL) {
725 : 0 : g_task_return_error (task, error);
726 : 0 : g_object_unref (task);
727 : : }
728 : 2 : g_task_return_pointer (task, value, secret_value_unref);
729 : 2 : g_object_unref (task);
730 : 2 : }
731 : :
732 : : static void
733 : 2 : secret_file_backend_real_lookup (SecretBackend *backend,
734 : : const SecretSchema *schema,
735 : : GHashTable *attributes,
736 : : GCancellable *cancellable,
737 : : GAsyncReadyCallback callback,
738 : : gpointer user_data)
739 : : {
740 : 2 : SecretFileBackend *self = SECRET_FILE_BACKEND (backend);
741 : : GTask *task;
742 : : GList *matches;
743 : : GVariant *variant;
744 : : SecretFileItem *item;
745 : 2 : GError *error = NULL;
746 : :
747 : : /* Warnings raised already */
748 [ - + - - ]: 2 : if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE))
749 : 0 : return;
750 : :
751 : 2 : task = g_task_new (self, cancellable, callback, user_data);
752 : :
753 : 2 : matches = secret_file_collection_search (self->collection, attributes);
754 : :
755 [ - + ]: 2 : if (matches == NULL) {
756 : 0 : g_task_return_pointer (task, NULL, NULL);
757 : 0 : g_object_unref (task);
758 : 0 : return;
759 : : }
760 : :
761 : 2 : variant = g_variant_ref (matches->data);
762 : 2 : g_list_free_full (matches, (GDestroyNotify)g_variant_unref);
763 : :
764 : 2 : item = _secret_file_item_decrypt (variant, self->collection, &error);
765 : 2 : g_variant_unref (variant);
766 [ - + ]: 2 : if (item == NULL) {
767 : 0 : g_task_return_error (task, error);
768 : 0 : g_object_unref (task);
769 : 0 : return;
770 : : }
771 : :
772 : 2 : secret_retrievable_retrieve_secret (SECRET_RETRIEVABLE (item),
773 : : cancellable,
774 : : on_retrieve_secret,
775 : : task);
776 : : }
777 : :
778 : : static SecretValue *
779 : 2 : secret_file_backend_real_lookup_finish (SecretBackend *backend,
780 : : GAsyncResult *result,
781 : : GError **error)
782 : : {
783 [ - + ]: 2 : g_return_val_if_fail (g_task_is_valid (result, backend), NULL);
784 : :
785 : 2 : return g_task_propagate_pointer (G_TASK (result), error);
786 : : }
787 : :
788 : : static void
789 : 2 : secret_file_backend_real_clear (SecretBackend *backend,
790 : : const SecretSchema *schema,
791 : : GHashTable *attributes,
792 : : GCancellable *cancellable,
793 : : GAsyncReadyCallback callback,
794 : : gpointer user_data)
795 : : {
796 : 2 : SecretFileBackend *self = SECRET_FILE_BACKEND (backend);
797 : : GTask *task;
798 : 2 : GError *error = NULL;
799 : : gboolean ret;
800 : :
801 : : /* Warnings raised already */
802 [ - + - - ]: 2 : if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE))
803 : 0 : return;
804 : :
805 : 2 : task = g_task_new (self, cancellable, callback, user_data);
806 : :
807 : 2 : ret = secret_file_collection_clear (self->collection, attributes, &error);
808 [ - + ]: 2 : if (error != NULL) {
809 : 0 : g_task_return_error (task, error);
810 : 0 : g_object_unref (task);
811 : 0 : return;
812 : : }
813 : :
814 : : /* No need to write as nothing has been removed. */
815 [ - + ]: 2 : if (!ret) {
816 : 0 : g_task_return_boolean (task, FALSE);
817 : 0 : g_object_unref (task);
818 : 0 : return;
819 : : }
820 : :
821 : 2 : secret_file_collection_write (self->collection,
822 : : cancellable,
823 : : on_collection_write,
824 : : task);
825 : : }
826 : :
827 : : static gboolean
828 : 2 : secret_file_backend_real_clear_finish (SecretBackend *backend,
829 : : GAsyncResult *result,
830 : : GError **error)
831 : : {
832 [ - + ]: 2 : g_return_val_if_fail (g_task_is_valid (result, backend), FALSE);
833 : :
834 : 2 : return g_task_propagate_boolean (G_TASK (result), error);
835 : : }
836 : :
837 : : static void
838 : 0 : unref_objects (gpointer data)
839 : : {
840 : 0 : GList *list = data;
841 : :
842 : 0 : g_list_free_full (list, g_object_unref);
843 : 0 : }
844 : :
845 : : static void
846 : 4 : secret_file_backend_real_search (SecretBackend *backend,
847 : : const SecretSchema *schema,
848 : : GHashTable *attributes,
849 : : SecretSearchFlags flags,
850 : : GCancellable *cancellable,
851 : : GAsyncReadyCallback callback,
852 : : gpointer user_data)
853 : : {
854 : 4 : SecretFileBackend *self = SECRET_FILE_BACKEND (backend);
855 : : GTask *task;
856 : : GList *matches;
857 : 4 : GList *results = NULL;
858 : : GList *l;
859 : 4 : GError *error = NULL;
860 : :
861 : : /* Warnings raised already */
862 [ - + - - ]: 4 : if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, FALSE))
863 : 0 : return;
864 : :
865 : 4 : task = g_task_new (self, cancellable, callback, user_data);
866 : :
867 : 4 : matches = secret_file_collection_search (self->collection, attributes);
868 [ + - + + ]: 10 : for (l = matches; l; l = g_list_next (l)) {
869 : 6 : SecretFileItem *item = _secret_file_item_decrypt (l->data, self->collection, &error);
870 [ - + ]: 6 : if (item == NULL) {
871 : 0 : g_task_return_error (task, error);
872 : 0 : g_object_unref (task);
873 : 0 : return;
874 : : }
875 : 6 : results = g_list_append (results, item);
876 : : }
877 : 4 : g_list_free_full (matches, (GDestroyNotify)g_variant_unref);
878 : :
879 : 4 : g_task_return_pointer (task, results, unref_objects);
880 : 4 : g_object_unref (task);
881 : : }
882 : :
883 : : static GList *
884 : 4 : secret_file_backend_real_search_finish (SecretBackend *backend,
885 : : GAsyncResult *result,
886 : : GError **error)
887 : : {
888 [ - + ]: 4 : g_return_val_if_fail (g_task_is_valid (result, backend), NULL);
889 : :
890 : 4 : return g_task_propagate_pointer (G_TASK (result), error);
891 : : }
892 : :
893 : : static void
894 : 12 : secret_file_backend_backend_iface (SecretBackendInterface *iface)
895 : : {
896 : 12 : iface->store = secret_file_backend_real_store;
897 : 12 : iface->store_finish = secret_file_backend_real_store_finish;
898 : 12 : iface->lookup = secret_file_backend_real_lookup;
899 : 12 : iface->lookup_finish = secret_file_backend_real_lookup_finish;
900 : 12 : iface->clear = secret_file_backend_real_clear;
901 : 12 : iface->clear_finish = secret_file_backend_real_clear_finish;
902 : 12 : iface->search = secret_file_backend_real_search;
903 : 12 : iface->search_finish = secret_file_backend_real_search_finish;
904 : 12 : }
905 : :
906 : : gboolean
907 : 0 : _secret_file_backend_check_portal_version (void)
908 : : {
909 : : GDBusConnection *connection;
910 : : GVariant *ret;
911 : : GVariant *value;
912 : : guint32 version;
913 : 0 : GError *error = NULL;
914 : :
915 : 0 : connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
916 [ # # ]: 0 : if (!connection) {
917 : 0 : g_warning ("couldn't get session bus: %s", error->message);
918 : 0 : g_error_free (error);
919 : 0 : return FALSE;
920 : : }
921 : :
922 : 0 : ret = g_dbus_connection_call_sync (connection,
923 : : PORTAL_BUS_NAME,
924 : : PORTAL_OBJECT_PATH,
925 : : "org.freedesktop.DBus.Properties",
926 : : "Get",
927 : : g_variant_new ("(ss)",
928 : : PORTAL_SECRET_INTERFACE,
929 : : "version"),
930 : : G_VARIANT_TYPE ("(v)"),
931 : : 0, -1, NULL, &error);
932 : 0 : g_object_unref (connection);
933 [ # # ]: 0 : if (!ret) {
934 : 0 : g_info ("secret portal is not available: %s", error->message);
935 : 0 : g_error_free (error);
936 : 0 : return FALSE;
937 : : }
938 : :
939 : 0 : g_variant_get (ret, "(v)", &value);
940 : 0 : g_variant_unref (ret);
941 : 0 : version = g_variant_get_uint32 (value);
942 : 0 : g_variant_unref (value);
943 [ # # ]: 0 : if (version != PORTAL_SECRET_VERSION) {
944 : 0 : g_info ("secret portal version mismatch: %u != %u", version, PORTAL_SECRET_VERSION);
945 : 0 : return FALSE;
946 : : }
947 : :
948 : 0 : return TRUE;
949 : : }
|