From f44aae6efa58c388bebd5778a169a62c6ddf3525 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Mon, 23 Jan 2012 17:20:18 +0100 Subject: [PATCH] Tests for GSecretItem and GSecretValue * And fix bugs found in the process --- library/Makefile.am | 1 + library/gsecret-collection.c | 121 +++-- library/gsecret-collection.h | 2 +- library/gsecret-item.c | 241 ++++++---- library/gsecret-item.h | 5 +- library/gsecret-private.h | 36 +- library/gsecret-service.c | 774 +++++--------------------------- library/gsecret-service.h | 18 +- library/gsecret-session.c | 671 +++++++++++++++++++++++++++ library/gsecret-util.c | 60 ++- library/gsecret-value.h | 2 +- library/tests/Makefile.am | 3 + library/tests/mock/service.py | 101 ++++- library/tests/test-collection.c | 339 ++++++++++++++ library/tests/test-item.c | 534 ++++++++++++++++++++++ library/tests/test-value.c | 210 +++++++++ 16 files changed, 2284 insertions(+), 834 deletions(-) create mode 100644 library/gsecret-session.c create mode 100644 library/tests/test-collection.c create mode 100644 library/tests/test-item.c create mode 100644 library/tests/test-value.c diff --git a/library/Makefile.am b/library/Makefile.am index 1dcdfa3..720b1e7 100644 --- a/library/Makefile.am +++ b/library/Makefile.am @@ -20,6 +20,7 @@ libgsecret_la_SOURCES = \ gsecret-password.h gsecret-password.c \ gsecret-prompt.h gsecret-prompt.c \ gsecret-service.h gsecret-service.c \ + gsecret-session.h gsecret-session.c \ gsecret-util.c \ gsecret-value.h gsecret-value.c \ $(BUILT_SOURCES) \ diff --git a/library/gsecret-collection.c b/library/gsecret-collection.c index 26638f4..3f1a8fa 100644 --- a/library/gsecret-collection.c +++ b/library/gsecret-collection.c @@ -1,6 +1,6 @@ /* GSecret - GLib wrapper for Secret Service * - * Copyright 2011 Red Hat Inc. + * Copyright 2012 Red Hat Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -23,6 +23,7 @@ enum { PROP_0, + PROP_SERVICE, PROP_ITEMS, PROP_LABEL, PROP_LOCKED, @@ -34,6 +35,7 @@ struct _GSecretCollectionPrivate { /* Doesn't change between construct and finalize */ GSecretService *service; GCancellable *cancellable; + gboolean constructing; /* Protected by mutex */ GMutex mutex; @@ -58,6 +60,7 @@ gsecret_collection_init (GSecretCollection *self) g_mutex_init (&self->pv->mutex); self->pv->cancellable = g_cancellable_new (); self->pv->items = items_table_new (); + self->pv->constructing = TRUE; } static void @@ -86,6 +89,13 @@ gsecret_collection_set_property (GObject *obj, GSecretCollection *self = GSECRET_COLLECTION (obj); switch (prop_id) { + case PROP_SERVICE: + g_return_if_fail (self->pv->service == NULL); + self->pv->service = g_value_get_object (value); + if (self->pv->service) + g_object_add_weak_pointer (G_OBJECT (self->pv->service), + (gpointer *)&self->pv->service); + break; case PROP_LABEL: gsecret_collection_set_label (self, g_value_get_string (value), self->pv->cancellable, on_set_label, @@ -106,6 +116,9 @@ gsecret_collection_get_property (GObject *obj, GSecretCollection *self = GSECRET_COLLECTION (obj); switch (prop_id) { + case PROP_SERVICE: + g_value_set_object (value, self->pv->service); + break; case PROP_ITEMS: g_value_take_boxed (value, gsecret_collection_get_items (self)); break; @@ -132,10 +145,9 @@ gsecret_collection_dispose (GObject *obj) { GSecretCollection *self = GSECRET_COLLECTION (obj); - g_clear_object (&self->pv->service); g_cancellable_cancel (self->pv->cancellable); - G_OBJECT_GET_CLASS (obj)->dispose (obj); + G_OBJECT_CLASS (gsecret_collection_parent_class)->dispose (obj); } static void @@ -143,14 +155,19 @@ gsecret_collection_finalize (GObject *obj) { GSecretCollection *self = GSECRET_COLLECTION (obj); + if (self->pv->service) + g_object_remove_weak_pointer (G_OBJECT (self->pv->service), + (gpointer *)&self->pv->service); + g_mutex_clear (&self->pv->mutex); g_hash_table_destroy (self->pv->items); g_object_unref (self->pv->cancellable); - G_OBJECT_GET_CLASS (obj)->finalize (obj); + G_OBJECT_CLASS (gsecret_collection_parent_class)->finalize (obj); } typedef struct { + GSecretCollection *collection; GCancellable *cancellable; GHashTable *items; gint items_loading; @@ -160,6 +177,7 @@ static void load_closure_free (gpointer data) { LoadClosure *closure = data; + g_object_unref (closure->collection); g_clear_object (&closure->cancellable); g_hash_table_unref (closure->items); g_slice_free (LoadClosure, closure); @@ -174,7 +192,7 @@ load_result_new (GCancellable *cancellable, LoadClosure *closure; res = g_simple_async_result_new (NULL, callback, user_data, load_result_new); - closure = g_slice_new (LoadClosure); + closure = g_slice_new0 (LoadClosure); closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL; closure->items = items_table_new (); g_simple_async_result_set_op_res_gpointer (res, closure, load_closure_free); @@ -183,10 +201,10 @@ load_result_new (GCancellable *cancellable, } static void -load_items_complete (GSecretCollection *self, - GSimpleAsyncResult *res) +load_items_complete (GSimpleAsyncResult *res) { LoadClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + GSecretCollection *self = closure->collection; GHashTable *items; g_assert (closure->items_loading == 0); @@ -209,7 +227,6 @@ on_item_loading (GObject *source, gpointer user_data) { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); - GSecretCollection *self = GSECRET_COLLECTION (g_async_result_get_source_object (user_data)); LoadClosure *closure = g_simple_async_result_get_op_res_gpointer (res); const gchar *item_path; GError *error = NULL; @@ -223,14 +240,13 @@ on_item_loading (GObject *source, g_simple_async_result_take_error (res, error); if (item != NULL) { - item_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (self)); + item_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (item)); g_hash_table_insert (closure->items, g_strdup (item_path), item); } if (closure->items_loading == 0) - load_items_complete (self, res); + load_items_complete (res); - g_object_unref (self); g_object_unref (res); } @@ -244,16 +260,23 @@ load_items_perform (GSecretCollection *self, GVariantIter iter; gchar *item_path; + g_assert (GSECRET_IS_COLLECTION (self)); + g_assert (item_paths != NULL); + g_assert (closure->collection == NULL); + + closure->collection = g_object_ref (self); + g_variant_iter_init (&iter, item_paths); while (g_variant_iter_loop (&iter, "o", &item_path)) { g_mutex_lock (&self->pv->mutex); item = g_hash_table_lookup (self->pv->items, item_path); - if (item == NULL) + if (item != NULL) g_object_ref (item); g_mutex_unlock (&self->pv->mutex); if (item == NULL) { + // TODO: xxxxxxxxxxxx; gsecret_item_new (self->pv->service, item_path, closure->cancellable, on_item_loading, g_object_ref (res)); @@ -267,9 +290,7 @@ load_items_perform (GSecretCollection *self, } if (closure->items_loading == 0) - load_items_complete (self, res); - - g_variant_unref (item_paths); + load_items_complete (res); } static void @@ -291,7 +312,7 @@ handle_property_changed (GSecretCollection *self, else if (g_str_equal (property_name, "Modified")) g_object_notify (G_OBJECT (self), "modified"); - else if (g_str_equal (property_name, "Items")) { + else if (g_str_equal (property_name, "Items") && !self->pv->constructing) { res = load_result_new (self->pv->cancellable, NULL, NULL); if (value == NULL) @@ -302,6 +323,7 @@ handle_property_changed (GSecretCollection *self, g_warning ("couldn't retrieve Collection Items property"); g_simple_async_result_complete (res); } else { + // TODO: yyyy; load_items_perform (self, res, value); g_variant_unref (value); } @@ -324,6 +346,7 @@ gsecret_collection_properties_changed (GDBusProxy *proxy, g_variant_iter_init (&iter, changed_properties); while (g_variant_iter_loop (&iter, "{sv}", &property_name, &value)) + // TODO: zzzz; handle_property_changed (self, property_name, value); g_object_thaw_notify (G_OBJECT (self)); @@ -342,25 +365,31 @@ gsecret_collection_class_init (GSecretCollectionClass *klass) proxy_class->g_properties_changed = gsecret_collection_properties_changed; + g_object_class_install_property (gobject_class, PROP_SERVICE, + g_param_spec_object ("service", "Service", "Secret Service", + GSECRET_TYPE_SERVICE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ITEMS, g_param_spec_boxed ("items", "Items", "Items in collection", - _gsecret_list_get_type (), G_PARAM_READABLE)); + _gsecret_list_get_type (), G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_LABEL, g_param_spec_string ("label", "Label", "Item label", - NULL, G_PARAM_READWRITE)); + NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_LOCKED, g_param_spec_boolean ("locked", "Locked", "Item locked", - TRUE, G_PARAM_READABLE)); + TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_CREATED, g_param_spec_uint64 ("created", "Created", "Item creation date", - 0UL, G_MAXUINT64, 0UL, G_PARAM_READWRITE)); + 0UL, G_MAXUINT64, 0UL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_MODIFIED, g_param_spec_uint64 ("modified", "Modified", "Item modified date", - 0UL, G_MAXUINT64, 0UL, G_PARAM_READWRITE)); + 0UL, G_MAXUINT64, 0UL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_type_class_add_private (gobject_class, sizeof (GSecretCollectionPrivate)); } static void @@ -369,24 +398,40 @@ on_collection_new (GObject *source, gpointer user_data) { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + GSecretCollection *self; GObject *source_object; GError *error = NULL; + GVariant *item_paths; GObject *object; + GDBusProxy *proxy; - source_object = g_async_result_get_source_object (user_data); + source_object = g_async_result_get_source_object (result); object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), result, &error); g_object_unref (source_object); + proxy = G_DBUS_PROXY (object); + if (error == NULL && !_gsecret_util_have_cached_properties (proxy)) { + g_set_error (&error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, + "No such secret collection at path: %s", g_dbus_proxy_get_object_path (proxy)); + } + if (error == NULL) { - load_items_perform (GSECRET_COLLECTION (object), res, NULL); - g_simple_async_result_set_op_res_gpointer (res, object, g_object_unref); + self = GSECRET_COLLECTION (object); + self->pv->constructing = FALSE; + + item_paths = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (object), "Items"); + g_return_if_fail (item_paths != NULL); + // TODO: yyyy; + load_items_perform (self, res, item_paths); + g_variant_unref (item_paths); } else { g_simple_async_result_take_error (res, error); g_simple_async_result_complete (res); } + g_clear_object (&object); g_object_unref (res); } @@ -410,6 +455,7 @@ gsecret_collection_new (GSecretService *service, g_async_initable_new_async (GSECRET_SERVICE_GET_CLASS (service)->collection_gtype, G_PRIORITY_DEFAULT, cancellable, + // TODO: zzzz; on_collection_new, g_object_ref (res), "g-flags", G_DBUS_CALL_FLAGS_NONE, @@ -418,6 +464,7 @@ gsecret_collection_new (GSecretService *service, "g-connection", g_dbus_proxy_get_connection (proxy), "g-object-path", collection_path, "g-interface-name", GSECRET_COLLECTION_INTERFACE, + "service", service, NULL); g_object_unref (res); @@ -427,18 +474,19 @@ GSecretCollection * gsecret_collection_new_finish (GAsyncResult *result, GError **error) { - GObject *object; - GObject *source_object; + GSimpleAsyncResult *res; + LoadClosure *closure; - source_object = g_async_result_get_source_object (result); - object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), - result, error); - g_object_unref (source_object); + g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL, load_result_new), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); - if (object != NULL) - return GSECRET_COLLECTION (object); - else + res = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (res, error)) return NULL; + + closure = g_simple_async_result_get_op_res_gpointer (res); + return g_object_ref (closure->collection); } GSecretCollection * @@ -458,6 +506,7 @@ gsecret_collection_new_sync (GSecretService *service, sync = _gsecret_sync_new (); g_main_context_push_thread_default (sync->context); + // TODO: xxxxx; gsecret_collection_new (service, collection_path, cancellable, _gsecret_sync_on_result, sync); @@ -590,7 +639,7 @@ gsecret_collection_set_label (GSecretCollection *self, GAsyncReadyCallback callback, gpointer user_data) { - g_return_if_fail (GSECRET_IS_ITEM (self)); + g_return_if_fail (GSECRET_IS_COLLECTION (self)); g_return_if_fail (label != NULL); _gsecret_util_set_property (G_DBUS_PROXY (self), "Label", @@ -604,7 +653,7 @@ gsecret_collection_set_label_finish (GSecretCollection *self, GAsyncResult *result, GError **error) { - g_return_val_if_fail (GSECRET_IS_ITEM (self), FALSE); + g_return_val_if_fail (GSECRET_IS_COLLECTION (self), FALSE); return _gsecret_util_set_property_finish (G_DBUS_PROXY (self), gsecret_collection_set_label, @@ -617,7 +666,7 @@ gsecret_collection_set_label_sync (GSecretCollection *self, GCancellable *cancellable, GError **error) { - g_return_val_if_fail (GSECRET_IS_ITEM (self), FALSE); + g_return_val_if_fail (GSECRET_IS_COLLECTION (self), FALSE); g_return_val_if_fail (label != NULL, FALSE); return _gsecret_util_set_property_sync (G_DBUS_PROXY (self), "Label", diff --git a/library/gsecret-collection.h b/library/gsecret-collection.h index f9cb43d..2d74986 100644 --- a/library/gsecret-collection.h +++ b/library/gsecret-collection.h @@ -1,6 +1,6 @@ /* GSecret - GLib wrapper for Secret Service * - * Copyright 2011 Collabora Ltd. + * Copyright 2012 Red Hat Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published diff --git a/library/gsecret-item.c b/library/gsecret-item.c index 59396ae..9522dbf 100644 --- a/library/gsecret-item.c +++ b/library/gsecret-item.c @@ -1,6 +1,6 @@ /* GSecret - GLib wrapper for Secret Service * - * Copyright 2011 Collabora Ltd. + * Copyright 2012 Red Hat Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -23,6 +23,7 @@ enum { PROP_0, + PROP_SERVICE, PROP_ATTRIBUTES, PROP_LABEL, PROP_LOCKED, @@ -38,17 +39,11 @@ typedef struct _GSecretItemPrivate { G_DEFINE_TYPE (GSecretItem, gsecret_item, G_TYPE_DBUS_PROXY); -static GSecretItemPrivate * -gsecret_item_private_get (GSecretItem *self) -{ - return G_TYPE_INSTANCE_GET_PRIVATE (self, GSECRET_TYPE_ITEM, GSecretItemPrivate); -} - static void gsecret_item_init (GSecretItem *self) { - GSecretItemPrivate *pv = gsecret_item_private_get (self); - pv->cancellable = g_cancellable_new (); + self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GSECRET_TYPE_ITEM, GSecretItemPrivate); + self->pv->cancellable = g_cancellable_new (); } static void @@ -92,17 +87,23 @@ gsecret_item_set_property (GObject *obj, GParamSpec *pspec) { GSecretItem *self = GSECRET_ITEM (obj); - GSecretItemPrivate *pv = gsecret_item_private_get (self); switch (prop_id) { + case PROP_SERVICE: + g_return_if_fail (self->pv->service == NULL); + self->pv->service = g_value_get_object (value); + if (self->pv->service) + g_object_add_weak_pointer (G_OBJECT (self->pv->service), + (gpointer *)&self->pv->service); + break; case PROP_ATTRIBUTES: gsecret_item_set_attributes (self, g_value_get_boxed (value), - pv->cancellable, on_set_attributes, + self->pv->cancellable, on_set_attributes, g_object_ref (self)); break; case PROP_LABEL: gsecret_item_set_label (self, g_value_get_string (value), - pv->cancellable, on_set_label, + self->pv->cancellable, on_set_label, g_object_ref (self)); break; default: @@ -120,6 +121,9 @@ gsecret_item_get_property (GObject *obj, GSecretItem *self = GSECRET_ITEM (obj); switch (prop_id) { + case PROP_SERVICE: + g_value_set_object (value, self->pv->service); + break; case PROP_ATTRIBUTES: g_value_take_boxed (value, gsecret_item_get_attributes (self)); break; @@ -145,23 +149,24 @@ static void gsecret_item_dispose (GObject *obj) { GSecretItem *self = GSECRET_ITEM (obj); - GSecretItemPrivate *pv = gsecret_item_private_get (self); - g_clear_object (&pv->service); - g_cancellable_cancel (pv->cancellable); + g_cancellable_cancel (self->pv->cancellable); - G_OBJECT_GET_CLASS (obj)->dispose (obj); + G_OBJECT_CLASS (gsecret_item_parent_class)->dispose (obj); } static void gsecret_item_finalize (GObject *obj) { GSecretItem *self = GSECRET_ITEM (obj); - GSecretItemPrivate *pv = gsecret_item_private_get (self); - g_clear_object (&pv->cancellable); + if (self->pv->service) + g_object_remove_weak_pointer (G_OBJECT (self->pv->service), + (gpointer *)&self->pv->service); - G_OBJECT_GET_CLASS (obj)->finalize (obj); + g_object_unref (self->pv->cancellable); + + G_OBJECT_CLASS (gsecret_item_parent_class)->finalize (obj); } static void @@ -216,27 +221,48 @@ gsecret_item_class_init (GSecretItemClass *klass) proxy_class->g_properties_changed = gsecret_item_properties_changed; + g_object_class_install_property (gobject_class, PROP_SERVICE, + g_param_spec_object ("service", "Service", "Secret Service", + GSECRET_TYPE_SERVICE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ATTRIBUTES, g_param_spec_boxed ("attributes", "Attributes", "Item attributes", - G_TYPE_HASH_TABLE, G_PARAM_READWRITE)); + G_TYPE_HASH_TABLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_LABEL, g_param_spec_string ("label", "Label", "Item label", - NULL, G_PARAM_READWRITE)); + NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_LOCKED, g_param_spec_boolean ("locked", "Locked", "Item locked", - TRUE, G_PARAM_READABLE)); + TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_CREATED, g_param_spec_uint64 ("created", "Created", "Item creation date", - 0UL, G_MAXUINT64, 0UL, G_PARAM_READWRITE)); + 0UL, G_MAXUINT64, 0UL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_MODIFIED, g_param_spec_uint64 ("modified", "Modified", "Item modified date", - 0UL, G_MAXUINT64, 0UL, G_PARAM_READWRITE)); + 0UL, G_MAXUINT64, 0UL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_type_class_add_private (gobject_class, sizeof (GSecretItemPrivate)); } +#if 0 +static void +all_properties_changed (GSecretItem *item) +{ + GObject *obj = G_OBJECT (item); + gchar **property_names; + guint i; + + property_names = g_dbus_proxy_get_cached_property_names (G_DBUS_PROXY (item)); + for (i = 0; property_names != NULL && property_names[i] != NULL; i++) + handle_property_changed (obj, property_names[i]); + g_strfreev (property_names); +} +#endif + void gsecret_item_new (GSecretService *service, const gchar *item_path, @@ -263,6 +289,7 @@ gsecret_item_new (GSecretService *service, "g-connection", g_dbus_proxy_get_connection (proxy), "g-object-path", item_path, "g-interface-name", GSECRET_ITEM_INTERFACE, + "service", service, NULL); } @@ -272,16 +299,25 @@ gsecret_item_new_finish (GAsyncResult *result, { GObject *object; GObject *source_object; + GDBusProxy *proxy; source_object = g_async_result_get_source_object (result); object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), result, error); g_object_unref (source_object); - if (object != NULL) - return GSECRET_ITEM (object); - else + if (object == NULL) return NULL; + + proxy = G_DBUS_PROXY (object); + if (!_gsecret_util_have_cached_properties (proxy)) { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, + "No such secret item at path: %s", g_dbus_proxy_get_object_path (proxy)); + g_object_unref (object); + return NULL; + } + + return GSECRET_ITEM (object); } GSecretItem * @@ -290,30 +326,29 @@ gsecret_item_new_sync (GSecretService *service, GCancellable *cancellable, GError **error) { - GInitable *initable; - GDBusProxy *proxy; - - proxy = G_DBUS_PROXY (service); + GSecretSync *sync; + GSecretItem *item; g_return_val_if_fail (GSECRET_IS_SERVICE (service), NULL); g_return_val_if_fail (item_path != NULL, NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - initable = g_initable_new (GSECRET_TYPE_ITEM, - cancellable, - error, - "g-flags", G_DBUS_CALL_FLAGS_NONE, - "g-interface-info", _gsecret_gen_item_interface_info (), - "g-name", g_dbus_proxy_get_name (proxy), - "g-connection", g_dbus_proxy_get_connection (proxy), - "g-object-path", item_path, - "g-interface-name", GSECRET_ITEM_INTERFACE, - NULL); + sync = _gsecret_sync_new (); + g_main_context_push_thread_default (sync->context); - if (initable != NULL) - return GSECRET_ITEM (initable); - else - return NULL; + // TODO: xxxxx; + gsecret_item_new (service, item_path, cancellable, + _gsecret_sync_on_result, sync); + + g_main_loop_run (sync->loop); + + item = gsecret_item_new_finish (sync->result, error); + + g_main_context_pop_thread_default (sync->context); + _gsecret_sync_free (sync); + + return item; } void @@ -335,8 +370,10 @@ on_item_deleted (GObject *source, GSecretItem *self = GSECRET_ITEM (g_async_result_get_source_object (user_data)); GError *error = NULL; - if (gsecret_service_delete_path_finish (GSECRET_SERVICE (source), result, &error)) + if (gsecret_service_delete_path_finish (GSECRET_SERVICE (source), result, &error)) { + g_simple_async_result_set_op_res_gboolean (res, TRUE); g_object_run_dispose (G_OBJECT (self)); + } if (error != NULL) g_simple_async_result_take_error (res, error); @@ -352,19 +389,17 @@ gsecret_item_delete (GSecretItem *self, GAsyncReadyCallback callback, gpointer user_data) { - GSecretItemPrivate *pv; GSimpleAsyncResult *res; const gchar *object_path; g_return_if_fail (GSECRET_IS_ITEM (self)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); - pv = gsecret_item_private_get (self); object_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (self)); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gsecret_item_delete); - gsecret_service_delete_path (pv->service, object_path, cancellable, + gsecret_service_delete_path (self->pv->service, object_path, cancellable, on_item_deleted, g_object_ref (res)); g_object_unref (res); @@ -375,13 +410,19 @@ gsecret_item_delete_finish (GSecretItem *self, GAsyncResult *result, GError **error) { - GSecretItemPrivate *pv; + GSimpleAsyncResult *res; g_return_val_if_fail (GSECRET_IS_ITEM (self), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), + gsecret_item_delete), FALSE); - pv = gsecret_item_private_get (self); - return gsecret_service_delete_path_finish (pv->service, result, error); + res = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (res, error)) + return FALSE; + + return g_simple_async_result_get_op_res_gboolean (res); } gboolean @@ -411,31 +452,47 @@ gsecret_item_delete_sync (GSecretItem *self, return ret; } +typedef struct { + GCancellable *cancellable; + GSecretValue *value; +} GetClosure; + +static void +get_closure_free (gpointer data) +{ + GetClosure *closure = data; + g_clear_object (&closure->cancellable); + gsecret_value_unref (closure->value); + g_slice_free (GetClosure, closure); +} + static void on_item_get_secret_ready (GObject *source, GAsyncResult *result, gpointer user_data) { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); GSecretItem *self = GSECRET_ITEM (g_async_result_get_source_object (user_data)); - GSecretItemPrivate *pv = gsecret_item_private_get (self); + GetClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + GSecretSession *session; GError *error = NULL; - GSecretValue *value; - GVariant *ret; + GVariant *retval; + GVariant *child; - ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, &error); + retval = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, &error); if (error == NULL) { - value = _gsecret_service_decode_secret (pv->service, ret); - if (value == NULL) { + child = g_variant_get_child_value (retval, 0); + g_variant_unref (retval); + + session = _gsecret_service_get_session (self->pv->service); + closure->value = _gsecret_session_decode_secret (session, child); + g_variant_unref (child); + + if (closure->value == NULL) g_set_error (&error, GSECRET_ERROR, GSECRET_ERROR_PROTOCOL, _("Received invalid secret from the secret storage")); - } - g_object_unref (ret); } if (error != NULL) g_simple_async_result_take_error (res, error); - else - g_simple_async_result_set_op_res_gpointer (res, value, - gsecret_value_unref); g_simple_async_result_complete (res); g_object_unref (res); @@ -446,13 +503,11 @@ on_service_ensure_session (GObject *source, GAsyncResult *result, gpointer user_ { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); GSecretItem *self = GSECRET_ITEM (g_async_result_get_source_object (user_data)); - GSecretItemPrivate *pv = gsecret_item_private_get (self); - GError *error = NULL; - GCancellable *cancellable = NULL; + GetClosure *closure = g_simple_async_result_get_op_res_gpointer (res); const gchar *session_path; + GError *error = NULL; - session_path = _gsecret_service_ensure_session_finish (pv->service, result, - &cancellable, &error); + session_path = gsecret_service_ensure_session_finish (self->pv->service, result, &error); if (error != NULL) { g_simple_async_result_take_error (res, error); g_simple_async_result_complete (res); @@ -460,31 +515,33 @@ on_service_ensure_session (GObject *source, GAsyncResult *result, gpointer user_ } else { g_assert (session_path != NULL && session_path[0] != '\0'); g_dbus_proxy_call (G_DBUS_PROXY (self), "GetSecret", - g_variant_new ("o", session_path), - G_DBUS_CALL_FLAGS_NONE, -1, cancellable, + g_variant_new ("(o)", session_path), + G_DBUS_CALL_FLAGS_NONE, -1, closure->cancellable, on_item_get_secret_ready, g_object_ref (res)); } - g_clear_object (&cancellable); g_object_unref (res); } void -gsecret_item_get_secret (GSecretItem *self, GCancellable *cancellable, - GAsyncReadyCallback callback, gpointer user_data) +gsecret_item_get_secret (GSecretItem *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { GSimpleAsyncResult *res; - GSecretItemPrivate *pv; - + GetClosure *closure; g_return_if_fail (GSECRET_IS_ITEM (self)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gsecret_item_get_secret); + closure = g_slice_new0 (GetClosure); + closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + g_simple_async_result_set_op_res_gpointer (res, closure, get_closure_free); - pv = gsecret_item_private_get (self); - gsecret_service_ensure_session (pv->service, cancellable, + gsecret_service_ensure_session (self->pv->service, cancellable, on_service_ensure_session, g_object_ref (res)); @@ -492,10 +549,12 @@ gsecret_item_get_secret (GSecretItem *self, GCancellable *cancellable, } GSecretValue* -gsecret_item_get_secret_finish (GSecretItem *self, GAsyncResult *result, +gsecret_item_get_secret_finish (GSecretItem *self, + GAsyncResult *result, GError **error) { GSimpleAsyncResult *res; + GetClosure *closure; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), gsecret_item_get_secret), NULL); @@ -504,7 +563,8 @@ gsecret_item_get_secret_finish (GSecretItem *self, GAsyncResult *result, if (g_simple_async_result_propagate_error (res, error)) return NULL; - return gsecret_value_ref (g_simple_async_result_get_op_res_gpointer (res)); + closure = g_simple_async_result_get_op_res_gpointer (res); + return closure->value ? gsecret_value_ref (closure->value) : NULL; } GSecretValue* @@ -558,18 +618,13 @@ gsecret_item_set_attributes (GSecretItem *self, GAsyncReadyCallback callback, gpointer user_data) { - GVariant *variant; - g_return_if_fail (GSECRET_IS_ITEM (self)); g_return_if_fail (attributes != NULL); - variant = _gsecret_util_variant_for_attributes (attributes); - - _gsecret_util_set_property (G_DBUS_PROXY (self), "Attributes", variant, + _gsecret_util_set_property (G_DBUS_PROXY (self), "Attributes", + _gsecret_util_variant_for_attributes (attributes), gsecret_item_set_attributes, cancellable, callback, user_data); - - g_variant_unref (variant); } gboolean @@ -590,20 +645,12 @@ gsecret_item_set_attributes_sync (GSecretItem *self, GCancellable *cancellable, GError **error) { - GVariant *variant; - gboolean ret; - g_return_val_if_fail (GSECRET_IS_ITEM (self), FALSE); g_return_val_if_fail (attributes != NULL, FALSE); - variant = _gsecret_util_variant_for_attributes (attributes); - - ret = _gsecret_util_set_property_sync (G_DBUS_PROXY (self), "Attributes", - variant, cancellable, error); - - g_variant_unref (variant); - - return ret; + return _gsecret_util_set_property_sync (G_DBUS_PROXY (self), "Attributes", + _gsecret_util_variant_for_attributes (attributes), + cancellable, error); } gchar * diff --git a/library/gsecret-item.h b/library/gsecret-item.h index a8a7352..75e5dd2 100644 --- a/library/gsecret-item.h +++ b/library/gsecret-item.h @@ -1,6 +1,6 @@ /* GSecret - GLib wrapper for Secret Service * - * Copyright 2011 Collabora Ltd. + * Copyright 2012 Red Hat Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -29,10 +29,11 @@ G_BEGIN_DECLS #define GSECRET_ITEM_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), GSECRET_TYPE_ITEM, GSecretItemClass)) typedef struct _GSecretItemClass GSecretItemClass; +typedef struct _GSecretItemPrivate GSecretItemPrivate; struct _GSecretItem { GDBusProxy parent_instance; - gpointer padding; + GSecretItemPrivate *pv;; }; struct _GSecretItemClass { diff --git a/library/gsecret-private.h b/library/gsecret-private.h index 229bd65..8ebf4b0 100644 --- a/library/gsecret-private.h +++ b/library/gsecret-private.h @@ -26,6 +26,8 @@ typedef struct { GMainLoop *loop; } GSecretSync; +typedef struct _GSecretSession GSecretSession; + #define GSECRET_SERVICE_PATH "/org/freedesktop/secrets" #define GSECRET_SERVICE_BUS_NAME "org.freedesktop.Secret.Service" @@ -93,6 +95,8 @@ gboolean _gsecret_util_set_property_sync (GDBusProxy *prox GCancellable *cancellable, GError **error); +gboolean _gsecret_util_have_cached_properties (GDBusProxy *proxy); + void _gsecret_service_set_default_bus_name (const gchar *bus_name); GSecretService * _gsecret_service_bare_instance (GDBusConnection *connection, @@ -107,16 +111,10 @@ void _gsecret_service_bare_connect (const gchar *bus GSecretService * _gsecret_service_bare_connect_finish (GAsyncResult *result, GError **error); -GVariant * _gsecret_service_encode_secret (GSecretService *self, - GSecretValue *value); +GSecretSession * _gsecret_service_get_session (GSecretService *self); -GSecretValue * _gsecret_service_decode_secret (GSecretService *service, - GVariant *encoded); - -const gchar * _gsecret_service_ensure_session_finish (GSecretService *self, - GAsyncResult *result, - GCancellable **cancellable, - GError **error); +void _gsecret_service_take_session (GSecretService *self, + GSecretSession *session); GSecretItem * _gsecret_service_find_item_instance (GSecretService *self, const gchar *item_path); @@ -126,6 +124,26 @@ GSecretItem * _gsecret_collection_find_item_instance (GSecretCollectio gchar * _gsecret_value_unref_to_password (GSecretValue *value); +void _gsecret_session_free (gpointer data); + +const gchar * _gsecret_session_get_algorithms (GSecretSession *session); + +const gchar * _gsecret_session_get_path (GSecretSession *session); + +void _gsecret_session_open (GSecretService *service, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GSecretSession * _gsecret_session_open_finish (GAsyncResult *result, + GError **error); + +GVariant * _gsecret_session_encode_secret (GSecretSession *session, + GSecretValue *value); + +GSecretValue * _gsecret_session_decode_secret (GSecretSession *session, + GVariant *encoded); + G_END_DECLS #endif /* __G_SERVICE_H___ */ diff --git a/library/gsecret-service.c b/library/gsecret-service.c index f3f740b..be26d88 100644 --- a/library/gsecret-service.c +++ b/library/gsecret-service.c @@ -1,6 +1,7 @@ /* GSecret - GLib wrapper for Secret Service * * Copyright 2011 Collabora Ltd. + * Copyright 2012 Red Hat Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -20,13 +21,6 @@ #include "gsecret-types.h" #include "gsecret-value.h" -#ifdef WITH_GCRYPT -#include "egg/egg-dh.h" -#include "egg/egg-hkdf.h" -#include "egg/egg-libgcrypt.h" -#endif - -#include "egg/egg-hex.h" #include "egg/egg-secure-memory.h" #include @@ -40,21 +34,6 @@ EGG_SECURE_DECLARE (secret_service); static const gchar *default_bus_name = GSECRET_SERVICE_BUS_NAME; -#define ALGORITHMS_AES "dh-ietf1024-sha256-aes128-cbc-pkcs7" -#define ALGORITHMS_PLAIN "plain" - -typedef struct { - gchar *path; - const gchar *algorithms; -#ifdef WITH_GCRYPT - gcry_mpi_t prime; - gcry_mpi_t privat; - gcry_mpi_t publi; -#endif - gpointer key; - gsize n_key; -} GSecretSession; - enum { PROP_0, PROP_COLLECTIONS @@ -75,24 +54,6 @@ static gpointer service_instance = NULL; G_DEFINE_TYPE (GSecretService, gsecret_service, G_TYPE_DBUS_PROXY); -static void -gsecret_session_free (gpointer data) -{ - GSecretSession *session = data; - - if (session == NULL) - return; - - g_free (session->path); -#ifdef WITH_GCRYPT - gcry_mpi_release (session->publi); - gcry_mpi_release (session->privat); - gcry_mpi_release (session->prime); -#endif - egg_secure_free (session->key); - g_free (session); -} - static GHashTable * collections_table_new (void) { @@ -144,7 +105,7 @@ gsecret_service_finalize (GObject *obj) { GSecretService *self = GSECRET_SERVICE (obj); - gsecret_session_free (self->pv->session); + _gsecret_session_free (self->pv->session); g_hash_table_destroy (self->pv->collections); g_clear_object (&self->pv->cancellable); @@ -315,6 +276,7 @@ load_collections_perform (GSecretService *self, g_mutex_unlock (&self->pv->mutex); if (collection == NULL) { + // TODO: xxxxx; gsecret_collection_new (self, collection_path, closure->cancellable, on_collection_loading, g_object_ref (res)); closure->collections_loading++; @@ -349,6 +311,7 @@ handle_property_changed (GSecretService *self, g_warning ("couldn't retrieve Service Collections property"); g_simple_async_result_complete (res); } else { + // TODO: yyyy; load_collections_perform (self, res, value); g_variant_unref (value); } @@ -392,6 +355,9 @@ gsecret_service_class_init (GSecretServiceClass *klass) klass->prompt_async = gsecret_service_real_prompt_async; klass->prompt_finish = gsecret_service_real_prompt_finish; + klass->item_gtype = GSECRET_TYPE_ITEM; + klass->collection_gtype = GSECRET_TYPE_COLLECTION; + g_type_class_add_private (klass, sizeof (GSecretServicePrivate)); } @@ -744,6 +710,35 @@ _gsecret_service_find_item_instance (GSecretService *self, return item; } +GSecretSession * +_gsecret_service_get_session (GSecretService *self) +{ + GSecretSession *session; + + g_return_val_if_fail (GSECRET_IS_SERVICE (self), NULL); + + g_mutex_lock (&self->pv->mutex); + session = self->pv->session; + g_mutex_lock (&self->pv->mutex); + + return session; +} + +void +_gsecret_service_take_session (GSecretService *self, + GSecretSession *session) +{ + g_return_if_fail (GSECRET_IS_SERVICE (self)); + g_return_if_fail (session != NULL); + + g_mutex_lock (&self->pv->mutex); + if (self->pv->session == NULL) + self->pv->session = session; + else + _gsecret_session_free (session); + g_mutex_unlock (&self->pv->mutex); +} + const gchar * gsecret_service_get_session_algorithms (GSecretService *self) { @@ -754,7 +749,7 @@ gsecret_service_get_session_algorithms (GSecretService *self) g_mutex_lock (&self->pv->mutex); session = self->pv->session; - algorithms = session ? session->algorithms : NULL; + algorithms = session ? _gsecret_session_get_algorithms (session) : NULL; g_mutex_unlock (&self->pv->mutex); /* Session never changes once established, so can return const */ @@ -771,266 +766,13 @@ gsecret_service_get_session_path (GSecretService *self) g_mutex_lock (&self->pv->mutex); session = self->pv->session; - path = session ? session->path : NULL; + path = session ? _gsecret_session_get_path (session) : NULL; g_mutex_unlock (&self->pv->mutex); /* Session never changes once established, so can return const */ return path; } -#ifdef WITH_GCRYPT - -static GVariant * -request_open_session_aes (GSecretSession *session) -{ - gcry_error_t gcry; - gcry_mpi_t base; - unsigned char *buffer; - size_t n_buffer; - GVariant *argument; - - g_assert (session->prime == NULL); - g_assert (session->privat == NULL); - g_assert (session->publi == NULL); - - /* Initialize our local parameters and values */ - if (!egg_dh_default_params ("ietf-ike-grp-modp-1536", - &session->prime, &base)) - g_return_val_if_reached (NULL); - -#if 0 - g_printerr ("\n lib prime: "); - gcry_mpi_dump (session->prime); - g_printerr ("\n lib base: "); - gcry_mpi_dump (base); - g_printerr ("\n"); -#endif - - if (!egg_dh_gen_pair (session->prime, base, 0, - &session->publi, &session->privat)) - g_return_val_if_reached (NULL); - gcry_mpi_release (base); - - gcry = gcry_mpi_aprint (GCRYMPI_FMT_USG, &buffer, &n_buffer, session->publi); - g_return_val_if_fail (gcry == 0, NULL); - argument = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), - buffer, n_buffer, TRUE, - gcry_free, buffer); - - return g_variant_new ("(sv)", ALGORITHMS_AES, argument); -} - -static gboolean -response_open_session_aes (GSecretSession *session, - GVariant *response) -{ - gconstpointer buffer; - GVariant *argument; - const gchar *sig; - gsize n_buffer; - gcry_mpi_t peer; - gcry_error_t gcry; - gpointer ikm; - gsize n_ikm; - - sig = g_variant_get_type_string (response); - g_return_val_if_fail (sig != NULL, FALSE); - - if (!g_str_equal (sig, "(vo)")) { - g_warning ("invalid OpenSession() response from daemon with signature: %s", sig); - return FALSE; - } - - g_assert (session->path == NULL); - g_variant_get (response, "(vo)", &argument, &session->path); - - buffer = g_variant_get_fixed_array (argument, &n_buffer, sizeof (guchar)); - gcry = gcry_mpi_scan (&peer, GCRYMPI_FMT_USG, buffer, n_buffer, NULL); - g_return_val_if_fail (gcry == 0, FALSE); - g_variant_unref (argument); - -#if 0 - g_printerr (" lib publi: "); - gcry_mpi_dump (session->publi); - g_printerr ("\n lib peer: "); - gcry_mpi_dump (peer); - g_printerr ("\n"); -#endif - - ikm = egg_dh_gen_secret (peer, session->privat, session->prime, &n_ikm); - gcry_mpi_release (peer); - -#if 0 - g_printerr (" lib ikm: %s\n", egg_hex_encode (ikm, n_ikm)); -#endif - - if (ikm == NULL) { - g_warning ("couldn't negotiate a valid AES session key"); - g_free (session->path); - session->path = NULL; - return FALSE; - } - - session->n_key = 16; - session->key = egg_secure_alloc (session->n_key); - if (!egg_hkdf_perform ("sha256", ikm, n_ikm, NULL, 0, NULL, 0, - session->key, session->n_key)) - g_return_val_if_reached (FALSE); - egg_secure_free (ikm); - - session->algorithms = ALGORITHMS_AES; - return TRUE; -} - -#endif /* WITH_GCRYPT */ - -static GVariant * -request_open_session_plain (GSecretSession *session) -{ - GVariant *argument = g_variant_new_string (""); - return g_variant_new ("(sv)", "plain", argument); -} - -static gboolean -response_open_session_plain (GSecretSession *session, - GVariant *response) -{ - GVariant *argument; - const gchar *sig; - - sig = g_variant_get_type_string (response); - g_return_val_if_fail (sig != NULL, FALSE); - - if (!g_str_equal (sig, "(vo)")) { - g_warning ("invalid OpenSession() response from daemon with signature: %s", - g_variant_get_type_string (response)); - return FALSE; - } - - g_assert (session->path == NULL); - g_variant_get (response, "(vo)", &argument, &session->path); - g_variant_unref (argument); - - g_assert (session->key == NULL); - g_assert (session->n_key == 0); - - session->algorithms = ALGORITHMS_PLAIN; - return TRUE; -} - -typedef struct { - GCancellable *cancellable; - GSecretSession *session; -} OpenSessionClosure; - -static void -open_session_closure_free (gpointer data) -{ - OpenSessionClosure *closure = data; - g_assert (closure); - g_clear_object (&closure->cancellable); - gsecret_session_free (closure->session); - g_free (closure); -} - -static void -on_service_open_session_plain (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); - OpenSessionClosure *closure = g_simple_async_result_get_op_res_gpointer (res); - GSecretService *self = GSECRET_SERVICE (source); - GError *error = NULL; - GVariant *response; - - response = g_dbus_proxy_call_finish (G_DBUS_PROXY (self), result, &error); - - /* A successful response, decode it */ - if (response != NULL) { - if (response_open_session_plain (closure->session, response)) { - - g_mutex_lock (&self->pv->mutex); - if (self->pv->session == NULL) { - self->pv->session = closure->session; - closure->session = NULL; /* Service takes ownership */ - } - g_mutex_unlock (&self->pv->mutex); - - } else { - g_simple_async_result_set_error (res, GSECRET_ERROR, GSECRET_ERROR_PROTOCOL, - _("Couldn't communicate with the secret storage")); - } - - g_simple_async_result_complete (res); - g_variant_unref (response); - - } else { - g_simple_async_result_take_error (res, error); - g_simple_async_result_complete (res); - } - - g_object_unref (res); -} - -#ifdef WITH_GCRYPT - -static void -on_service_open_session_aes (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); - OpenSessionClosure * closure = g_simple_async_result_get_op_res_gpointer (res); - GSecretService *self = GSECRET_SERVICE (source); - GError *error = NULL; - GVariant *response; - - response = g_dbus_proxy_call_finish (G_DBUS_PROXY (self), result, &error); - - /* A successful response, decode it */ - if (response != NULL) { - if (response_open_session_aes (closure->session, response)) { - - g_mutex_lock (&self->pv->mutex); - if (self->pv->session == NULL) { - self->pv->session = closure->session; - closure->session = NULL; /* Service takes ownership */ - } - g_mutex_unlock (&self->pv->mutex); - - } else { - g_simple_async_result_set_error (res, GSECRET_ERROR, GSECRET_ERROR_PROTOCOL, - _("Couldn't communicate with the secret storage")); - } - - g_simple_async_result_complete (res); - g_variant_unref (response); - - } else { - /* AES session not supported, request a plain session */ - if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED)) { - g_dbus_proxy_call (G_DBUS_PROXY (source), "OpenSession", - request_open_session_plain (closure->session), - G_DBUS_CALL_FLAGS_NONE, -1, - closure->cancellable, on_service_open_session_plain, - g_object_ref (res)); - g_error_free (error); - - /* Other errors result in a failure */ - } else { - g_simple_async_result_take_error (res, error); - g_simple_async_result_complete (res); - } - } - - g_object_unref (res); -} - - - -#endif /* WITH_GCRYPT */ - void gsecret_service_ensure_session (GSecretService *self, GCancellable *cancellable, @@ -1038,72 +780,24 @@ gsecret_service_ensure_session (GSecretService *self, gpointer user_data) { GSimpleAsyncResult *res; - OpenSessionClosure *closure; GSecretSession *session; g_return_if_fail (GSECRET_IS_SERVICE (self)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); - res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, - gsecret_service_ensure_session); - g_mutex_lock (&self->pv->mutex); session = self->pv->session; g_mutex_unlock (&self->pv->mutex); - /* If we have no session, then request an AES session */ if (session == NULL) { + _gsecret_session_open (self, cancellable, callback, user_data); - closure = g_new (OpenSessionClosure, 1); - closure->cancellable = cancellable ? g_object_ref (cancellable) : cancellable; - closure->session = g_new0 (GSecretSession, 1); - g_simple_async_result_set_op_res_gpointer (res, closure, open_session_closure_free); - - g_dbus_proxy_call (G_DBUS_PROXY (self), "OpenSession", -#ifdef WITH_GCRYPT - request_open_session_aes (closure->session), - G_DBUS_CALL_FLAGS_NONE, -1, - cancellable, on_service_open_session_aes, -#else - request_open_session_plain (closure->session), - G_DBUS_CALL_FLAGS_NONE, -1, - cancellable, on_service_open_session_plain, -#endif - g_object_ref (res)); - - /* Already have a session */ } else { + res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + gsecret_service_ensure_session); g_simple_async_result_complete_in_idle (res); + g_object_unref (res); } - - g_object_unref (res); -} - -const gchar * -_gsecret_service_ensure_session_finish (GSecretService *self, - GAsyncResult *result, - GCancellable **cancellable, - GError **error) -{ - OpenSessionClosure *closure; - - g_return_val_if_fail (GSECRET_IS_SERVICE (self), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - g_return_val_if_fail (cancellable == NULL || *cancellable == NULL, NULL); - - g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), - gsecret_service_ensure_session), NULL); - - if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) - return NULL; - - if (cancellable) { - closure = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)); - *cancellable = closure->cancellable ? g_object_ref (closure->cancellable) : NULL; - } - - /* The session we have should never change once created */ - return gsecret_service_get_session_path (self); } const gchar * @@ -1111,7 +805,17 @@ gsecret_service_ensure_session_finish (GSecretService *self, GAsyncResult *result, GError **error) { - return _gsecret_service_ensure_session_finish (self, result, NULL, error); + g_return_val_if_fail (GSECRET_IS_SERVICE (self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (!g_simple_async_result_is_valid (result, G_OBJECT (self), + gsecret_service_ensure_session)) { + if (!_gsecret_session_open_finish (result, error)) + return NULL; + } + + g_return_val_if_fail (self->pv->session != NULL, NULL); + return gsecret_service_get_session_path (self->pv->session); } const gchar * @@ -1142,328 +846,47 @@ gsecret_service_ensure_session_sync (GSecretService *self, return path; } -#ifdef WITH_GCRYPT - -static gboolean -pkcs7_unpad_bytes_in_place (guchar *padded, - gsize *n_padded) -{ - gsize n_pad, i; - - if (*n_padded == 0) - return FALSE; - - n_pad = padded[*n_padded - 1]; - - /* Validate the padding */ - if (n_pad == 0 || n_pad > 16) - return FALSE; - if (n_pad > *n_padded) - return FALSE; - for (i = *n_padded - n_pad; i < *n_padded; ++i) { - if (padded[i] != n_pad) - return FALSE; - } - - /* The last bit of data */ - *n_padded -= n_pad; - - /* Null teriminate as a courtesy */ - padded[*n_padded] = 0; - - return TRUE; -} - -static GSecretValue * -service_decode_aes_secret (GSecretSession *session, - gconstpointer param, - gsize n_param, - gconstpointer value, - gsize n_value, - const gchar *content_type) -{ - gcry_cipher_hd_t cih; - gsize n_padded; - gcry_error_t gcry; - guchar *padded; - gsize pos; - - if (n_param != 16) { - g_message ("received an encrypted secret structure with invalid parameter"); - return NULL; - } - - if (n_value == 0 || n_value % 16 != 0) { - g_message ("received an encrypted secret structure with bad secret length"); - return NULL; - } - - gcry = gcry_cipher_open (&cih, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0); - if (gcry != 0) { - g_warning ("couldn't create AES cipher: %s", gcry_strerror (gcry)); - return NULL; - } - #if 0 - g_printerr (" lib iv: %s\n", egg_hex_encode (param, n_param)); +void +gsecret_service_ensure_collections (GSecretService *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + + g_return_if_fail (GSECRET_IS_SERVICE (self)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + +} + +gboolean +gsecret_service_ensure_collections_finish (GSecretService *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (GSECRET_IS_SERVICE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), + gsecret_service_ensure_collections), FALSE); + + +} + +gboolean +gsecret_service_ensure_collections_sync (GSecretService *self, + GCancellable *cancellable, + GError **error) +{ + g_return_val_if_fail (GSECRET_IS_SERVICE (self), FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + +} #endif - gcry = gcry_cipher_setiv (cih, param, n_param); - g_return_val_if_fail (gcry == 0, NULL); - -#if 0 - g_printerr (" lib key: %s\n", egg_hex_encode (session->key, session->n_key)); -#endif - - gcry = gcry_cipher_setkey (cih, session->key, session->n_key); - g_return_val_if_fail (gcry == 0, NULL); - - /* Copy the memory buffer */ - n_padded = n_value; - padded = egg_secure_alloc (n_padded); - memcpy (padded, value, n_padded); - - /* Perform the decryption */ - for (pos = 0; pos < n_padded; pos += 16) { - gcry = gcry_cipher_decrypt (cih, (guchar*)padded + pos, 16, NULL, 0); - g_return_val_if_fail (gcry == 0, FALSE); - } - - gcry_cipher_close (cih); - - /* Unpad the resulting value */ - if (!pkcs7_unpad_bytes_in_place (padded, &n_padded)) { - egg_secure_clear (padded, n_padded); - egg_secure_free (padded); - g_message ("received an invalid or unencryptable secret"); - return FALSE; - } - - return gsecret_value_new_full ((gchar *)padded, n_padded, content_type, egg_secure_free); -} - -#endif /* WITH_GCRYPT */ - -static GSecretValue * -service_decode_plain_secret (GSecretSession *session, - gconstpointer param, - gsize n_param, - gconstpointer value, - gsize n_value, - const gchar *content_type) -{ - if (n_param != 0) { - g_message ("received a plain secret structure with invalid parameter"); - return NULL; - } - - return gsecret_value_new (value, n_value, content_type); -} - -GSecretValue * -_gsecret_service_decode_secret (GSecretService *self, - GVariant *encoded) -{ - GSecretSession *session; - GSecretValue *result; - gconstpointer param; - gconstpointer value; - gchar *session_path; - gchar *content_type; - gsize n_param; - gsize n_value; - GVariant *vparam; - GVariant *vvalue; - - g_return_val_if_fail (GSECRET_IS_SERVICE (self), NULL); - g_return_val_if_fail (encoded, NULL); - - g_mutex_lock (&self->pv->mutex); - session = self->pv->session; - g_assert (session == NULL || session->path != NULL); - g_mutex_unlock (&self->pv->mutex); - - g_return_val_if_fail (session != NULL, NULL); - - /* Parsing (oayays) */ - g_variant_get_child (encoded, 0, "o", &session_path); - - if (session_path == NULL || !g_str_equal (session_path, session->path)) { - g_message ("received a secret encoded with wrong session: %s != %s", - session_path, session->path); - g_free (session_path); - return NULL; - } - - vparam = g_variant_get_child_value (encoded, 1); - param = g_variant_get_fixed_array (vparam, &n_param, sizeof (guchar)); - vvalue = g_variant_get_child_value (encoded, 2); - value = g_variant_get_fixed_array (vvalue, &n_value, sizeof (guchar)); - g_variant_get_child (encoded, 3, "s", &content_type); - -#ifdef WITH_GCRYPT - if (session->key != NULL) - result = service_decode_aes_secret (session, param, n_param, - value, n_value, content_type); - else -#endif - result = service_decode_plain_secret (session, param, n_param, - value, n_value, content_type); - - g_variant_unref (vparam); - g_variant_unref (vvalue); - g_free (content_type); - g_free (session_path); - - return result; -} - -#ifdef WITH_GCRYPT - -static guchar* -pkcs7_pad_bytes_in_secure_memory (gconstpointer secret, - gsize length, - gsize *n_padded) -{ - gsize n_pad; - guchar *padded; - - /* Pad the secret */ - *n_padded = ((length + 16) / 16) * 16; - g_assert (length < *n_padded); - g_assert (*n_padded > 0); - n_pad = *n_padded - length; - g_assert (n_pad > 0 && n_pad <= 16); - padded = egg_secure_alloc (*n_padded); - memcpy (padded, secret, length); - memset (padded + length, n_pad, n_pad); - return padded; -} - -static gboolean -service_encode_aes_secret (GSecretSession *session, - GSecretValue *value, - GVariantBuilder *builder) -{ - gcry_cipher_hd_t cih; - guchar *padded; - gsize n_padded, pos; - gcry_error_t gcry; - gpointer iv; - gconstpointer secret; - gsize n_secret; - GVariant *child; - - g_variant_builder_add (builder, "o", session->path); - - /* Create the cipher */ - gcry = gcry_cipher_open (&cih, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0); - if (gcry != 0) { - g_warning ("couldn't create AES cipher: %s", gcry_strerror (gcry)); - return FALSE; - } - - secret = gsecret_value_get (value, &n_secret); - - /* Perform the encoding here */ - padded = pkcs7_pad_bytes_in_secure_memory (secret, n_secret, &n_padded); - g_assert (padded != NULL); - - /* Setup the IV */ - iv = g_malloc0 (16); - gcry_create_nonce (iv, 16); - gcry = gcry_cipher_setiv (cih, iv, 16); - g_return_val_if_fail (gcry == 0, FALSE); - - /* Setup the key */ - gcry = gcry_cipher_setkey (cih, session->key, session->n_key); - g_return_val_if_fail (gcry == 0, FALSE); - - /* Perform the encryption */ - for (pos = 0; pos < n_padded; pos += 16) { - gcry = gcry_cipher_encrypt (cih, (guchar*)padded + pos, 16, NULL, 0); - g_return_val_if_fail (gcry == 0, FALSE); - } - - gcry_cipher_close (cih); - - child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), iv, 16, TRUE, g_free, iv); - g_variant_builder_add_value (builder, child); - g_variant_unref (child); - - child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), padded, n_padded, TRUE, egg_secure_free, padded); - g_variant_builder_add_value (builder, child); - g_variant_unref (child); - - g_variant_builder_add (builder, "s", gsecret_value_get_content_type (value)); - return TRUE; -} - -#endif /* WITH_GCRYPT */ - -static gboolean -service_encode_plain_secret (GSecretSession *session, - GSecretValue *value, - GVariantBuilder *builder) -{ - gconstpointer secret; - gsize n_secret; - GVariant *child; - - g_variant_builder_add (builder, "o", session->path); - - secret = gsecret_value_get (value, &n_secret); - - child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), "", 0, TRUE, NULL, NULL); - g_variant_builder_add_value (builder, child); - g_variant_unref (child); - - child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), secret, n_secret, TRUE, - gsecret_value_unref, gsecret_value_ref (value)); - g_variant_builder_add_value (builder, child); - g_variant_unref (child); - - g_variant_builder_add (builder, "s", gsecret_value_get_content_type (value)); - return TRUE; -} - -GVariant * -_gsecret_service_encode_secret (GSecretService *self, - GSecretValue *value) -{ - GVariantBuilder *builder; - GSecretSession *session; - GVariant *result = NULL; - GVariantType *type; - gboolean ret; - - g_return_val_if_fail (GSECRET_IS_SERVICE (self), NULL); - g_return_val_if_fail (value, NULL); - - g_mutex_lock (&self->pv->mutex); - session = self->pv->session; - g_assert (session == NULL || session->path != NULL); - g_mutex_unlock (&self->pv->mutex); - - g_return_val_if_fail (session != NULL, NULL); - - type = g_variant_type_new ("(oayays)"); - builder = g_variant_builder_new (type); - -#ifdef WITH_GCRYPT - if (session->key) - ret = service_encode_aes_secret (session, value, builder); - else -#endif - ret = service_encode_plain_secret (session, value, builder); - if (ret) - result = g_variant_builder_end (builder); - - g_variant_builder_unref (builder); - g_variant_type_free (type); - return result; -} - static void on_search_items_complete (GObject *source, GAsyncResult *result, @@ -1640,6 +1063,7 @@ search_load_item (GSecretService *self, item = _gsecret_service_find_item_instance (self, path); if (item == NULL) { + // TODO: xxxxxxxxxx; gsecret_item_new (self, path, closure->cancellable, on_search_loaded, g_object_ref (res)); closure->loading++; @@ -1875,14 +1299,16 @@ static GSecretValue * service_decode_get_secrets_first (GSecretService *self, GVariant *out) { + GSecretSession *session; + GSecretValue *value; GVariantIter *iter; GVariant *variant; - GSecretValue *value; const gchar *path; g_variant_get (out, "(a{o(oayays)})", &iter); while (g_variant_iter_next (iter, "{&o@(oayays)}", &path, &variant)) { - value = _gsecret_service_decode_secret (self, variant); + session = _gsecret_service_get_session (self); + value = _gsecret_session_decode_secret (session, variant); g_variant_unref (variant); break; } @@ -1894,17 +1320,19 @@ static GHashTable * service_decode_get_secrets_all (GSecretService *self, GVariant *out) { + GSecretSession *session; GVariantIter *iter; GVariant *variant; GHashTable *values; GSecretValue *value; gchar *path; + session = _gsecret_service_get_session (self); values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, gsecret_value_unref); g_variant_get (out, "(a{o(oayays)})", &iter); while (g_variant_iter_loop (iter, "{o@(oayays)}", &path, &variant)) { - value = _gsecret_service_decode_secret (self, variant); + value = _gsecret_session_decode_secret (session, variant); if (value && path) g_hash_table_insert (values, g_strdup (path), value); } @@ -2778,6 +2206,7 @@ gsecret_service_storev (GSecretService *self, gpointer user_data) { GSimpleAsyncResult *res; + GSecretSession *session; GVariant *attrs; StoreClosure *closure; GVariantBuilder builder; @@ -2808,9 +2237,10 @@ gsecret_service_storev (GSecretService *self, closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL; g_simple_async_result_set_op_res_gpointer (res, closure, store_closure_free); + session = _gsecret_service_get_session (self); params = g_variant_new ("(&a{sv}&(oayays)b)", g_variant_builder_end (&builder), - _gsecret_service_encode_secret (self, value), + _gsecret_session_encode_secret (session, value), TRUE); proxy = G_DBUS_PROXY (self); diff --git a/library/gsecret-service.h b/library/gsecret-service.h index 89d6280..197a1fa 100644 --- a/library/gsecret-service.h +++ b/library/gsecret-service.h @@ -1,6 +1,7 @@ /* GSecret - GLib wrapper for Secret Service * * Copyright 2011 Collabora Ltd. + * Copyright 2012 Red Hat Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -72,12 +73,12 @@ GSecretService * gsecret_service_get_finish (GAsyncResult GSecretService * gsecret_service_get_sync (GCancellable *cancellable, GError **error); -GList * gsecret_service_get_collections (GSecretService *self); - const gchar * gsecret_service_get_session_algorithms (GSecretService *self); const gchar * gsecret_service_get_session_path (GSecretService *self); +GList * gsecret_service_get_collections (GSecretService *self); + void gsecret_service_ensure_session (GSecretService *self, GCancellable *cancellable, GAsyncReadyCallback callback, @@ -91,6 +92,19 @@ const gchar * gsecret_service_ensure_session_sync (GSecretServi GCancellable *cancellable, GError **error); +void gsecret_service_ensure_collections (GSecretService *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gsecret_service_ensure_collections_finish (GSecretService *self, + GAsyncResult *result, + GError **error); + +gboolean gsecret_service_ensure_collections_sync (GSecretService *self, + GCancellable *cancellable, + GError **error); + void gsecret_service_search (GSecretService *self, GHashTable *attributes, GCancellable *cancellable, diff --git a/library/gsecret-session.c b/library/gsecret-session.c new file mode 100644 index 0000000..7548ad7 --- /dev/null +++ b/library/gsecret-session.c @@ -0,0 +1,671 @@ +/* GSecret - GLib wrapper for Secret Service + * + * Copyright 2011 Collabora Ltd. + * Copyright 2012 Red Hat Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + */ + +#include "config.h" + +#include "gsecret-private.h" + +#ifdef WITH_GCRYPT +#include "egg/egg-dh.h" +#include "egg/egg-hkdf.h" +#include "egg/egg-libgcrypt.h" +#endif + +#include "egg/egg-hex.h" +#include "egg/egg-secure-memory.h" + +#include + +EGG_SECURE_DECLARE (secret_session); + +#define ALGORITHMS_AES "dh-ietf1024-sha256-aes128-cbc-pkcs7" +#define ALGORITHMS_PLAIN "plain" + +struct _GSecretSession { + gchar *path; + const gchar *algorithms; +#ifdef WITH_GCRYPT + gcry_mpi_t prime; + gcry_mpi_t privat; + gcry_mpi_t publi; +#endif + gpointer key; + gsize n_key; +}; + +void +_gsecret_session_free (gpointer data) +{ + GSecretSession *session = data; + + if (session == NULL) + return; + + g_free (session->path); +#ifdef WITH_GCRYPT + gcry_mpi_release (session->publi); + gcry_mpi_release (session->privat); + gcry_mpi_release (session->prime); +#endif + egg_secure_free (session->key); + g_free (session); +} + +#ifdef WITH_GCRYPT + +static GVariant * +request_open_session_aes (GSecretSession *session) +{ + gcry_error_t gcry; + gcry_mpi_t base; + unsigned char *buffer; + size_t n_buffer; + GVariant *argument; + + g_assert (session->prime == NULL); + g_assert (session->privat == NULL); + g_assert (session->publi == NULL); + + /* Initialize our local parameters and values */ + if (!egg_dh_default_params ("ietf-ike-grp-modp-1536", + &session->prime, &base)) + g_return_val_if_reached (NULL); + +#if 0 + g_printerr ("\n lib prime: "); + gcry_mpi_dump (session->prime); + g_printerr ("\n lib base: "); + gcry_mpi_dump (base); + g_printerr ("\n"); +#endif + + if (!egg_dh_gen_pair (session->prime, base, 0, + &session->publi, &session->privat)) + g_return_val_if_reached (NULL); + gcry_mpi_release (base); + + gcry = gcry_mpi_aprint (GCRYMPI_FMT_USG, &buffer, &n_buffer, session->publi); + g_return_val_if_fail (gcry == 0, NULL); + argument = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), + buffer, n_buffer, TRUE, + gcry_free, buffer); + + return g_variant_new ("(sv)", ALGORITHMS_AES, argument); +} + +static gboolean +response_open_session_aes (GSecretSession *session, + GVariant *response) +{ + gconstpointer buffer; + GVariant *argument; + const gchar *sig; + gsize n_buffer; + gcry_mpi_t peer; + gcry_error_t gcry; + gpointer ikm; + gsize n_ikm; + + sig = g_variant_get_type_string (response); + g_return_val_if_fail (sig != NULL, FALSE); + + if (!g_str_equal (sig, "(vo)")) { + g_warning ("invalid OpenSession() response from daemon with signature: %s", sig); + return FALSE; + } + + g_assert (session->path == NULL); + g_variant_get (response, "(vo)", &argument, &session->path); + + buffer = g_variant_get_fixed_array (argument, &n_buffer, sizeof (guchar)); + gcry = gcry_mpi_scan (&peer, GCRYMPI_FMT_USG, buffer, n_buffer, NULL); + g_return_val_if_fail (gcry == 0, FALSE); + g_variant_unref (argument); + +#if 0 + g_printerr (" lib publi: "); + gcry_mpi_dump (session->publi); + g_printerr ("\n lib peer: "); + gcry_mpi_dump (peer); + g_printerr ("\n"); +#endif + + ikm = egg_dh_gen_secret (peer, session->privat, session->prime, &n_ikm); + gcry_mpi_release (peer); + +#if 0 + g_printerr (" lib ikm: %s\n", egg_hex_encode (ikm, n_ikm)); +#endif + + if (ikm == NULL) { + g_warning ("couldn't negotiate a valid AES session key"); + g_free (session->path); + session->path = NULL; + return FALSE; + } + + session->n_key = 16; + session->key = egg_secure_alloc (session->n_key); + if (!egg_hkdf_perform ("sha256", ikm, n_ikm, NULL, 0, NULL, 0, + session->key, session->n_key)) + g_return_val_if_reached (FALSE); + egg_secure_free (ikm); + + session->algorithms = ALGORITHMS_AES; + return TRUE; +} + +#endif /* WITH_GCRYPT */ + +static GVariant * +request_open_session_plain (GSecretSession *session) +{ + GVariant *argument = g_variant_new_string (""); + return g_variant_new ("(sv)", "plain", argument); +} + +static gboolean +response_open_session_plain (GSecretSession *session, + GVariant *response) +{ + GVariant *argument; + const gchar *sig; + + sig = g_variant_get_type_string (response); + g_return_val_if_fail (sig != NULL, FALSE); + + if (!g_str_equal (sig, "(vo)")) { + g_warning ("invalid OpenSession() response from daemon with signature: %s", + g_variant_get_type_string (response)); + return FALSE; + } + + g_assert (session->path == NULL); + g_variant_get (response, "(vo)", &argument, &session->path); + g_variant_unref (argument); + + g_assert (session->key == NULL); + g_assert (session->n_key == 0); + + session->algorithms = ALGORITHMS_PLAIN; + return TRUE; +} + +typedef struct { + GCancellable *cancellable; + GSecretSession *session; +} OpenSessionClosure; + +static void +open_session_closure_free (gpointer data) +{ + OpenSessionClosure *closure = data; + g_assert (closure); + g_clear_object (&closure->cancellable); + _gsecret_session_free (closure->session); + g_free (closure); +} + +static void +on_service_open_session_plain (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + OpenSessionClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + GSecretService *service = GSECRET_SERVICE (source); + GError *error = NULL; + GVariant *response; + + response = g_dbus_proxy_call_finish (G_DBUS_PROXY (service), result, &error); + + /* A successful response, decode it */ + if (response != NULL) { + if (response_open_session_plain (closure->session, response)) { + _gsecret_service_take_session (service, closure->session); + closure->session = NULL; + + } else { + g_simple_async_result_set_error (res, GSECRET_ERROR, GSECRET_ERROR_PROTOCOL, + _("Couldn't communicate with the secret storage")); + } + + g_simple_async_result_complete (res); + g_variant_unref (response); + + } else { + g_simple_async_result_take_error (res, error); + g_simple_async_result_complete (res); + } + + g_object_unref (res); +} + +#ifdef WITH_GCRYPT + +static void +on_service_open_session_aes (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + OpenSessionClosure * closure = g_simple_async_result_get_op_res_gpointer (res); + GSecretService *service = GSECRET_SERVICE (source); + GError *error = NULL; + GVariant *response; + + response = g_dbus_proxy_call_finish (G_DBUS_PROXY (service), result, &error); + + /* A successful response, decode it */ + if (response != NULL) { + if (response_open_session_aes (closure->session, response)) { + _gsecret_service_take_session (service, closure->session); + closure->session = NULL; + + } else { + g_simple_async_result_set_error (res, GSECRET_ERROR, GSECRET_ERROR_PROTOCOL, + _("Couldn't communicate with the secret storage")); + } + + g_simple_async_result_complete (res); + g_variant_unref (response); + + } else { + /* AES session not supported, request a plain session */ + if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED)) { + g_dbus_proxy_call (G_DBUS_PROXY (source), "OpenSession", + request_open_session_plain (closure->session), + G_DBUS_CALL_FLAGS_NONE, -1, + closure->cancellable, on_service_open_session_plain, + g_object_ref (res)); + g_error_free (error); + + /* Other errors result in a failure */ + } else { + g_simple_async_result_take_error (res, error); + g_simple_async_result_complete (res); + } + } + + g_object_unref (res); +} + +#endif /* WITH_GCRYPT */ + + +void +_gsecret_session_open (GSecretService *service, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + OpenSessionClosure *closure; + + res = g_simple_async_result_new (G_OBJECT (service), callback, user_data, + _gsecret_session_open); + closure = g_new (OpenSessionClosure, 1); + closure->cancellable = cancellable ? g_object_ref (cancellable) : cancellable; + closure->session = g_new0 (GSecretSession, 1); + g_simple_async_result_set_op_res_gpointer (res, closure, open_session_closure_free); + + g_dbus_proxy_call (G_DBUS_PROXY (service), "OpenSession", +#ifdef WITH_GCRYPT + request_open_session_aes (closure->session), + G_DBUS_CALL_FLAGS_NONE, -1, + cancellable, on_service_open_session_aes, +#else + request_open_session_plain (closure->session), + G_DBUS_CALL_FLAGS_NONE, -1, + cancellable, on_service_open_session_plain, +#endif + g_object_ref (res)); + + g_object_unref (res); +} + +GSecretSession * +_gsecret_session_open_finish (GAsyncResult *result, + GError **error) +{ + OpenSessionClosure *closure; + GSecretSession *session; + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) + return NULL; + + closure = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)); + session = closure->session; + closure->session = NULL; + return session; +} + +#ifdef WITH_GCRYPT + +static gboolean +pkcs7_unpad_bytes_in_place (guchar *padded, + gsize *n_padded) +{ + gsize n_pad, i; + + if (*n_padded == 0) + return FALSE; + + n_pad = padded[*n_padded - 1]; + + /* Validate the padding */ + if (n_pad == 0 || n_pad > 16) + return FALSE; + if (n_pad > *n_padded) + return FALSE; + for (i = *n_padded - n_pad; i < *n_padded; ++i) { + if (padded[i] != n_pad) + return FALSE; + } + + /* The last bit of data */ + *n_padded -= n_pad; + + /* Null teriminate as a courtesy */ + padded[*n_padded] = 0; + + return TRUE; +} + +static GSecretValue * +service_decode_aes_secret (GSecretSession *session, + gconstpointer param, + gsize n_param, + gconstpointer value, + gsize n_value, + const gchar *content_type) +{ + gcry_cipher_hd_t cih; + gsize n_padded; + gcry_error_t gcry; + guchar *padded; + gsize pos; + + if (n_param != 16) { + g_message ("received an encrypted secret structure with invalid parameter"); + return NULL; + } + + if (n_value == 0 || n_value % 16 != 0) { + g_message ("received an encrypted secret structure with bad secret length"); + return NULL; + } + + gcry = gcry_cipher_open (&cih, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0); + if (gcry != 0) { + g_warning ("couldn't create AES cipher: %s", gcry_strerror (gcry)); + return NULL; + } + +#if 0 + g_printerr (" lib iv: %s\n", egg_hex_encode (param, n_param)); +#endif + + gcry = gcry_cipher_setiv (cih, param, n_param); + g_return_val_if_fail (gcry == 0, NULL); + +#if 0 + g_printerr (" lib key: %s\n", egg_hex_encode (session->key, session->n_key)); +#endif + + gcry = gcry_cipher_setkey (cih, session->key, session->n_key); + g_return_val_if_fail (gcry == 0, NULL); + + /* Copy the memory buffer */ + n_padded = n_value; + padded = egg_secure_alloc (n_padded); + memcpy (padded, value, n_padded); + + /* Perform the decryption */ + for (pos = 0; pos < n_padded; pos += 16) { + gcry = gcry_cipher_decrypt (cih, (guchar*)padded + pos, 16, NULL, 0); + g_return_val_if_fail (gcry == 0, FALSE); + } + + gcry_cipher_close (cih); + + /* Unpad the resulting value */ + if (!pkcs7_unpad_bytes_in_place (padded, &n_padded)) { + egg_secure_clear (padded, n_padded); + egg_secure_free (padded); + g_message ("received an invalid or unencryptable secret"); + return FALSE; + } + + return gsecret_value_new_full ((gchar *)padded, n_padded, content_type, egg_secure_free); +} + +#endif /* WITH_GCRYPT */ + +static GSecretValue * +service_decode_plain_secret (GSecretSession *session, + gconstpointer param, + gsize n_param, + gconstpointer value, + gsize n_value, + const gchar *content_type) +{ + if (n_param != 0) { + g_message ("received a plain secret structure with invalid parameter"); + return NULL; + } + + return gsecret_value_new (value, n_value, content_type); +} + +GSecretValue * +_gsecret_session_decode_secret (GSecretSession *session, + GVariant *encoded) +{ + GSecretValue *result; + gconstpointer param; + gconstpointer value; + gchar *session_path; + gchar *content_type; + gsize n_param; + gsize n_value; + GVariant *vparam; + GVariant *vvalue; + + g_return_val_if_fail (session != NULL, NULL); + g_return_val_if_fail (encoded != NULL, NULL); + + /* Parsing (oayays) */ + g_variant_get_child (encoded, 0, "o", &session_path); + + if (session_path == NULL || !g_str_equal (session_path, session->path)) { + g_message ("received a secret encoded with wrong session: %s != %s", + session_path, session->path); + g_free (session_path); + return NULL; + } + + vparam = g_variant_get_child_value (encoded, 1); + param = g_variant_get_fixed_array (vparam, &n_param, sizeof (guchar)); + vvalue = g_variant_get_child_value (encoded, 2); + value = g_variant_get_fixed_array (vvalue, &n_value, sizeof (guchar)); + g_variant_get_child (encoded, 3, "s", &content_type); + +#ifdef WITH_GCRYPT + if (session->key != NULL) + result = service_decode_aes_secret (session, param, n_param, + value, n_value, content_type); + else +#endif + result = service_decode_plain_secret (session, param, n_param, + value, n_value, content_type); + + g_variant_unref (vparam); + g_variant_unref (vvalue); + g_free (content_type); + g_free (session_path); + + return result; +} + +#ifdef WITH_GCRYPT + +static guchar* +pkcs7_pad_bytes_in_secure_memory (gconstpointer secret, + gsize length, + gsize *n_padded) +{ + gsize n_pad; + guchar *padded; + + /* Pad the secret */ + *n_padded = ((length + 16) / 16) * 16; + g_assert (length < *n_padded); + g_assert (*n_padded > 0); + n_pad = *n_padded - length; + g_assert (n_pad > 0 && n_pad <= 16); + padded = egg_secure_alloc (*n_padded); + memcpy (padded, secret, length); + memset (padded + length, n_pad, n_pad); + return padded; +} + +static gboolean +service_encode_aes_secret (GSecretSession *session, + GSecretValue *value, + GVariantBuilder *builder) +{ + gcry_cipher_hd_t cih; + guchar *padded; + gsize n_padded, pos; + gcry_error_t gcry; + gpointer iv; + gconstpointer secret; + gsize n_secret; + GVariant *child; + + g_variant_builder_add (builder, "o", session->path); + + /* Create the cipher */ + gcry = gcry_cipher_open (&cih, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0); + if (gcry != 0) { + g_warning ("couldn't create AES cipher: %s", gcry_strerror (gcry)); + return FALSE; + } + + secret = gsecret_value_get (value, &n_secret); + + /* Perform the encoding here */ + padded = pkcs7_pad_bytes_in_secure_memory (secret, n_secret, &n_padded); + g_assert (padded != NULL); + + /* Setup the IV */ + iv = g_malloc0 (16); + gcry_create_nonce (iv, 16); + gcry = gcry_cipher_setiv (cih, iv, 16); + g_return_val_if_fail (gcry == 0, FALSE); + + /* Setup the key */ + gcry = gcry_cipher_setkey (cih, session->key, session->n_key); + g_return_val_if_fail (gcry == 0, FALSE); + + /* Perform the encryption */ + for (pos = 0; pos < n_padded; pos += 16) { + gcry = gcry_cipher_encrypt (cih, (guchar*)padded + pos, 16, NULL, 0); + g_return_val_if_fail (gcry == 0, FALSE); + } + + gcry_cipher_close (cih); + + child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), iv, 16, TRUE, g_free, iv); + g_variant_builder_add_value (builder, child); + g_variant_unref (child); + + child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), padded, n_padded, TRUE, egg_secure_free, padded); + g_variant_builder_add_value (builder, child); + g_variant_unref (child); + + g_variant_builder_add (builder, "s", gsecret_value_get_content_type (value)); + return TRUE; +} + +#endif /* WITH_GCRYPT */ + +static gboolean +service_encode_plain_secret (GSecretSession *session, + GSecretValue *value, + GVariantBuilder *builder) +{ + gconstpointer secret; + gsize n_secret; + GVariant *child; + + g_variant_builder_add (builder, "o", session->path); + + secret = gsecret_value_get (value, &n_secret); + + child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), "", 0, TRUE, NULL, NULL); + g_variant_builder_add_value (builder, child); + g_variant_unref (child); + + child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), secret, n_secret, TRUE, + gsecret_value_unref, gsecret_value_ref (value)); + g_variant_builder_add_value (builder, child); + g_variant_unref (child); + + g_variant_builder_add (builder, "s", gsecret_value_get_content_type (value)); + return TRUE; +} + +GVariant * +_gsecret_session_encode_secret (GSecretSession *session, + GSecretValue *value) +{ + GVariantBuilder *builder; + GVariant *result = NULL; + GVariantType *type; + gboolean ret; + + g_return_val_if_fail (session != NULL, NULL); + g_return_val_if_fail (value != NULL, NULL); + + type = g_variant_type_new ("(oayays)"); + builder = g_variant_builder_new (type); + +#ifdef WITH_GCRYPT + if (session->key) + ret = service_encode_aes_secret (session, value, builder); + else +#endif + ret = service_encode_plain_secret (session, value, builder); + if (ret) + result = g_variant_builder_end (builder); + + g_variant_builder_unref (builder); + g_variant_type_free (type); + return result; +} + +const gchar * +_gsecret_session_get_algorithms (GSecretSession *session) +{ + g_return_val_if_fail (session != NULL, NULL); + return session->algorithms; +} + +const gchar * +_gsecret_session_get_path (GSecretSession *session) +{ + g_return_val_if_fail (session != NULL, NULL); + return session->path; +} diff --git a/library/gsecret-util.c b/library/gsecret-util.c index 44ffc35..b558b51 100644 --- a/library/gsecret-util.c +++ b/library/gsecret-util.c @@ -118,7 +118,7 @@ _gsecret_util_attributes_for_variant (GVariant *variant) attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); g_variant_iter_init (&iter, variant); - while (g_variant_iter_next (&iter, "{sv}", &key, &value)) + while (g_variant_iter_next (&iter, "{ss}", &key, &value)) g_hash_table_insert (attributes, key, value); return attributes; @@ -288,6 +288,20 @@ _gsecret_util_get_properties_finish (GDBusProxy *proxy, return TRUE; } +typedef struct { + gchar *property; + GVariant *value; + gboolean result; +} SetClosure; + +static void +set_closure_free (gpointer data) +{ + SetClosure *closure = data; + g_free (closure->property); + g_variant_unref (closure->value); + g_slice_free (SetClosure, closure); +} static void on_set_property (GObject *source, @@ -295,6 +309,8 @@ on_set_property (GObject *source, gpointer user_data) { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + SetClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + GDBusProxy *proxy = G_DBUS_PROXY (g_async_result_get_source_object (user_data)); GError *error = NULL; GVariant *retval; @@ -304,7 +320,13 @@ on_set_property (GObject *source, g_simple_async_result_take_error (res, error); if (retval != NULL) g_variant_unref (retval); - g_simple_async_result_set_op_res_gboolean (res, retval != NULL); + + closure->result = retval != NULL; + if (closure->result) + g_dbus_proxy_set_cached_property (proxy, closure->property, closure->value); + + g_simple_async_result_complete (res); + g_object_unref (proxy); g_object_unref (res); } @@ -318,10 +340,15 @@ _gsecret_util_set_property (GDBusProxy *proxy, gpointer user_data) { GSimpleAsyncResult *res; + SetClosure *closure; g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); res = g_simple_async_result_new (G_OBJECT (proxy), callback, user_data, result_tag); + closure = g_slice_new0 (SetClosure); + closure->property = g_strdup (property); + closure->value = g_variant_ref_sink (value); + g_simple_async_result_set_op_res_gpointer (res, closure, set_closure_free); g_dbus_connection_call (g_dbus_proxy_get_connection (proxy), g_dbus_proxy_get_name (proxy), @@ -331,7 +358,7 @@ _gsecret_util_set_property (GDBusProxy *proxy, g_variant_new ("(ssv)", g_dbus_proxy_get_interface_name (proxy), property, - value), + closure->value), G_VARIANT_TYPE ("()"), G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, cancellable, on_set_property, @@ -347,6 +374,7 @@ _gsecret_util_set_property_finish (GDBusProxy *proxy, GError **error) { GSimpleAsyncResult *res; + SetClosure *closure; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (proxy), result_tag), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); @@ -356,7 +384,8 @@ _gsecret_util_set_property_finish (GDBusProxy *proxy, if (g_simple_async_result_propagate_error (res, error)) return FALSE; - return g_simple_async_result_get_op_res_gboolean (res); + closure = g_simple_async_result_get_op_res_gpointer (res); + return closure->result; } gboolean @@ -366,11 +395,14 @@ _gsecret_util_set_property_sync (GDBusProxy *proxy, GCancellable *cancellable, GError **error) { + gboolean result = FALSE; GVariant *retval; g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_variant_ref_sink (value); + retval = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (proxy), g_dbus_proxy_get_name (proxy), g_dbus_proxy_get_object_path (proxy), @@ -384,10 +416,26 @@ _gsecret_util_set_property_sync (GDBusProxy *proxy, G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, cancellable, error); - if (retval != NULL) + if (retval != NULL) { + result = TRUE; g_variant_unref (retval); + g_dbus_proxy_set_cached_property (proxy, property, value); + } - return (retval != NULL); + g_variant_unref (value); + + return result; +} + +gboolean +_gsecret_util_have_cached_properties (GDBusProxy *proxy) +{ + gchar **names; + + names = g_dbus_proxy_get_cached_property_names (proxy); + g_strfreev (names); + + return names != NULL; } GSecretSync * diff --git a/library/gsecret-value.h b/library/gsecret-value.h index 556827e..96bad3f 100644 --- a/library/gsecret-value.h +++ b/library/gsecret-value.h @@ -19,7 +19,7 @@ G_BEGIN_DECLS -#define GSECRET_TYPE_VALUE (gsecret_service_get_type ()) +#define GSECRET_TYPE_VALUE (gsecret_value_get_type ()) GType gsecret_value_get_type (void) G_GNUC_CONST; diff --git a/library/tests/Makefile.am b/library/tests/Makefile.am index 9d2146c..540f9e0 100644 --- a/library/tests/Makefile.am +++ b/library/tests/Makefile.am @@ -26,10 +26,13 @@ LDADD = \ $(NULL) TEST_PROGS = \ + test-value \ test-prompt \ test-service \ test-session \ test-password \ + test-item \ + test-collection \ $(NULL) check_PROGRAMS = \ diff --git a/library/tests/mock/service.py b/library/tests/mock/service.py index 35892dd..eac037a 100644 --- a/library/tests/mock/service.py +++ b/library/tests/mock/service.py @@ -149,8 +149,9 @@ class SecretSession(dbus.service.Object): (params, data) = self.algorithm.encrypt(self.key, secret) # print " mock iv: ", hex_encode(params) # print " mock ciph: ", hex_encode(data) - return dbus.Struct((self.path, dbus.ByteArray(params), dbus.ByteArray(data), - dbus.String(content_type)), signature="oayays") + return dbus.Struct((dbus.ObjectPath(self.path), dbus.ByteArray(params), + dbus.ByteArray(data), dbus.String(content_type)), + signature="oayays") @dbus.service.method('org.freedesktop.Secret.Session') def Close(self): @@ -167,9 +168,9 @@ class SecretItem(dbus.service.Object): self.secret = secret self.attributes = attributes self.content_type = content_type - self.locked = collection.locked self.path = "%s/%s" % (collection.path, identifier) self.confirm = confirm + self.created = self.modified = time.time() dbus.service.Object.__init__(self, collection.service.bus_name, self.path) collection.items[identifier] = self objects[self.path] = self @@ -183,13 +184,14 @@ class SecretItem(dbus.service.Object): def perform_delete(self): del self.collection.items[self.identifier] del objects[self.path] + self.remove_from_connection() @dbus.service.method('org.freedesktop.Secret.Item', sender_keyword='sender') def GetSecret(self, session_path, sender=None): session = objects.get(session_path, None) if not session or session.sender != sender: raise InvalidArgs("session invalid: %s" % session_path) - if self.locked: + if self.collection.locked: raise IsLocked("secret is locked: %s" % self.path) return session.encode_secret(self.secret, self.content_type) @@ -203,14 +205,49 @@ class SecretItem(dbus.service.Object): self.perform_delete() return dbus.ObjectPath("/") + @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v') + def Get(self, interface_name, property_name): + return self.GetAll(interface_name)[property_name] + + @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='s', out_signature='a{sv}') + def GetAll(self, interface_name): + if interface_name == 'org.freedesktop.Secret.Item': + return { + 'Locked': self.collection.locked, + 'Attributes': dbus.Dictionary(self.attributes, signature='ss'), + 'Label': self.label, + 'Created': dbus.UInt64(self.created), + 'Modified': dbus.UInt64(self.modified) + } + else: + raise InvalidArgs('Unknown %s interface' % interface_name) + + @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ssv') + def Set(self, interface_name, property_name, new_value): + if interface_name != 'org.freedesktop.Secret.Item': + raise InvalidArgs('Unknown %s interface' % interface_name) + if property_name == "Label": + self.label = str(new_value) + elif property_name == "Attributes": + self.attributes = dict(new_value) + else: + raise InvalidArgs('Unknown %s interface' % property_name) + self.PropertiesChanged(interface_name, { property_name: new_value }, []) + + @dbus.service.signal(dbus.PROPERTIES_IFACE, signature='sa{sv}as') + def PropertiesChanged(self, interface_name, changed_properties, invalidated_properties): + self.modified = time.time() + class SecretCollection(dbus.service.Object): - def __init__(self, service, identifier, label="Collection", locked=False): + def __init__(self, service, identifier, label="Collection", locked=False, confirm=False): self.service = service self.identifier = identifier self.label = label self.locked = locked self.items = { } + self.confirm = confirm + self.created = self.modified = time.time() self.path = "%s%s" % (COLLECTION_PREFIX, identifier) dbus.service.Object.__init__(self, service.bus_name, self.path) service.collections[identifier] = self @@ -223,6 +260,54 @@ class SecretCollection(dbus.service.Object): results.append(item) return results + def perform_delete(self): + for item in self.items.values(): + item.perform_delete() + del self.service.collections[self.identifier] + del objects[self.path] + self.remove_from_connection() + + @dbus.service.method('org.freedesktop.Secret.Collection', sender_keyword='sender') + def Delete(self, sender=None): + if self.confirm: + prompt = SecretPrompt(self.collection.service, sender, + dismiss=False, action=self.perform_delete) + return dbus.ObjectPath(prompt.path) + else: + self.perform_delete() + return dbus.ObjectPath("/") + + @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v') + def Get(self, interface_name, property_name): + return self.GetAll(interface_name)[property_name] + + @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='s', out_signature='a{sv}') + def GetAll(self, interface_name): + if interface_name == 'org.freedesktop.Secret.Collection': + return { + 'Locked': self.locked, + 'Label': self.label, + 'Created': dbus.UInt64(self.created), + 'Modified': dbus.UInt64(self.modified), + 'Items': [dbus.ObjectPath(i.path) for i in self.items.values()] + } + else: + raise InvalidArgs('Unknown %s interface' % interface_name) + + @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ssv') + def Set(self, interface_name, property_name, new_value): + if interface_name != 'org.freedesktop.Secret.Collection': + raise InvalidArgs('Unknown %s interface' % interface_name) + if property_name == "Label": + self.label = str(new_value) + else: + raise InvalidArgs('Unknown %s interface' % property_name) + self.PropertiesChanged(interface_name, { property_name: new_value }, []) + + @dbus.service.signal(dbus.PROPERTIES_IFACE, signature='sa{sv}as') + def PropertiesChanged(self, interface_name, changed_properties, invalidated_properties): + self.modified = time.time() + class SecretService(dbus.service.Object): @@ -251,8 +336,8 @@ class SecretService(dbus.service.Object): 'org.freedesktop.DBus') def add_standard_objects(self): - collection = SecretCollection(self, "collection", locked=False) - SecretItem(collection, "item_one", attributes={ "number": "1", "string": "one", "parity": "odd" }, secret="uno") + collection = SecretCollection(self, "collection", label="Collection One", locked=False) + SecretItem(collection, "item_one", label="Item One", attributes={ "number": "1", "string": "one", "parity": "odd" }, secret="uno") SecretItem(collection, "item_two", attributes={ "number": "2", "string": "two", "parity": "even" }, secret="dos") SecretItem(collection, "item_three", attributes={ "number": "3", "string": "three", "parity": "odd" }, secret="tres") @@ -323,7 +408,7 @@ class SecretService(dbus.service.Object): results = dbus.Dictionary(signature="o(oayays)") for item_path in item_paths: item = objects.get(item_path, None) - if item and not item.locked: + if item and not item.collection.locked: results[item_path] = item.GetSecret(session_path, sender) return results diff --git a/library/tests/test-collection.c b/library/tests/test-collection.c new file mode 100644 index 0000000..7b0f3c3 --- /dev/null +++ b/library/tests/test-collection.c @@ -0,0 +1,339 @@ +/* GSecret - GLib wrapper for Secret Service + * + * Copyright 2012 Red Hat Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: Stef Walter + */ + + +#include "config.h" + +#include "gsecret-collection.h" +#include "gsecret-service.h" +#include "gsecret-private.h" + +#include "mock-service.h" + +#include "egg/egg-testing.h" + +#include + +#include +#include + +typedef struct { + GDBusConnection *connection; + GSecretService *service; +} Test; + +static void +setup (Test *test, + gconstpointer data) +{ + GError *error = NULL; + const gchar *mock_script = data; + + mock_service_start (mock_script, &error); + g_assert_no_error (error); + + test->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + + test->service = _gsecret_service_bare_instance (test->connection, NULL); +} + +static void +teardown (Test *test, + gconstpointer unused) +{ + GError *error = NULL; + + g_object_unref (test->service); + egg_assert_not_object (test->service); + + mock_service_stop (); + + g_dbus_connection_flush_sync (test->connection, NULL, &error); + g_assert_no_error (error); + g_object_unref (test->connection); +} + +static void +on_async_result (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GAsyncResult **ret = user_data; + g_assert (ret != NULL); + g_assert (*ret == NULL); + *ret = g_object_ref (result); + egg_test_wait_stop (); +} + +static void +on_notify_stop (GObject *obj, + GParamSpec *spec, + gpointer user_data) +{ + guint *sigs = user_data; + g_assert (sigs != NULL); + g_assert (*sigs > 0); + if (--(*sigs) == 0) + egg_test_wait_stop (); +} + +static void +test_new_sync (Test *test, + gconstpointer unused) +{ + const gchar *collection_path = "/org/freedesktop/secrets/collection/collection"; + GError *error = NULL; + GSecretCollection *collection; + + collection = gsecret_collection_new_sync (test->service, collection_path, NULL, &error); + g_assert_no_error (error); + + g_assert_cmpstr (g_dbus_proxy_get_object_path (G_DBUS_PROXY (collection)), ==, collection_path); + + g_object_unref (collection); + egg_assert_not_object (collection); +} + +static void +test_new_async (Test *test, + gconstpointer unused) +{ + const gchar *collection_path = "/org/freedesktop/secrets/collection/collection"; + GError *error = NULL; + GSecretCollection *collection; + GAsyncResult *result = NULL; + + gsecret_collection_new (test->service, collection_path, NULL, on_async_result, &result); + g_assert (result == NULL); + + egg_test_wait (); + + collection = gsecret_collection_new_finish (result, &error); + g_assert_no_error (error); + g_object_unref (result); + + g_assert_cmpstr (g_dbus_proxy_get_object_path (G_DBUS_PROXY (collection)), ==, collection_path); + + g_object_unref (collection); + egg_assert_not_object (collection); +} + +static void +test_properties (Test *test, + gconstpointer unused) +{ + const gchar *collection_path = "/org/freedesktop/secrets/collection/collection"; + GError *error = NULL; + GSecretCollection *collection; + guint64 created; + guint64 modified; + gboolean locked; + gchar *label; + + collection = gsecret_collection_new_sync (test->service, collection_path, NULL, &error); + g_assert_no_error (error); + + g_assert (gsecret_collection_get_locked (collection) == FALSE); + g_assert_cmpuint (gsecret_collection_get_created (collection), <=, time (NULL)); + g_assert_cmpuint (gsecret_collection_get_modified (collection), <=, time (NULL)); + + label = gsecret_collection_get_label (collection); + g_assert_cmpstr (label, ==, "Collection One"); + g_free (label); + + g_object_get (collection, + "locked", &locked, + "created", &created, + "modified", &modified, + "label", &label, + NULL); + + g_assert (locked == FALSE); + g_assert_cmpuint (created, <=, time (NULL)); + g_assert_cmpuint (modified, <=, time (NULL)); + + g_assert_cmpstr (label, ==, "Collection One"); + g_free (label); + + g_object_unref (collection); +} + +static void +test_set_label_sync (Test *test, + gconstpointer unused) +{ + const gchar *collection_path = "/org/freedesktop/secrets/collection/collection"; + GError *error = NULL; + GSecretCollection *collection; + gboolean ret; + gchar *label; + + collection = gsecret_collection_new_sync (test->service, collection_path, NULL, &error); + g_assert_no_error (error); + + label = gsecret_collection_get_label (collection); + g_assert_cmpstr (label, ==, "Collection One"); + g_free (label); + + ret = gsecret_collection_set_label_sync (collection, "Another label", NULL, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + + label = gsecret_collection_get_label (collection); + g_assert_cmpstr (label, ==, "Another label"); + g_free (label); + + g_object_unref (collection); +} + +static void +test_set_label_async (Test *test, + gconstpointer unused) +{ + const gchar *collection_path = "/org/freedesktop/secrets/collection/collection"; + GAsyncResult *result = NULL; + GError *error = NULL; + GSecretCollection *collection; + gboolean ret; + gchar *label; + + collection = gsecret_collection_new_sync (test->service, collection_path, NULL, &error); + g_assert_no_error (error); + + label = gsecret_collection_get_label (collection); + g_assert_cmpstr (label, ==, "Collection One"); + g_free (label); + + gsecret_collection_set_label (collection, "Another label", NULL, on_async_result, &result); + g_assert (result == NULL); + + egg_test_wait (); + + ret = gsecret_collection_set_label_finish (collection, result, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + g_object_unref (result); + + label = gsecret_collection_get_label (collection); + g_assert_cmpstr (label, ==, "Another label"); + g_free (label); + + g_object_unref (collection); +} + +static void +test_set_label_prop (Test *test, + gconstpointer unused) +{ + const gchar *collection_path = "/org/freedesktop/secrets/collection/collection"; + GError *error = NULL; + GSecretCollection *collection; + guint sigs = 2; + gchar *label; + + collection = gsecret_collection_new_sync (test->service, collection_path, NULL, &error); + g_assert_no_error (error); + + label = gsecret_collection_get_label (collection); + g_assert_cmpstr (label, ==, "Collection One"); + g_free (label); + + g_signal_connect (collection, "notify::label", G_CALLBACK (on_notify_stop), &sigs); + g_object_set (collection, "label", "Blah blah", NULL); + + /* Wait for the property to actually 'take' */ + egg_test_wait (); + + label = gsecret_collection_get_label (collection); + g_assert_cmpstr (label, ==, "Blah blah"); + g_free (label); + + g_object_unref (collection); +} + +#if 0 +static void +test_delete_sync (Test *test, + gconstpointer unused) +{ + const gchar *item_path = "/org/freedesktop/secrets/collection/collection/item_one"; + GError *error = NULL; + GSecretItem *item; + gboolean ret; + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_no_error (error); + + ret = gsecret_item_delete_sync (item, NULL, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + + g_object_unref (item); + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD); + g_assert (item == NULL); +} + +static void +test_delete_async (Test *test, + gconstpointer unused) +{ + const gchar *item_path = "/org/freedesktop/secrets/collection/collection/item_one"; + GAsyncResult *result = NULL; + GError *error = NULL; + GSecretItem *item; + gboolean ret; + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_no_error (error); + + gsecret_item_delete (item, NULL, on_async_result, &result); + g_assert (result == NULL); + + egg_test_wait (); + + ret = gsecret_item_delete_finish (item, result, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + + g_object_unref (item); + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD); + g_assert (item == NULL); +} +#endif + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_set_prgname ("test-collection"); + g_type_init (); + + g_test_add ("/collection/new-sync", Test, "mock-service-normal.py", setup, test_new_sync, teardown); + g_test_add ("/collection/new-async", Test, "mock-service-normal.py", setup, test_new_async, teardown); + g_test_add ("/collection/properties", Test, "mock-service-normal.py", setup, test_properties, teardown); + g_test_add ("/collection/set-label-sync", Test, "mock-service-normal.py", setup, test_set_label_sync, teardown); + g_test_add ("/collection/set-label-async", Test, "mock-service-normal.py", setup, test_set_label_async, teardown); + g_test_add ("/collection/set-label-prop", Test, "mock-service-normal.py", setup, test_set_label_prop, teardown); +#if 0 + g_test_add ("/item/delete-sync", Test, "mock-service-normal.py", setup, test_delete_sync, teardown); + g_test_add ("/item/delete-async", Test, "mock-service-normal.py", setup, test_delete_async, teardown); +#endif + + return egg_tests_run_with_loop (); +} diff --git a/library/tests/test-item.c b/library/tests/test-item.c new file mode 100644 index 0000000..9bf239d --- /dev/null +++ b/library/tests/test-item.c @@ -0,0 +1,534 @@ +/* GSecret - GLib wrapper for Secret Service + * + * Copyright 2012 Red Hat Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: Stef Walter + */ + + +#include "config.h" + +#include "gsecret-item.h" +#include "gsecret-service.h" +#include "gsecret-private.h" + +#include "mock-service.h" + +#include "egg/egg-testing.h" + +#include + +#include +#include + +typedef struct { + GDBusConnection *connection; + GSecretService *service; +} Test; + +static void +setup (Test *test, + gconstpointer data) +{ + GError *error = NULL; + const gchar *mock_script = data; + + mock_service_start (mock_script, &error); + g_assert_no_error (error); + + test->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + + test->service = _gsecret_service_bare_instance (test->connection, NULL); +} + +static void +teardown (Test *test, + gconstpointer unused) +{ + GError *error = NULL; + + g_object_unref (test->service); + egg_assert_not_object (test->service); + + mock_service_stop (); + + g_dbus_connection_flush_sync (test->connection, NULL, &error); + g_assert_no_error (error); + g_object_unref (test->connection); +} + +static void +on_async_result (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GAsyncResult **ret = user_data; + g_assert (ret != NULL); + g_assert (*ret == NULL); + *ret = g_object_ref (result); + egg_test_wait_stop (); +} + +static void +on_notify_stop (GObject *obj, + GParamSpec *spec, + gpointer user_data) +{ + guint *sigs = user_data; + g_assert (sigs != NULL); + g_assert (*sigs > 0); + if (--(*sigs) == 0) + egg_test_wait_stop (); +g_printerr ("sigs: %u\n", *sigs); +} + +static void +test_new_sync (Test *test, + gconstpointer unused) +{ + const gchar *item_path = "/org/freedesktop/secrets/collection/collection/item_one"; + GError *error = NULL; + GSecretItem *item; + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_no_error (error); + + g_assert_cmpstr (g_dbus_proxy_get_object_path (G_DBUS_PROXY (item)), ==, item_path); + + g_object_unref (item); +} + +static void +test_new_async (Test *test, + gconstpointer unused) +{ + const gchar *item_path = "/org/freedesktop/secrets/collection/collection/item_one"; + GAsyncResult *result = NULL; + GError *error = NULL; + GSecretItem *item; + + gsecret_item_new (test->service, item_path, NULL, on_async_result, &result); + g_assert (result == NULL); + + egg_test_wait (); + + item = gsecret_item_new_finish (result, &error); + g_assert_no_error (error); + g_object_unref (result); + + g_assert_cmpstr (g_dbus_proxy_get_object_path (G_DBUS_PROXY (item)), ==, item_path); + + g_object_unref (item); +} + +static void +test_properties (Test *test, + gconstpointer unused) +{ + const gchar *item_path = "/org/freedesktop/secrets/collection/collection/item_one"; + GError *error = NULL; + GHashTable *attributes; + GSecretItem *item; + guint64 created; + guint64 modified; + gboolean locked; + gchar *label; + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_no_error (error); + + g_assert (gsecret_item_get_locked (item) == FALSE); + g_assert_cmpuint (gsecret_item_get_created (item), <=, time (NULL)); + g_assert_cmpuint (gsecret_item_get_modified (item), <=, time (NULL)); + + label = gsecret_item_get_label (item); + g_assert_cmpstr (label, ==, "Item One"); + g_free (label); + + attributes = gsecret_item_get_attributes (item); + g_assert_cmpstr (g_hash_table_lookup (attributes, "string"), ==, "one"); + g_assert_cmpstr (g_hash_table_lookup (attributes, "number"), ==, "1"); + g_assert_cmpstr (g_hash_table_lookup (attributes, "parity"), ==, "odd"); + g_assert_cmpuint (g_hash_table_size (attributes), ==, 3); + g_hash_table_unref (attributes); + + g_object_get (item, + "locked", &locked, + "created", &created, + "modified", &modified, + "label", &label, + "attributes", &attributes, + NULL); + + g_assert (locked == FALSE); + g_assert_cmpuint (created, <=, time (NULL)); + g_assert_cmpuint (modified, <=, time (NULL)); + + g_assert_cmpstr (label, ==, "Item One"); + g_free (label); + + g_assert_cmpstr (g_hash_table_lookup (attributes, "string"), ==, "one"); + g_assert_cmpstr (g_hash_table_lookup (attributes, "number"), ==, "1"); + g_assert_cmpstr (g_hash_table_lookup (attributes, "parity"), ==, "odd"); + g_assert_cmpuint (g_hash_table_size (attributes), ==, 3); + g_hash_table_unref (attributes); + + g_object_unref (item); +} + +static void +test_set_label_sync (Test *test, + gconstpointer unused) +{ + const gchar *item_path = "/org/freedesktop/secrets/collection/collection/item_one"; + GError *error = NULL; + GSecretItem *item; + gboolean ret; + gchar *label; + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_no_error (error); + + label = gsecret_item_get_label (item); + g_assert_cmpstr (label, ==, "Item One"); + g_free (label); + + ret = gsecret_item_set_label_sync (item, "Another label", NULL, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + + label = gsecret_item_get_label (item); + g_assert_cmpstr (label, ==, "Another label"); + g_free (label); + + g_object_unref (item); +} + +static void +test_set_label_async (Test *test, + gconstpointer unused) +{ + const gchar *item_path = "/org/freedesktop/secrets/collection/collection/item_one"; + GAsyncResult *result = NULL; + GError *error = NULL; + GSecretItem *item; + gboolean ret; + gchar *label; + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_no_error (error); + + label = gsecret_item_get_label (item); + g_assert_cmpstr (label, ==, "Item One"); + g_free (label); + + gsecret_item_set_label (item, "Another label", NULL, on_async_result, &result); + g_assert (result == NULL); + + egg_test_wait (); + + ret = gsecret_item_set_label_finish (item, result, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + g_object_unref (result); + + label = gsecret_item_get_label (item); + g_assert_cmpstr (label, ==, "Another label"); + g_free (label); + + g_object_unref (item); +} + +static void +test_set_label_prop (Test *test, + gconstpointer unused) +{ + const gchar *item_path = "/org/freedesktop/secrets/collection/collection/item_one"; + GError *error = NULL; + GSecretItem *item; + guint sigs = 2; + gchar *label; + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_no_error (error); + + label = gsecret_item_get_label (item); + g_assert_cmpstr (label, ==, "Item One"); + g_free (label); + + g_signal_connect (item, "notify::label", G_CALLBACK (on_notify_stop), &sigs); + g_object_set (item, "label", "Blah blah", NULL); + + /* Wait for the property to actually 'take' */ + egg_test_wait (); + + label = gsecret_item_get_label (item); + g_assert_cmpstr (label, ==, "Blah blah"); + g_free (label); + + g_object_unref (item); +} + +static void +test_set_attributes_sync (Test *test, + gconstpointer unused) +{ + const gchar *item_path = "/org/freedesktop/secrets/collection/collection/item_one"; + GError *error = NULL; + GSecretItem *item; + gboolean ret; + GHashTable *attributes; + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_no_error (error); + + attributes = gsecret_item_get_attributes (item); + g_assert_cmpstr (g_hash_table_lookup (attributes, "string"), ==, "one"); + g_assert_cmpstr (g_hash_table_lookup (attributes, "number"), ==, "1"); + g_assert_cmpstr (g_hash_table_lookup (attributes, "parity"), ==, "odd"); + g_assert_cmpuint (g_hash_table_size (attributes), ==, 3); + g_hash_table_unref (attributes); + + attributes = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (attributes, "string", "five"); + g_hash_table_insert (attributes, "number", "5"); + ret = gsecret_item_set_attributes_sync (item, attributes, NULL, &error); + g_hash_table_unref (attributes); + g_assert_no_error (error); + g_assert (ret == TRUE); + + attributes = gsecret_item_get_attributes (item); + g_assert_cmpstr (g_hash_table_lookup (attributes, "string"), ==, "five"); + g_assert_cmpstr (g_hash_table_lookup (attributes, "number"), ==, "5"); + g_assert_cmpuint (g_hash_table_size (attributes), ==, 2); + g_hash_table_unref (attributes); + + g_object_unref (item); +} + +static void +test_set_attributes_async (Test *test, + gconstpointer unused) +{ + const gchar *item_path = "/org/freedesktop/secrets/collection/collection/item_one"; + GHashTable *attributes; + GError *error = NULL; + GAsyncResult *result = NULL; + GSecretItem *item; + gboolean ret; + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_no_error (error); + + attributes = gsecret_item_get_attributes (item); + g_assert_cmpstr (g_hash_table_lookup (attributes, "string"), ==, "one"); + g_assert_cmpstr (g_hash_table_lookup (attributes, "number"), ==, "1"); + g_assert_cmpstr (g_hash_table_lookup (attributes, "parity"), ==, "odd"); + g_assert_cmpuint (g_hash_table_size (attributes), ==, 3); + g_hash_table_unref (attributes); + + attributes = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (attributes, "string", "five"); + g_hash_table_insert (attributes, "number", "5"); + gsecret_item_set_attributes (item, attributes, NULL, on_async_result, &result); + g_assert (result == NULL); + + egg_test_wait (); + + ret = gsecret_item_set_attributes_finish (item, result, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + g_object_unref (result); + + attributes = gsecret_item_get_attributes (item); + g_assert_cmpstr (g_hash_table_lookup (attributes, "string"), ==, "five"); + g_assert_cmpstr (g_hash_table_lookup (attributes, "number"), ==, "5"); + g_assert_cmpuint (g_hash_table_size (attributes), ==, 2); + g_hash_table_unref (attributes); + + g_object_unref (item); +} + +static void +test_set_attributes_prop (Test *test, + gconstpointer unused) +{ + const gchar *item_path = "/org/freedesktop/secrets/collection/collection/item_one"; + GError *error = NULL; + GSecretItem *item; + GHashTable *attributes; + guint sigs = 2; + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_no_error (error); + + attributes = gsecret_item_get_attributes (item); + g_assert_cmpstr (g_hash_table_lookup (attributes, "string"), ==, "one"); + g_assert_cmpstr (g_hash_table_lookup (attributes, "number"), ==, "1"); + g_assert_cmpstr (g_hash_table_lookup (attributes, "parity"), ==, "odd"); + g_assert_cmpuint (g_hash_table_size (attributes), ==, 3); + g_hash_table_unref (attributes); + + g_signal_connect (item, "notify::attributes", G_CALLBACK (on_notify_stop), &sigs); + + attributes = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (attributes, "string", "five"); + g_hash_table_insert (attributes, "number", "5"); + g_object_set (item, "attributes", attributes, NULL); + g_hash_table_unref (attributes); + + /* Wait for the property to actually 'take' */ + egg_test_wait (); + + attributes = gsecret_item_get_attributes (item); + g_assert_cmpstr (g_hash_table_lookup (attributes, "string"), ==, "five"); + g_assert_cmpstr (g_hash_table_lookup (attributes, "number"), ==, "5"); + g_assert_cmpuint (g_hash_table_size (attributes), ==, 2); + g_hash_table_unref (attributes); + + g_object_unref (item); +} + +static void +test_get_secret_sync (Test *test, + gconstpointer unused) +{ + const gchar *item_path = "/org/freedesktop/secrets/collection/collection/item_one"; + GError *error = NULL; + GSecretItem *item; + GSecretValue *value; + gconstpointer data; + gsize length; + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_no_error (error); + + value = gsecret_item_get_secret_sync (item, NULL, &error); + g_assert_no_error (error); + g_assert (value != NULL); + + data = gsecret_value_get (value, &length); + egg_assert_cmpmem (data, length, ==, "uno", 3); + + gsecret_value_unref (value); + + g_object_unref (item); +} + +static void +test_get_secret_async (Test *test, + gconstpointer unused) +{ + const gchar *item_path = "/org/freedesktop/secrets/collection/collection/item_one"; + GAsyncResult *result = NULL; + GError *error = NULL; + GSecretItem *item; + GSecretValue *value; + gconstpointer data; + gsize length; + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_no_error (error); + + gsecret_item_get_secret (item, NULL, on_async_result, &result); + g_assert (result == NULL); + + egg_test_wait (); + + value = gsecret_item_get_secret_finish (item, result, &error); + g_assert_no_error (error); + g_assert (value != NULL); + g_object_unref (result); + + data = gsecret_value_get (value, &length); + egg_assert_cmpmem (data, length, ==, "uno", 3); + + gsecret_value_unref (value); + + g_object_unref (item); +} + +static void +test_delete_sync (Test *test, + gconstpointer unused) +{ + const gchar *item_path = "/org/freedesktop/secrets/collection/collection/item_one"; + GError *error = NULL; + GSecretItem *item; + gboolean ret; + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_no_error (error); + + ret = gsecret_item_delete_sync (item, NULL, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + + g_object_unref (item); + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD); + g_assert (item == NULL); +} + +static void +test_delete_async (Test *test, + gconstpointer unused) +{ + const gchar *item_path = "/org/freedesktop/secrets/collection/collection/item_one"; + GAsyncResult *result = NULL; + GError *error = NULL; + GSecretItem *item; + gboolean ret; + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_no_error (error); + + gsecret_item_delete (item, NULL, on_async_result, &result); + g_assert (result == NULL); + + egg_test_wait (); + + ret = gsecret_item_delete_finish (item, result, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + + g_object_unref (item); + + item = gsecret_item_new_sync (test->service, item_path, NULL, &error); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD); + g_assert (item == NULL); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_set_prgname ("test-item"); + g_type_init (); + + g_test_add ("/item/new-sync", Test, "mock-service-normal.py", setup, test_new_sync, teardown); + g_test_add ("/item/new-async", Test, "mock-service-normal.py", setup, test_new_async, teardown); + g_test_add ("/item/properties", Test, "mock-service-normal.py", setup, test_properties, teardown); + g_test_add ("/item/set-label-sync", Test, "mock-service-normal.py", setup, test_set_label_sync, teardown); + g_test_add ("/item/set-label-async", Test, "mock-service-normal.py", setup, test_set_label_async, teardown); + g_test_add ("/item/set-attributes-sync", Test, "mock-service-normal.py", setup, test_set_attributes_sync, teardown); + g_test_add ("/item/set-attributes-async", Test, "mock-service-normal.py", setup, test_set_attributes_async, teardown); + g_test_add ("/item/get-secret-sync", Test, "mock-service-normal.py", setup, test_get_secret_sync, teardown); + g_test_add ("/item/get-secret-async", Test, "mock-service-normal.py", setup, test_get_secret_async, teardown); + g_test_add ("/item/delete-sync", Test, "mock-service-normal.py", setup, test_delete_sync, teardown); + g_test_add ("/item/delete-async", Test, "mock-service-normal.py", setup, test_delete_async, teardown); + + g_test_add ("/item/set-attributes-prop", Test, "mock-service-normal.py", setup, test_set_attributes_prop, teardown); + g_test_add ("/item/set-label-prop", Test, "mock-service-normal.py", setup, test_set_label_prop, teardown); + + return egg_tests_run_with_loop (); +} diff --git a/library/tests/test-value.c b/library/tests/test-value.c new file mode 100644 index 0000000..6ef051f --- /dev/null +++ b/library/tests/test-value.c @@ -0,0 +1,210 @@ +/* GSecret - GLib wrapper for Secret Service + * + * Copyright 2012 Red Hat Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + */ + + +#include "config.h" + +#include "gsecret-value.h" +#include "gsecret-private.h" + +#include "egg/egg-testing.h" +#include "egg/egg-secure-memory.h" + +#include + +#include +#include + +EGG_SECURE_DECLARE (test_value); + +static void +test_new (void) +{ + GSecretValue *value; + gsize length; + + value = gsecret_value_new ("blahblah", 4, "text/plain"); + + g_assert_cmpstr (gsecret_value_get (value, &length), ==, "blah"); + g_assert_cmpuint (length, ==, 4); + + g_assert_cmpstr (gsecret_value_get_content_type (value), ==, "text/plain"); + + gsecret_value_unref (value); +} + +static void +test_new_terminated (void) +{ + GSecretValue *value; + gsize length; + + value = gsecret_value_new ("blah", -1, "text/plain"); + + g_assert_cmpstr (gsecret_value_get (value, &length), ==, "blah"); + g_assert_cmpuint (length, ==, 4); + + g_assert_cmpstr (gsecret_value_get_content_type (value), ==, "text/plain"); + + gsecret_value_unref (value); +} + +static void +test_new_full (void) +{ + GSecretValue *value; + gchar *data = g_strdup ("blah"); + gsize length; + + value = gsecret_value_new_full (data, 4, "text/plain", g_free); + + g_assert_cmpstr (gsecret_value_get (value, &length), ==, "blah"); + g_assert_cmpuint (length, ==, 4); + + /* No copy done here */ + g_assert (gsecret_value_get (value, NULL) == data); + + gsecret_value_unref (value); +} + +static void +test_new_full_terminated (void) +{ + GSecretValue *value; + gchar *data = g_strdup ("blah"); + gsize length; + + value = gsecret_value_new_full (data, -1, "text/plain", g_free); + + g_assert_cmpstr (gsecret_value_get (value, &length), ==, "blah"); + g_assert_cmpuint (length, ==, 4); + + /* No copy done here */ + g_assert (gsecret_value_get (value, NULL) == data); + + gsecret_value_unref (value); +} + +static void +test_ref_unref (void) +{ + GSecretValue *value; + GSecretValue *value2; + gsize length; + + value = gsecret_value_new ("blah", 4, "text/plain"); + value2 = gsecret_value_ref(value); + gsecret_value_unref (value); + + g_assert_cmpstr (gsecret_value_get (value2, &length), ==, "blah"); + g_assert_cmpuint (length, ==, 4); + + gsecret_value_unref (value2); +} + +static void +test_boxed (void) +{ + GSecretValue *value; + GSecretValue *value2; + gsize length; + + value = gsecret_value_new ("blah", 4, "text/plain"); + value2 = g_boxed_copy (GSECRET_TYPE_VALUE, value); + g_boxed_free (GSECRET_TYPE_VALUE, value); + + g_assert_cmpstr (gsecret_value_get (value2, &length), ==, "blah"); + g_assert_cmpuint (length, ==, 4); + + g_boxed_free (GSECRET_TYPE_VALUE, value2); +} + +static void +test_to_password (void) +{ + GSecretValue *value; + gchar *password; + + value = gsecret_value_new_full (egg_secure_strdup ("blah"), -1, + "text/plain", egg_secure_free); + + password = _gsecret_value_unref_to_password (value); + g_assert_cmpstr (password, ==, "blah"); + + egg_secure_free (password); +} + +static void +test_to_password_bad_destroy (void) +{ + GSecretValue *value; + gchar *password; + + value = gsecret_value_new_full (g_strdup ("blah"), -1, + "text/plain", g_free); + + password = _gsecret_value_unref_to_password (value); + g_assert_cmpstr (password, ==, "blah"); + + egg_secure_free (password); +} + +static void +test_to_password_bad_content (void) +{ + GSecretValue *value; + gchar *password; + + value = gsecret_value_new_full (g_strdup ("wooowhee"), -1, + "application/octet-stream", g_free); + + password = _gsecret_value_unref_to_password (value); + g_assert_cmpstr (password, ==, NULL); +} + +static void +test_to_password_extra_ref (void) +{ + GSecretValue *value; + gchar *password; + + value = gsecret_value_new_full (egg_secure_strdup ("blah"), -1, + "text/plain", egg_secure_free); + gsecret_value_ref (value); + + password = _gsecret_value_unref_to_password (value); + g_assert_cmpstr (password, ==, "blah"); + + egg_secure_free (password); + gsecret_value_unref (value); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_set_prgname ("test-value"); + g_type_init (); + + g_test_add_func ("/value/new", test_new); + g_test_add_func ("/value/new-terminated", test_new_terminated); + g_test_add_func ("/value/new-full", test_new_full); + g_test_add_func ("/value/new-full-terminated", test_new_full_terminated); + g_test_add_func ("/value/ref-unref", test_ref_unref); + g_test_add_func ("/value/boxed", test_boxed); + g_test_add_func ("/value/to-password", test_to_password); + g_test_add_func ("/value/to-password-bad-destroy", test_to_password_bad_destroy); + g_test_add_func ("/value/to-password-bad-content", test_to_password_bad_content); + g_test_add_func ("/value/to-password-extra-ref", test_to_password_extra_ref); + + return egg_tests_run_with_loop (); +}