1 /* libsecret - GLib wrapper for Secret Service
2  *
3  * Copyright 2011 Collabora Ltd.
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: Stef Walter <stefw@gnome.org>
13  */
14 
15 #include "config.h"
16 
17 #include "secret-private.h"
18 #include "secret-types.h"
19 
20 #include <string.h>
21 
22 /**
23  * SECTION:secret-error
24  * @title: SecretError
25  * @short_description: libsecret errors
26  *
27  * Various errors reported by the libsecret library. No error returned from
28  * the libsecret API is suitable for direct display to the user. It is up
29  * to the application to handle them appropriately.
30  *
31  * Stability: Stable
32  */
33 
34 /**
35  * SECRET_ERROR:
36  *
37  * The error domain quark which denotes libsecret specific errors from the
38  * #SecretError enumeration.
39  */
40 
41 /**
42  * SecretError:
43  * @SECRET_ERROR_PROTOCOL: received an invalid data or message from the Secret
44  *                         Service
45  * @SECRET_ERROR_IS_LOCKED: the item or collection is locked and the operation
46  *                          cannot be performed
47  * @SECRET_ERROR_NO_SUCH_OBJECT: no such item or collection found in the
48  *                               Secret Service
49  * @SECRET_ERROR_ALREADY_EXISTS: a relevant item or collection already exists
50  *
51  * Errors returned by the Secret Service. None of the errors are appropriate
52  * for display to the user.
53  */
54 
55 static void
list_unref_free(GList * reflist)56 list_unref_free (GList *reflist)
57 {
58 	GList *l;
59 	for (l = reflist; l; l = g_list_next (l)) {
60 		g_return_if_fail (G_IS_OBJECT (l->data));
61 		g_object_unref (l->data);
62 	}
63 	g_list_free (reflist);
64 }
65 
66 static GList *
list_ref_copy(GList * reflist)67 list_ref_copy (GList *reflist)
68 {
69 	GList *l, *copy = g_list_copy (reflist);
70 	for (l = copy; l; l = g_list_next (l)) {
71 		g_return_val_if_fail (G_IS_OBJECT (l->data), NULL);
72 		g_object_ref (l->data);
73 	}
74 	return copy;
75 }
76 
77 GType
_secret_list_get_type(void)78 _secret_list_get_type (void)
79 {
80 	static GType type = 0;
81 	if (!type)
82 		type = g_boxed_type_register_static ("SecretObjectList",
83 		                                     (GBoxedCopyFunc)list_ref_copy,
84 		                                     (GBoxedFreeFunc)list_unref_free);
85 	return type;
86 
87 }
88 
89 GQuark
secret_error_get_quark(void)90 secret_error_get_quark (void)
91 {
92 	static volatile gsize quark = 0;
93 
94 	static const GDBusErrorEntry entries[] = {
95 		{ SECRET_ERROR_IS_LOCKED, "org.freedesktop.Secret.Error.IsLocked", },
96 		{ SECRET_ERROR_NO_SUCH_OBJECT, "org.freedesktop.Secret.Error.NoSuchObject", },
97 		{ SECRET_ERROR_ALREADY_EXISTS, "org.freedesktop.Secret.Error.AlreadyExists" },
98 	};
99 
100 	g_dbus_error_register_error_domain ("secret-error", &quark,
101 	                                    entries, G_N_ELEMENTS (entries));
102 
103 	return quark;
104 }
105 
106 gboolean
_secret_util_propagate_error(GSimpleAsyncResult * async,GError ** error)107 _secret_util_propagate_error (GSimpleAsyncResult *async,
108                               GError **error)
109 {
110 	if (!g_simple_async_result_propagate_error (async, error))
111 		return FALSE;
112 
113 	_secret_util_strip_remote_error (error);
114 	return TRUE;
115 }
116 
117 void
_secret_util_strip_remote_error(GError ** error)118 _secret_util_strip_remote_error (GError **error)
119 {
120 	gchar *remote;
121 
122 	if (error == NULL || *error == NULL)
123 		return;
124 
125 	remote = g_dbus_error_get_remote_error (*error);
126 	if (remote) {
127 		if (g_dbus_error_strip_remote_error (*error)) {
128 			g_info ("Remote error from secret service: %s: %s", remote, (*error)->message);
129 		}
130 		g_free (remote);
131 	}
132 }
133 
134 gchar *
_secret_util_parent_path(const gchar * path)135 _secret_util_parent_path (const gchar *path)
136 {
137 	const gchar *pos;
138 
139 	g_return_val_if_fail (path != NULL, NULL);
140 
141 	pos = strrchr (path, '/');
142 	g_return_val_if_fail (pos != NULL, NULL);
143 	g_return_val_if_fail (pos != path, NULL);
144 
145 	return g_strndup (path, pos - path);
146 }
147 
148 gboolean
_secret_util_empty_path(const gchar * path)149 _secret_util_empty_path (const gchar *path)
150 {
151 	g_return_val_if_fail (path != NULL, TRUE);
152 	return (g_str_equal (path, "") || g_str_equal (path, "/"));
153 }
154 
155 gchar *
_secret_util_collection_to_path(const gchar * collection)156 _secret_util_collection_to_path (const gchar *collection)
157 {
158 	if (collection == NULL)
159 		collection = SECRET_COLLECTION_DEFAULT;
160 	if (strchr (collection, '/') == NULL)
161 		return g_strdup_printf ("/org/freedesktop/secrets/aliases/%s", collection);
162 	return g_strdup (collection);
163 }
164 
165 GVariant *
_secret_util_variant_for_properties(GHashTable * properties)166 _secret_util_variant_for_properties (GHashTable *properties)
167 {
168 	GHashTableIter iter;
169 	GVariantBuilder builder;
170 	const gchar *name;
171 	GVariant *value;
172 
173 	g_return_val_if_fail (properties != NULL, NULL);
174 
175 	g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
176 
177 	g_hash_table_iter_init (&iter, properties);
178 	while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&value))
179 		g_variant_builder_add (&builder, "{sv}", name, value);
180 
181 	return g_variant_builder_end (&builder);
182 }
183 
184 static void
process_get_all_reply(GDBusProxy * proxy,GVariant * retval)185 process_get_all_reply (GDBusProxy *proxy,
186                        GVariant *retval)
187 {
188 	const gchar *invalidated_properties[1] = { NULL };
189 	GVariant *changed_properties;
190 	GVariantIter *iter;
191 	GVariant *value;
192 	gchar *key;
193 
194 	if (!g_variant_is_of_type (retval, G_VARIANT_TYPE ("(a{sv})"))) {
195 		g_warning ("Value for GetAll reply with type `%s' does not match `(a{sv})'",
196 		           g_variant_get_type_string (retval));
197 		return;
198 	}
199 
200 	g_variant_get (retval, "(a{sv})", &iter);
201 	while (g_variant_iter_loop (iter, "{sv}", &key, &value))
202 		g_dbus_proxy_set_cached_property (proxy, key, value);
203 	g_variant_iter_free (iter);
204 
205 	g_variant_get (retval, "(@a{sv})", &changed_properties);
206 	g_signal_emit_by_name (proxy, "g-properties-changed",
207 	                       changed_properties, invalidated_properties);
208 	g_variant_unref (changed_properties);
209 }
210 
211 static void
on_get_properties(GObject * source,GAsyncResult * result,gpointer user_data)212 on_get_properties (GObject *source,
213                    GAsyncResult *result,
214                    gpointer user_data)
215 {
216 	GTask *task = G_TASK (user_data);
217 	GDBusProxy *proxy = G_DBUS_PROXY (g_task_get_source_object (task));
218 	GError *error = NULL;
219 	GVariant *retval;
220 
221 	retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
222 
223 	if (error == NULL) {
224 		process_get_all_reply (proxy, retval);
225 		g_task_return_boolean (task, TRUE);
226 	} else {
227 		g_task_return_error (task, g_steal_pointer (&error));
228 	}
229 	if (retval != NULL)
230 		g_variant_unref (retval);
231 
232 	g_clear_object (&task);
233 }
234 
235 void
_secret_util_get_properties(GDBusProxy * proxy,gpointer result_tag,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)236 _secret_util_get_properties (GDBusProxy *proxy,
237                              gpointer result_tag,
238                              GCancellable *cancellable,
239                              GAsyncReadyCallback callback,
240                              gpointer user_data)
241 {
242 	GTask *task;
243 
244 	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
245 
246 	task = g_task_new (proxy, cancellable, callback, user_data);
247 	g_task_set_source_tag (task, result_tag);
248 
249 	g_dbus_connection_call (g_dbus_proxy_get_connection (proxy),
250 	                        g_dbus_proxy_get_name (proxy),
251 	                        g_dbus_proxy_get_object_path (proxy),
252 	                        "org.freedesktop.DBus.Properties", "GetAll",
253 	                        g_variant_new ("(s)", g_dbus_proxy_get_interface_name (proxy)),
254 	                        G_VARIANT_TYPE ("(a{sv})"),
255 	                        G_DBUS_CALL_FLAGS_NONE, -1,
256 	                        cancellable, on_get_properties,
257 	                        g_steal_pointer (&task));
258 
259 	g_clear_object (&task);
260 }
261 
262 gboolean
_secret_util_get_properties_finish(GDBusProxy * proxy,gpointer result_tag,GAsyncResult * result,GError ** error)263 _secret_util_get_properties_finish (GDBusProxy *proxy,
264                                     gpointer result_tag,
265                                     GAsyncResult *result,
266                                     GError **error)
267 {
268 	g_return_val_if_fail (g_task_is_valid (result, proxy), FALSE);
269 	g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == result_tag, FALSE);
270 	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
271 
272 	if (!g_task_propagate_boolean (G_TASK (result), error)) {
273 		_secret_util_strip_remote_error (error);
274 		return FALSE;
275 	}
276 
277 	return TRUE;
278 }
279 
280 typedef struct {
281 	gchar *property;
282 	GVariant *value;
283 	gboolean result;
284 } SetClosure;
285 
286 static void
set_closure_free(gpointer data)287 set_closure_free (gpointer data)
288 {
289 	SetClosure *closure = data;
290 	g_free (closure->property);
291 	g_variant_unref (closure->value);
292 	g_slice_free (SetClosure, closure);
293 }
294 
295 static void
on_set_property(GObject * source,GAsyncResult * result,gpointer user_data)296 on_set_property (GObject *source,
297                  GAsyncResult *result,
298                  gpointer user_data)
299 {
300 	GTask *task = G_TASK (user_data);
301 	SetClosure *closure = g_task_get_task_data (task);
302 	GDBusProxy *proxy = G_DBUS_PROXY (g_task_get_source_object (user_data));
303 	GError *error = NULL;
304 	GVariant *retval;
305 	gboolean success = FALSE;
306 
307 	retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source),
308 	                                        result, &error);
309 	if (error != NULL) {
310 		g_task_return_error (task, g_steal_pointer (&error));
311 	} else {
312 		success = (retval != NULL);
313 
314 		if (success) {
315 			g_dbus_proxy_set_cached_property (proxy, closure->property, closure->value);
316 			g_variant_unref (retval);
317 		}
318 
319 		g_task_return_boolean (task, success);
320 	}
321 
322 	g_clear_object (&task);
323 }
324 
325 void
_secret_util_set_property(GDBusProxy * proxy,const gchar * property,GVariant * value,gpointer result_tag,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)326 _secret_util_set_property (GDBusProxy *proxy,
327                            const gchar *property,
328                            GVariant *value,
329                            gpointer result_tag,
330                            GCancellable *cancellable,
331                            GAsyncReadyCallback callback,
332                            gpointer user_data)
333 {
334 	GTask *task;
335 	SetClosure *closure;
336 
337 	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
338 
339 	task = g_task_new (proxy, cancellable, callback, user_data);
340 	g_task_set_source_tag (task, result_tag);
341 	closure = g_slice_new0 (SetClosure);
342 	closure->property = g_strdup (property);
343 	closure->value = g_variant_ref_sink (value);
344 	g_task_set_task_data (task, closure, set_closure_free);
345 
346 	g_dbus_connection_call (g_dbus_proxy_get_connection (proxy),
347 	                        g_dbus_proxy_get_name (proxy),
348 	                        g_dbus_proxy_get_object_path (proxy),
349 	                        SECRET_PROPERTIES_INTERFACE,
350 	                        "Set",
351 	                        g_variant_new ("(ssv)",
352 	                                       g_dbus_proxy_get_interface_name (proxy),
353 	                                       property,
354 	                                       closure->value),
355 	                        G_VARIANT_TYPE ("()"),
356 	                        G_DBUS_CALL_FLAGS_NO_AUTO_START, -1,
357 	                        cancellable, on_set_property,
358 	                        g_steal_pointer (&task));
359 
360 	g_clear_object (&task);
361 }
362 
363 gboolean
_secret_util_set_property_finish(GDBusProxy * proxy,gpointer result_tag,GAsyncResult * result,GError ** error)364 _secret_util_set_property_finish (GDBusProxy *proxy,
365                                   gpointer result_tag,
366                                   GAsyncResult *result,
367                                   GError **error)
368 {
369 	g_return_val_if_fail (g_task_is_valid (result, proxy), FALSE);
370 	g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == result_tag, FALSE);
371 	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
372 
373 	if (!g_task_propagate_boolean (G_TASK (result), error)) {
374 		_secret_util_strip_remote_error (error);
375 		return FALSE;
376 	}
377 
378 	return TRUE;
379 }
380 
381 gboolean
_secret_util_set_property_sync(GDBusProxy * proxy,const gchar * property,GVariant * value,GCancellable * cancellable,GError ** error)382 _secret_util_set_property_sync (GDBusProxy *proxy,
383                                 const gchar *property,
384                                 GVariant *value,
385                                 GCancellable *cancellable,
386                                 GError **error)
387 {
388 	gboolean result = FALSE;
389 	GVariant *retval;
390 
391 	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
392 	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
393 
394 	g_variant_ref_sink (value);
395 
396 	retval = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (proxy),
397 	                                      g_dbus_proxy_get_name (proxy),
398 	                                      g_dbus_proxy_get_object_path (proxy),
399 	                                      SECRET_PROPERTIES_INTERFACE,
400 	                                      "Set",
401 	                                      g_variant_new ("(ssv)",
402 	                                                     g_dbus_proxy_get_interface_name (proxy),
403 	                                                     property,
404 	                                                     value),
405 	                                      G_VARIANT_TYPE ("()"),
406 	                                      G_DBUS_CALL_FLAGS_NO_AUTO_START, -1,
407 	                                      cancellable, error);
408 
409 	if (retval != NULL) {
410 		result = TRUE;
411 		g_variant_unref (retval);
412 		g_dbus_proxy_set_cached_property (proxy, property, value);
413 	}
414 
415 	g_variant_unref (value);
416 
417 	return result;
418 }
419 
420 gboolean
_secret_util_have_cached_properties(GDBusProxy * proxy)421 _secret_util_have_cached_properties (GDBusProxy *proxy)
422 {
423 	gchar **names;
424 
425 	names = g_dbus_proxy_get_cached_property_names (proxy);
426 	g_strfreev (names);
427 
428 	return names != NULL;
429 }
430 
431 SecretSync *
_secret_sync_new(void)432 _secret_sync_new (void)
433 {
434 	SecretSync *sync;
435 
436 	sync = g_new0 (SecretSync, 1);
437 
438 	sync->context = g_main_context_new ();
439 	sync->loop = g_main_loop_new (sync->context, FALSE);
440 
441 	return sync;
442 }
443 
444 void
_secret_sync_free(gpointer data)445 _secret_sync_free (gpointer data)
446 {
447 	SecretSync *sync = data;
448 
449 	while (g_main_context_iteration (sync->context, FALSE));
450 
451 	g_clear_object (&sync->result);
452 	g_main_loop_unref (sync->loop);
453 	g_main_context_unref (sync->context);
454 	g_free (sync);
455 }
456 
457 void
_secret_sync_on_result(GObject * source,GAsyncResult * result,gpointer user_data)458 _secret_sync_on_result (GObject *source,
459                         GAsyncResult *result,
460                         gpointer user_data)
461 {
462 	SecretSync *sync = user_data;
463 	g_assert (sync->result == NULL);
464 	sync->result = g_object_ref (result);
465 	g_main_loop_quit (sync->loop);
466 }
467