diff --git a/.gitignore b/.gitignore index 0aded49..9b4c7c2 100644 --- a/.gitignore +++ b/.gitignore @@ -31,14 +31,13 @@ stamp* /build/coverage* /build/m4 -/build/valgrind-built.supp +/build/valgrind-suppressions /po/POTFILES /po/gsecret.pot -/egg/tests/test-dh -/egg/tests/test-hkdf -/egg/tests/test-secmem +/egg/tests/test-* +!/egg/tests/test-*.c /library/gsecret-dbus-generated.[ch] /library/tests/test-* diff --git a/Makefile.am b/Makefile.am index 008c363..915217b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -28,17 +28,3 @@ check-memory: test -d $(builddir)/$$subdir/tests && \ make -C $(builddir)/$$subdir/tests check-memory; \ done - -if WITH_COVERAGE -coverage: - mkdir -p $(builddir)/build/coverage - $(LCOV) --directory . --capture --output-file $(builddir)/build/coverage.info - $(GENHTML) --output-directory $(builddir)/build/coverage $(builddir)/build/coverage.info - $(LCOV) --directory . --zerocounters - @echo "file://$(abs_top_builddir)/build/coverage/index.html" - -clear-coverage: - $(LCOV) --directory . --zerocounters - -.PHONY: coverage -endif diff --git a/Makefile.decl b/Makefile.decl index 4eaa858..9ea9528 100644 --- a/Makefile.decl +++ b/Makefile.decl @@ -1,11 +1,28 @@ NULL = -perform-memcheck: $(TEST_PROGS) $(top_builddir)/build/valgrind-built.supp +TEST_SUPPRESSIONS = $(top_builddir)/build/valgrind-suppressions + +perform-memcheck: $(TEST_PROGS) $(TEST_SUPPRESSIONS) @for test in $(TEST_PROGS); do \ G_SLICE=always-malloc libtool --mode=execute \ valgrind --trace-children=no --gen-suppressions=all \ - --suppressions=$(top_builddir)/build/valgrind-built.supp \ + --suppressions=$(TEST_SUPPRESSIONS) \ --leak-check=full --show-reachable=yes --num-callers=16 \ --quiet --error-exitcode=33 \ $(builddir)/$$test; \ done + +if WITH_COVERAGE +coverage: + mkdir -p $(top_builddir)/build/coverage + $(LCOV) --directory . --capture --output-file $(top_builddir)/build/coverage.info + $(GENHTML) --output-directory $(top_builddir)/build/coverage $(top_builddir)/build/coverage.info + $(LCOV) --directory . --zerocounters + @echo "file://$(abs_top_builddir)/build/coverage/index.html" + +clear-coverage: + $(LCOV) --directory . --zerocounters + +.PHONY: coverage +endif + \ No newline at end of file diff --git a/build/Makefile.am b/build/Makefile.am index 267e822..52eb08c 100644 --- a/build/Makefile.am +++ b/build/Makefile.am @@ -9,11 +9,11 @@ SUPPRESSIONS = \ pthread.supp \ unknown.supp -valgrind-built.supp: $(SUPPRESSIONS) +valgrind-suppressions: $(SUPPRESSIONS) $(AM_V_GEN) cat $(SUPPRESSIONS) > $@ EXTRA_DIST = \ $(VALGRIND_CONTRIB) \ $(SUPPRESSIONS) -all-local: valgrind-built.supp \ No newline at end of file +all-local: valgrind-suppressions \ No newline at end of file diff --git a/build/glib.supp b/build/glib.supp index 8accd45..2da79f1 100644 --- a/build/glib.supp +++ b/build/glib.supp @@ -238,6 +238,15 @@ ... fun:g_test_run_suite } +{ + g_test_run_suite__timer_new2 + Memcheck:Leak + ... + fun:g_timer_new + fun:test_case_run_suite_internal + ... + fun:g_test_run_suite +} { g_test_run_suite__strconcat Memcheck:Leak @@ -301,3 +310,10 @@ ... fun:g_static_mutex_get_mutex_impl } +{ + g_variant_type_info_unref + Memcheck:Leak + ... + fun:g_hash_table_remove + fun:g_variant_type_info_unref +} diff --git a/build/unknown.supp b/build/unknown.supp index 862d79a..314570c 100644 --- a/build/unknown.supp +++ b/build/unknown.supp @@ -344,3 +344,12 @@ ... fun:gdbus_shared_thread_func } +{ + + Memcheck:Leak + ... + fun:g_main_context_add_poll_unlocked + ... + fun:g_main_loop_run + fun:gdbus_shared_thread_func +} diff --git a/egg/Makefile.am b/egg/Makefile.am index ebc0094..84fc104 100644 --- a/egg/Makefile.am +++ b/egg/Makefile.am @@ -19,7 +19,11 @@ ENCRYPTION_SRCS = endif libegg_la_SOURCES = \ + egg-hex.c egg-hex.h \ egg-secure-memory.c egg-secure-memory.h \ egg-testing.c egg-testing.h \ $(ENCRYPTION_SRCS) \ - $(BUILT_SOURCES) \ No newline at end of file + $(BUILT_SOURCES) + +check-memory: + make -C tests check-memory \ No newline at end of file diff --git a/egg/egg-hex.c b/egg/egg-hex.c new file mode 100644 index 0000000..0e74f57 --- /dev/null +++ b/egg/egg-hex.c @@ -0,0 +1,150 @@ +/* + * gnome-keyring + * + * Copyright (C) 2008 Stefan Walter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General License for more details. + * + * You should have received a copy of the GNU Lesser General + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "config.h" + +#include "egg-hex.h" + +#include + +static const char HEXC_UPPER[] = "0123456789ABCDEF"; +static const char HEXC_LOWER[] = "0123456789abcdef"; + +gpointer +egg_hex_decode (const gchar *data, gssize n_data, gsize *n_decoded) +{ + return egg_hex_decode_full (data, n_data, 0, 1, n_decoded); +} + +gpointer +egg_hex_decode_full (const gchar *data, gssize n_data, + gchar delim, guint group, gsize *n_decoded) +{ + guchar *result; + guchar *decoded; + gushort j; + gint state = 0; + gint part = 0; + const gchar* pos; + + g_return_val_if_fail (data || !n_data, NULL); + g_return_val_if_fail (n_decoded, NULL); + g_return_val_if_fail (group >= 1, NULL); + + if (n_data == -1) + n_data = strlen (data); + + decoded = result = g_malloc0 ((n_data / 2) + 1); + *n_decoded = 0; + + while (n_data > 0 && state == 0) { + + if (decoded != result && delim) { + if (*data != delim) { + state = -1; + break; + } + + ++data; + --n_data; + } + + while (part < group && n_data > 0) { + + /* Find the position */ + pos = strchr (HEXC_UPPER, g_ascii_toupper (*data)); + if (pos == 0) { + if (n_data > 0) + state = -1; + break; + } + + j = pos - HEXC_UPPER; + if(!state) { + *decoded = (j & 0xf) << 4; + state = 1; + } else { + *decoded |= (j & 0xf); + (*n_decoded)++; + decoded++; + state = 0; + part++; + } + + ++data; + --n_data; + } + + part = 0; + } + + /* Parsing error */ + if (state != 0) { + g_free (result); + result = NULL; + } + + return result; +} + +gchar* +egg_hex_encode (gconstpointer data, gsize n_data) +{ + return egg_hex_encode_full (data, n_data, TRUE, '\0', 0); +} + +gchar* +egg_hex_encode_full (gconstpointer data, gsize n_data, + gboolean upper_case, gchar delim, guint group) +{ + GString *result; + const gchar *input; + const char *hexc; + gsize bytes; + guchar j; + + g_return_val_if_fail (data || !n_data, NULL); + + input = data; + hexc = upper_case ? HEXC_UPPER : HEXC_LOWER; + + result = g_string_sized_new (n_data * 2 + 1); + bytes = 0; + + while (n_data > 0) { + + if (group && bytes && (bytes % group) == 0) + g_string_append_c (result, delim); + + j = *(input) >> 4 & 0xf; + g_string_append_c (result, hexc[j]); + + j = *(input++) & 0xf; + g_string_append_c (result, hexc[j]); + + ++bytes; + --n_data; + } + + /* Make sure still null terminated */ + return g_string_free (result, FALSE); +} + diff --git a/egg/egg-hex.h b/egg/egg-hex.h new file mode 100644 index 0000000..d30b641 --- /dev/null +++ b/egg/egg-hex.h @@ -0,0 +1,46 @@ +/* + * gnome-keyring + * + * Copyright (C) 2008 Stefan Walter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General License for more details. + * + * You should have received a copy of the GNU Lesser General + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef EGG_HEX_H_ +#define EGG_HEX_H_ + +#include + +gpointer egg_hex_decode (const gchar *data, + gssize n_data, + gsize *n_decoded); + +gpointer egg_hex_decode_full (const gchar *data, + gssize n_data, + gchar delim, + guint group, + gsize *n_decoded); + +gchar* egg_hex_encode (gconstpointer data, + gsize n_data); + +gchar* egg_hex_encode_full (gconstpointer data, + gsize n_data, + gboolean upper_case, + gchar delim, + guint group); + +#endif /* EGG_HEX_H_ */ diff --git a/egg/egg-testing.c b/egg/egg-testing.c index e60779c..0fed4a1 100644 --- a/egg/egg-testing.c +++ b/egg/egg-testing.c @@ -138,7 +138,6 @@ on_loop_wait_timeout (gpointer data) static gboolean loop_wait_until (int timeout) { - gboolean ret = FALSE; gboolean timed_out = FALSE; guint source; @@ -149,16 +148,10 @@ loop_wait_until (int timeout) g_main_loop_run (wait_loop); - if (timed_out) { - g_source_remove (source); - ret = FALSE; - } else { - ret = TRUE; - } - + g_source_remove (source); g_main_loop_unref (wait_loop); wait_loop = NULL; - return ret; + return !timed_out; } gint diff --git a/egg/tests/Makefile.am b/egg/tests/Makefile.am index 9ad124d..88b9e51 100644 --- a/egg/tests/Makefile.am +++ b/egg/tests/Makefile.am @@ -12,6 +12,7 @@ LDADD = \ $(GLIB_LIBS) TEST_PROGS = \ + test-hex \ test-secmem if WITH_GCRYPT diff --git a/egg/tests/test-hex.c b/egg/tests/test-hex.c new file mode 100644 index 0000000..d2c6ba7 --- /dev/null +++ b/egg/tests/test-hex.c @@ -0,0 +1,121 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* unit-test-util.c: Test gck-util.c + + Copyright (C) 2007 Stefan Walter + + The Gnome Keyring Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Keyring Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter +*/ + +#include "config.h" + +#include "egg/egg-hex.h" + +#include +#include +#include + +static const guchar TEST_DATA[] = { 0x05, 0xD6, 0x95, 0x96, 0x10, 0x12, 0xAE, 0x35 }; +static const gchar *TEST_HEX = "05D695961012AE35"; +static const gchar *TEST_HEX_DELIM = "05 D6 95 96 10 12 AE 35"; + +static void +test_encode (void) +{ + gchar *hex; + + hex = egg_hex_encode (TEST_DATA, sizeof (TEST_DATA)); + g_assert (hex); + g_assert_cmpstr (hex, ==, TEST_HEX); + g_free (hex); +} + +static void +test_encode_spaces (void) +{ + gchar *hex; + + /* Encode without spaces */ + hex = egg_hex_encode_full (TEST_DATA, sizeof (TEST_DATA), TRUE, 0, 0); + g_assert (hex); + g_assert_cmpstr (hex, ==, TEST_HEX); + g_free (hex); + + /* Encode with spaces */ + hex = egg_hex_encode_full (TEST_DATA, sizeof (TEST_DATA), TRUE, ' ', 1); + g_assert (hex); + g_assert_cmpstr (hex, ==, TEST_HEX_DELIM); + g_free (hex); +} + +static void +test_decode (void) +{ + guchar *data; + gsize n_data; + + data = egg_hex_decode (TEST_HEX, -1, &n_data); + g_assert (data); + g_assert (n_data == sizeof (TEST_DATA)); + g_assert (memcmp (data, TEST_DATA, n_data) == 0); + g_free (data); + + /* Nothing in, empty out */ + data = egg_hex_decode ("AB", 0, &n_data); + g_assert (data); + g_assert (n_data == 0); + g_free (data); + + /* Delimited*/ + data = egg_hex_decode_full (TEST_HEX_DELIM, -1, ' ', 1, &n_data); + g_assert (data); + g_assert (n_data == sizeof (TEST_DATA)); + g_assert (memcmp (data, TEST_DATA, n_data) == 0); + g_free (data); +} + +static void +test_decode_fail (void) +{ + guchar *data; + gsize n_data; + + /* Invalid input, null out */ + data = egg_hex_decode ("AB", 1, &n_data); + g_assert (!data); + + /* Bad characters, null out */ + data = egg_hex_decode ("ABXX", -1, &n_data); + g_assert (!data); + + /* Not Delimited, null out*/ + data = egg_hex_decode_full ("ABABAB", -1, ':', 1, &n_data); + g_assert (!data); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/hex/encode", test_encode); + g_test_add_func ("/hex/encode_spaces", test_encode_spaces); + g_test_add_func ("/hex/decode", test_decode); + g_test_add_func ("/hex/decode_fail", test_decode_fail); + + return g_test_run (); +} diff --git a/library/Makefile.am b/library/Makefile.am index 3a121a9..b77da6b 100644 --- a/library/Makefile.am +++ b/library/Makefile.am @@ -40,3 +40,6 @@ gsecret-dbus-generated.c: $(DBUS_XML_DEFINITIONS) Makefile.am $(AM_V_GEN) sed -i -e 's/gsecret_gen_/_gsecret_gen_/g' gsecret-dbus-generated.[ch] gsecret-dbus-generated.h: gsecret-dbus-generated.c + +check-memory: + make -C tests check-memory \ No newline at end of file diff --git a/library/gsecret-private.h b/library/gsecret-private.h index dd2681a..17c97c9 100644 --- a/library/gsecret-private.h +++ b/library/gsecret-private.h @@ -20,6 +20,12 @@ G_BEGIN_DECLS +typedef struct { + GVariant *in; + GVariant *out; + GCancellable *cancellable; +} GSecretParams; + #define GSECRET_SERVICE_PATH "/org/freedesktop/secrets" #define GSECRET_SERVICE_BUS_NAME "org.freedesktop.Secret.Service" @@ -28,6 +34,11 @@ G_BEGIN_DECLS #define GSECRET_COLLECTION_INTERFACE "org.freedesktop.Secret.Collection" +GSecretParams * _gsecret_params_new (GCancellable *cancellable, + GVariant *in); + +void _gsecret_params_free (gpointer data); + gchar * _gsecret_util_parent_path (const gchar *path); GVariant * _gsecret_util_variant_for_attributes (GHashTable *attributes); diff --git a/library/gsecret-service.c b/library/gsecret-service.c index eb3e84f..50e5be3 100644 --- a/library/gsecret-service.c +++ b/library/gsecret-service.c @@ -24,6 +24,7 @@ #include "egg/egg-libgcrypt.h" #endif +#include "egg/egg-hex.h" #include "egg/egg-secure-memory.h" #include @@ -209,9 +210,18 @@ request_open_session_aes (GSecretSession *session) g_assert (session->publi == NULL); /* Initialize our local parameters and values */ - if (!egg_dh_default_params ("ietf-ike-grp-modp-1024", + if (!egg_dh_default_params ("ietf-ike-grp-modp-1536", &session->prime, &base)) - g_return_val_if_reached (NULL); + 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); @@ -255,9 +265,21 @@ response_open_session_aes (GSecretSession *session, 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); @@ -633,9 +655,17 @@ service_decode_aes_secret (GSecretSession *session, 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); @@ -656,7 +686,7 @@ service_decode_aes_secret (GSecretSession *session, if (!pkcs7_unpad_bytes_in_place (padded, &n_padded)) { egg_secure_clear (padded, n_padded); egg_secure_free (padded); - g_message ("received an invalid, unencryptable, or non-utf8 secret"); + g_message ("received an invalid or unencryptable secret"); return FALSE; } @@ -900,11 +930,11 @@ on_search_items_complete (GObject *source, } void -gsecret_service_search_paths (GSecretService *self, - GHashTable *attributes, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +gsecret_service_search_for_paths (GSecretService *self, + GHashTable *attributes, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { GSimpleAsyncResult *res; @@ -913,7 +943,7 @@ gsecret_service_search_paths (GSecretService *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_search_paths); + gsecret_service_search_for_paths); g_dbus_proxy_call (G_DBUS_PROXY (self), "SearchItems", g_variant_new ("(@a{ss})", @@ -925,18 +955,18 @@ gsecret_service_search_paths (GSecretService *self, } gboolean -gsecret_service_search_paths_finish (GSecretService *self, - GAsyncResult *result, - gchar ***unlocked, - gchar ***locked, - GError **error) +gsecret_service_search_for_paths_finish (GSecretService *self, + GAsyncResult *result, + gchar ***unlocked, + gchar ***locked, + GError **error) { GVariant *response; GSimpleAsyncResult *res; gchar **dummy = NULL; g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), - gsecret_service_search_paths), FALSE); + gsecret_service_search_for_paths), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); res = G_SIMPLE_ASYNC_RESULT (result); @@ -957,12 +987,12 @@ gsecret_service_search_paths_finish (GSecretService *self, } gboolean -gsecret_service_search_paths_sync (GSecretService *self, - GHashTable *attributes, - GCancellable *cancellable, - gchar ***unlocked, - gchar ***locked, - GError **error) +gsecret_service_search_for_paths_sync (GSecretService *self, + GHashTable *attributes, + GCancellable *cancellable, + gchar ***unlocked, + gchar ***locked, + GError **error) { gchar **dummy = NULL; GVariant *response; @@ -993,3 +1023,251 @@ gsecret_service_search_paths_sync (GSecretService *self, return response != NULL; } + +static void +on_get_secrets_complete (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + GSecretParams *params = g_simple_async_result_get_op_res_gpointer (res); + GError *error = NULL; + + params->out = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, &error); + if (error != NULL) + g_simple_async_result_take_error (res, error); + g_simple_async_result_complete (res); + + g_object_unref (res); +} + +static void +on_get_secrets_session (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + GSecretParams *params = g_simple_async_result_get_op_res_gpointer (res); + GError *error = NULL; + const gchar *session; + + session = gsecret_service_ensure_session_finish (GSECRET_SERVICE (source), + result, &error); + if (error != NULL) { + g_simple_async_result_take_error (res, error); + g_simple_async_result_complete (res); + } else { + g_dbus_proxy_call (G_DBUS_PROXY (source), "GetSecrets", + g_variant_new ("(@aoo)", params->in, session), + G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, + params->cancellable, on_get_secrets_complete, + g_object_ref (res)); + } + + g_object_unref (res); +} + +void +gsecret_service_get_secret_for_path (GSecretService *self, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + GSecretParams *params; + + g_return_if_fail (GSECRET_IS_SERVICE (self)); + g_return_if_fail (object_path != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + gsecret_service_get_secret_for_path); + + params = _gsecret_params_new (cancellable, g_variant_new_objv (&object_path, 1)); + g_simple_async_result_set_op_res_gpointer (res, params, _gsecret_params_free); + + gsecret_service_ensure_session (self, cancellable, + on_get_secrets_session, + g_object_ref (res)); + + g_object_unref (res); +} + +static GSecretValue * +service_decode_get_secrets_first (GSecretService *self, + GVariant *out) +{ + 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); + g_variant_unref (variant); + break; + } + g_variant_iter_free (iter); + return value; +} + +static GHashTable * +service_decode_get_secrets_all (GSecretService *self, + GVariant *out) +{ + GVariantIter *iter; + GVariant *variant; + GHashTable *values; + GSecretValue *value; + gchar *path; + + 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); + if (value && path) + g_hash_table_insert (values, g_strdup (path), value); + } + g_variant_iter_free (iter); + return values; +} + +GSecretValue * +gsecret_service_get_secret_for_path_finish (GSecretService *self, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *res; + GSecretParams *params; + + g_return_val_if_fail (GSECRET_IS_SERVICE (self), NULL); + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), + gsecret_service_get_secret_for_path), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + res = G_SIMPLE_ASYNC_RESULT (result); + if (g_simple_async_result_propagate_error (res, error)) + return NULL; + + params = g_simple_async_result_get_op_res_gpointer (res); + return service_decode_get_secrets_first (self, params->out); +} + +GSecretValue * +gsecret_service_get_secret_for_path_sync (GSecretService *self, + const gchar *object_path, + GCancellable *cancellable, + GError **error) +{ + const gchar *session; + GSecretValue *value; + GVariant *out; + + g_return_val_if_fail (GSECRET_IS_SERVICE (self), NULL); + g_return_val_if_fail (object_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); + + session = gsecret_service_ensure_session_sync (self, cancellable, error); + if (!session) + return NULL; + + out = g_dbus_proxy_call_sync (G_DBUS_PROXY (self), "GetSecrets", + g_variant_new ("(@aoo)", + g_variant_new_objv (&object_path, 1), + session), + G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, + cancellable, error); + if (!out) + return NULL; + + value = service_decode_get_secrets_first (self, out); + g_variant_unref (out); + + return value; +} + +void +gsecret_service_get_secrets_for_paths (GSecretService *self, + const gchar **object_paths, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + GSecretParams *params; + + g_return_if_fail (GSECRET_IS_SERVICE (self)); + g_return_if_fail (object_paths != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + gsecret_service_get_secret_for_path); + + params = _gsecret_params_new (cancellable, g_variant_new_objv (object_paths, -1)); + g_simple_async_result_set_op_res_gpointer (res, params, _gsecret_params_free); + + gsecret_service_ensure_session (self, cancellable, + on_get_secrets_session, + g_object_ref (res)); + + g_object_unref (res); +} + +GHashTable * +gsecret_service_get_secrets_for_paths_finish (GSecretService *self, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *res; + GSecretParams *params; + + g_return_val_if_fail (GSECRET_IS_SERVICE (self), NULL); + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), + gsecret_service_get_secret_for_path), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + res = G_SIMPLE_ASYNC_RESULT (result); + if (g_simple_async_result_propagate_error (res, error)) + return NULL; + + params = g_simple_async_result_get_op_res_gpointer (res); + return service_decode_get_secrets_all (self, params->out); +} + +GHashTable * +gsecret_service_get_secrets_for_paths_sync (GSecretService *self, + const gchar **object_paths, + GCancellable *cancellable, + GError **error) +{ + const gchar *session; + GHashTable *values; + GVariant *out; + + g_return_val_if_fail (GSECRET_IS_SERVICE (self), NULL); + g_return_val_if_fail (object_paths != NULL, NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + session = gsecret_service_ensure_session_sync (self, cancellable, error); + if (!session) + return NULL; + + out = g_dbus_proxy_call_sync (G_DBUS_PROXY (self), "GetSecrets", + g_variant_new ("(@aoo)", + g_variant_new_objv (object_paths, -1), + session), + G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, + cancellable, error); + if (!out) + return NULL; + + values = service_decode_get_secrets_all (self, out); + g_variant_unref (out); + + return values; +} diff --git a/library/gsecret-service.h b/library/gsecret-service.h index 6ec832c..af13f19 100644 --- a/library/gsecret-service.h +++ b/library/gsecret-service.h @@ -15,6 +15,8 @@ #include +#include "gsecret-value.h" + G_BEGIN_DECLS #define GSECRET_TYPE_SERVICE (gsecret_service_get_type ()) @@ -110,24 +112,54 @@ gboolean gsecret_service_search_sync (GSecretService *se GError **error); #endif -void gsecret_service_search_paths (GSecretService *self, - GHashTable *attributes, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); +void gsecret_service_search_for_paths (GSecretService *self, + GHashTable *attributes, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); -gboolean gsecret_service_search_paths_finish (GSecretService *self, - GAsyncResult *result, - gchar ***unlocked, - gchar ***locked, - GError **error); +gboolean gsecret_service_search_for_paths_finish (GSecretService *self, + GAsyncResult *result, + gchar ***unlocked, + gchar ***locked, + GError **error); -gboolean gsecret_service_search_paths_sync (GSecretService *self, - GHashTable *attributes, - GCancellable *cancellable, - gchar ***unlocked, - gchar ***locked, - GError **error); +gboolean gsecret_service_search_for_paths_sync (GSecretService *self, + GHashTable *attributes, + GCancellable *cancellable, + gchar ***unlocked, + gchar ***locked, + GError **error); + +void gsecret_service_get_secret_for_path (GSecretService *self, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GSecretValue * gsecret_service_get_secret_for_path_finish (GSecretService *self, + GAsyncResult *result, + GError **error); + +GSecretValue * gsecret_service_get_secret_for_path_sync (GSecretService *self, + const gchar *object_path, + GCancellable *cancellable, + GError **error); + +void gsecret_service_get_secrets_for_paths (GSecretService *self, + const gchar **object_paths, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GHashTable * gsecret_service_get_secrets_for_paths_finish (GSecretService *self, + GAsyncResult *result, + GError **error); + +GHashTable * gsecret_service_get_secrets_for_paths_sync (GSecretService *self, + const gchar **object_paths, + GCancellable *cancellable, + GError **error); #if 0 void gsecret_service_lock (GSecretService *self, diff --git a/library/gsecret-util.c b/library/gsecret-util.c index 87d594c..0fd98dd 100644 --- a/library/gsecret-util.c +++ b/library/gsecret-util.c @@ -31,6 +31,32 @@ gsecret_error_get_quark (void) return quark; } +GSecretParams * +_gsecret_params_new (GCancellable *cancellable, + GVariant *in) +{ + GSecretParams *params; + + params = g_slice_new0 (GSecretParams); + params->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + params->in = g_variant_ref_sink (in); + + return params; +} + +void +_gsecret_params_free (gpointer data) +{ + GSecretParams *params = data; + + g_clear_object (¶ms->cancellable); + if (params->in) + g_variant_unref (params->in); + if (params->out) + g_variant_unref (params->out); + g_slice_free (GSecretParams, params); +} + gchar * _gsecret_util_parent_path (const gchar *path) { diff --git a/library/gsecret-value.c b/library/gsecret-value.c index a521ed3..6980264 100644 --- a/library/gsecret-value.c +++ b/library/gsecret-value.c @@ -49,7 +49,7 @@ gsecret_value_new (const gchar *secret, gssize length, const gchar *content_type { gchar *copy; - g_return_val_if_fail (!secret && length, NULL); + g_return_val_if_fail (secret == NULL || length != 0, NULL); g_return_val_if_fail (content_type, NULL); if (length < 0) @@ -67,14 +67,15 @@ gsecret_value_new_full (gchar *secret, gssize length, { GSecretValue *value; - g_return_val_if_fail (!secret && length, NULL); + g_return_val_if_fail (secret == NULL || length != 0, NULL); g_return_val_if_fail (content_type, NULL); if (length < 0) length = strlen (secret); value = g_slice_new0 (GSecretValue); - value->content_type = strdup (content_type); + value->refs = 1; + value->content_type = g_strdup (content_type); value->destroy = destroy; value->length = length; value->secret = secret; diff --git a/library/tests/mock-service-normal.py b/library/tests/mock-service-normal.py index ec0efdf..f96bbd5 100644 --- a/library/tests/mock-service-normal.py +++ b/library/tests/mock-service-normal.py @@ -3,15 +3,5 @@ import mock service = mock.SecretService() - -collection = mock.SecretCollection(service, "collection", locked=False) -mock.SecretItem(collection, "item_one", attributes={ "number": "1", "string": "one", "parity": "odd" }) -mock.SecretItem(collection, "item_two", attributes={ "number": "2", "string": "two", "parity": "even" }) -mock.SecretItem(collection, "item_three", attributes={ "number": "3", "string": "three", "parity": "odd" }) - -collection = mock.SecretCollection(service, "second", locked=True) -mock.SecretItem(collection, "item_one", attributes={ "number": "1", "string": "one", "parity": "odd" }) -mock.SecretItem(collection, "item_two", attributes={ "number": "2", "string": "two", "parity": "even" }) -mock.SecretItem(collection, "item_three", attributes={ "number": "3", "string": "three", "parity": "odd" }) - +service.add_standard_objects() service.listen() \ No newline at end of file diff --git a/library/tests/mock-service-only-plain.py b/library/tests/mock-service-only-plain.py index e06b9ba..f9f939c 100644 --- a/library/tests/mock-service-only-plain.py +++ b/library/tests/mock-service-only-plain.py @@ -3,5 +3,6 @@ import mock service = mock.SecretService() +service.add_standard_objects() service.algorithms = { "plain": mock.PlainAlgorithm() } service.listen() \ No newline at end of file diff --git a/library/tests/mock/dh.py b/library/tests/mock/dh.py index e48b85a..8d0171a 100644 --- a/library/tests/mock/dh.py +++ b/library/tests/mock/dh.py @@ -22,14 +22,14 @@ import math import random -PRIME = '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC9\x0F\xDA\xA2\x21\x68\xC2\x34\xC4' \ - '\xC6\x62\x8B\x80\xDC\x1C\xD1\x29\x02\x4E\x08\x8A\x67\xCC\x74\x02\x0B\xBE' \ - '\xA6\x3B\x13\x9B\x22\x51\x4A\x08\x79\x8E\x34\x04\xDD\xEF\x95\x19\xB3\xCD' \ - '\x3A\x43\x1B\x30\x2B\x0A\x6D\xF2\x5F\x14\x37\x4F\xE1\x35\x6D\x6D\x51\xC2' \ - '\x45\xE4\x85\xB5\x76\x62\x5E\x7E\xC6\xF4\x4C\x42\xE9\xA6\x37\xED\x6B\x0B' \ - '\xFF\x5C\xB6\xF4\x06\xB7\xED\xEE\x38\x6B\xFB\x5A\x89\x9F\xA5\xAE\x9F\x24' \ - '\x11\x7C\x4B\x1F\xE6\x49\x28\x66\x51\xEC\xE6\x53\x81\xFF\xFF\xFF\xFF\xFF' \ - '\xFF\xFF\xFF' +PRIME = '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC9\x0F\xDA\xA2\x21\x68\xC2\x34\xC4\xC6\x62\x8B\x80\xDC\x1C\xD1' \ + '\x29\x02\x4E\x08\x8A\x67\xCC\x74\x02\x0B\xBE\xA6\x3B\x13\x9B\x22\x51\x4A\x08\x79\x8E\x34\x04\xDD' \ + '\xEF\x95\x19\xB3\xCD\x3A\x43\x1B\x30\x2B\x0A\x6D\xF2\x5F\x14\x37\x4F\xE1\x35\x6D\x6D\x51\xC2\x45' \ + '\xE4\x85\xB5\x76\x62\x5E\x7E\xC6\xF4\x4C\x42\xE9\xA6\x37\xED\x6B\x0B\xFF\x5C\xB6\xF4\x06\xB7\xED' \ + '\xEE\x38\x6B\xFB\x5A\x89\x9F\xA5\xAE\x9F\x24\x11\x7C\x4B\x1F\xE6\x49\x28\x66\x51\xEC\xE4\x5B\x3D' \ + '\xC2\x00\x7C\xB8\xA1\x63\xBF\x05\x98\xDA\x48\x36\x1C\x55\xD3\x9A\x69\x16\x3F\xA8\xFD\x24\xCF\x5F' \ + '\x83\x65\x5D\x23\xDC\xA3\xAD\x96\x1C\x62\xF3\x56\x20\x85\x52\xBB\x9E\xD5\x29\x07\x70\x96\x96\x6D' \ + '\x67\x0C\x35\x4E\x4A\xBC\x98\x04\xF1\x74\x6C\x08\xCA\x23\x73\x27\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' def num_bits(number): if number == 0: @@ -67,6 +67,8 @@ def number_to_bytes(number): def generate_pair(): prime = bytes_to_number (PRIME) base = 2 + # print "mock prime: ", hex(prime) + # print " mock base: ", hex(base) bits = num_bits(prime) privat = 0 while privat == 0: @@ -77,4 +79,5 @@ def generate_pair(): def derive_key(privat, peer): prime = bytes_to_number (PRIME) key = pow(peer, privat, prime) + # print " mock ikm2: ", hex(key) return number_to_bytes(key) diff --git a/library/tests/mock/service.py b/library/tests/mock/service.py index cccd7e3..b3f9187 100644 --- a/library/tests/mock/service.py +++ b/library/tests/mock/service.py @@ -25,7 +25,10 @@ import dbus.service import dbus.glib import gobject +COLLECTION_PREFIX = "/org/freedesktop/secrets/collection/" + bus_name = 'org.freedesktop.Secret.MockService' +objects = { } class NotSupported(dbus.exceptions.DBusException): def __init__(self, msg): @@ -35,38 +38,75 @@ class InvalidArgs(dbus.exceptions.DBusException): def __init__(self, msg): dbus.exceptions.DBusException.__init__(self, msg, name="org.freedesktop.DBus.Error.InvalidArgs") +class IsLocked(dbus.exceptions.DBusException): + def __init__(self, msg): + dbus.exceptions.DBusException.__init__(self, msg, name="org.freedesktop.Secret.Error.IsLocked") + unique_identifier = 0 def next_identifier(): global unique_identifier unique_identifier += 1 return unique_identifier +def hex_encode(string): + return "".join([hex(ord(c))[2:].zfill(2) for c in string]) + + class PlainAlgorithm(): def negotiate(self, service, sender, param): if type (param) != dbus.String: raise InvalidArgs("invalid argument passed to OpenSession") - session = SecretSession(service, sender, None) + session = SecretSession(service, sender, self, None) return (dbus.String("", variant_level=1), session) + def encrypt(self, key, data): + return ("", data) + + class AesAlgorithm(): def negotiate(self, service, sender, param): if type (param) != dbus.ByteArray: raise InvalidArgs("invalid argument passed to OpenSession") - publi, privat = dh.generate_pair() + privat, publi = dh.generate_pair() peer = dh.bytes_to_number(param) + # print "mock publi: ", hex(publi) + # print " mock peer: ", hex(peer) ikm = dh.derive_key(privat, peer) + # print " mock ikm: ", hex_encode(ikm) key = hkdf.hkdf(ikm, 16) - session = SecretSession(service, sender, key) + # print " mock key: ", hex_encode(key) + session = SecretSession(service, sender, self, key) return (dbus.ByteArray(dh.number_to_bytes(publi), variant_level=1), session) + def encrypt(self, key, data): + key = map(ord, key) + data = aes.append_PKCS7_padding(data) + keysize = len(key) + iv = [ord(i) for i in os.urandom(16)] + mode = aes.AESModeOfOperation.modeOfOperation["CBC"] + moo = aes.AESModeOfOperation() + (mode, length, ciph) = moo.encrypt(data, mode, key, keysize, iv) + return ("".join([chr(i) for i in iv]), + "".join([chr(i) for i in ciph])) + + class SecretSession(dbus.service.Object): - def __init__(self, service, sender, key): + def __init__(self, service, sender, algorithm, key): self.sender = sender self.service = service + self.algorithm = algorithm self.key = key self.path = "/org/freedesktop/secrets/sessions/%d" % next_identifier() dbus.service.Object.__init__(self, service.bus_name, self.path) service.add_session(self) + objects[self.path] = self + + def encode_secret(self, secret, content_type): + (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") @dbus.service.method('org.freedesktop.Secret.Session') def Close(self): @@ -75,15 +115,19 @@ class SecretSession(dbus.service.Object): class SecretItem(dbus.service.Object): - def __init__(self, collection, identifier, label="Item", attributes={ }, secret=""): + def __init__(self, collection, identifier, label="Item", attributes={ }, + secret="", content_type="text/plain"): self.collection = collection self.identifier = identifier self.label = label self.secret = secret self.attributes = attributes - self.path = "/org/freedesktop/secrets/collection/%s/%s" % (collection.identifier, identifier) + self.content_type = content_type + self.locked = collection.locked + self.path = "%s/%s" % (collection.path, identifier) dbus.service.Object.__init__(self, collection.service.bus_name, self.path) collection.items[identifier] = self + objects[self.path] = self def match_attributes(self, attributes): for (key, value) in attributes.items(): @@ -91,6 +135,15 @@ class SecretItem(dbus.service.Object): return False return True + @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: + raise IsLocked("secret is locked: %s" % self.path) + return session.encode_secret(self.secret, self.content_type) + class SecretCollection(dbus.service.Object): def __init__(self, service, identifier, label="Collection", locked=False): @@ -99,9 +152,10 @@ class SecretCollection(dbus.service.Object): self.label = label self.locked = locked self.items = { } - self.path = "/org/freedesktop/secrets/collection/%s" % identifier + self.path = "%s%s" % (COLLECTION_PREFIX, identifier) dbus.service.Object.__init__(self, service.bus_name, self.path) service.collections[identifier] = self + objects[self.path] = self def search_items(self, attributes): results = [] @@ -136,6 +190,17 @@ class SecretService(dbus.service.Object): 'NameOwnerChanged', '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") + 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") + + collection = SecretCollection(self, "second", locked=True) + SecretItem(collection, "item_one", attributes={ "number": "1", "string": "one", "parity": "odd" }) + SecretItem(collection, "item_two", attributes={ "number": "2", "string": "two", "parity": "even" }) + SecretItem(collection, "item_three", attributes={ "number": "3", "string": "three", "parity": "odd" }) + def listen(self): loop = gobject.MainLoop() loop.run() @@ -148,6 +213,13 @@ class SecretService(dbus.service.Object): def remove_session(self, session): self.sessions[session.sender].remove(session) + def find_item(self, object): + if object.startswith(COLLECTION_PREFIX): + parts = object[len(COLLECTION_PREFIX):].split("/", 1) + if len(parts) == 2 and parts[0] in self.collections: + return self.collections[parts[0]].get(parts[1], None) + return None + @dbus.service.method('org.freedesktop.Secret.Service', byte_arrays=True, sender_keyword='sender') def OpenSession(self, algorithm, param, sender=None): assert type(algorithm) == dbus.String @@ -170,6 +242,17 @@ class SecretService(dbus.service.Object): unlocked.extend(items) return (dbus.Array(unlocked, "o"), dbus.Array(locked, "o")) + @dbus.service.method('org.freedesktop.Secret.Service', sender_keyword='sender') + def GetSecrets(self, item_paths, session_path, sender=None): + session = objects.get(session_path, None) + if not session or session.sender != sender: + raise InvalidArgs("session invalid: %s" % session_path) + results = dbus.Dictionary(signature="o(oayays)") + for item_path in item_paths: + item = objects.get(item_path, None) + if item and not item.locked: + results[item_path] = item.GetSecret(session_path, sender) + return results def parse_options(args): global bus_name diff --git a/library/tests/test-service.c b/library/tests/test-service.c index c31db2d..50fbbd0 100644 --- a/library/tests/test-service.c +++ b/library/tests/test-service.c @@ -57,7 +57,7 @@ static void teardown (Test *test, gconstpointer unused) { - g_clear_object (&test->service); + g_object_unref (test->service); egg_assert_not_object (test->service); g_clear_object (&test->connection); @@ -129,8 +129,8 @@ test_search_paths (Test *test, attributes = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (attributes, "number", "1"); - ret = gsecret_service_search_paths_sync (test->service, attributes, NULL, - &unlocked, &locked, &error); + ret = gsecret_service_search_for_paths_sync (test->service, attributes, NULL, + &unlocked, &locked, &error); g_assert_no_error (error); g_assert (ret == TRUE); @@ -160,14 +160,14 @@ test_search_paths_async (Test *test, attributes = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (attributes, "number", "1"); - gsecret_service_search_paths (test->service, attributes, NULL, - on_complete_get_result, &result); + gsecret_service_search_for_paths (test->service, attributes, NULL, + on_complete_get_result, &result); egg_test_wait (); g_assert (G_IS_ASYNC_RESULT (result)); - ret = gsecret_service_search_paths_finish (test->service, result, - &unlocked, &locked, - &error); + ret = gsecret_service_search_for_paths_finish (test->service, result, + &unlocked, &locked, + &error); g_assert_no_error (error); g_assert (ret == TRUE); @@ -197,33 +197,33 @@ test_search_paths_nulls (Test *test, attributes = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (attributes, "number", "1"); - ret = gsecret_service_search_paths_sync (test->service, attributes, NULL, - &paths, NULL, &error); + ret = gsecret_service_search_for_paths_sync (test->service, attributes, NULL, + &paths, NULL, &error); g_assert_no_error (error); g_assert (ret == TRUE); g_assert (paths != NULL); g_assert_cmpstr (paths[0], ==, "/org/freedesktop/secrets/collection/collection/item_one"); g_strfreev (paths); - ret = gsecret_service_search_paths_sync (test->service, attributes, NULL, - NULL, &paths, &error); + ret = gsecret_service_search_for_paths_sync (test->service, attributes, NULL, + NULL, &paths, &error); g_assert_no_error (error); g_assert (ret == TRUE); g_assert (paths != NULL); g_assert_cmpstr (paths[0], ==, "/org/freedesktop/secrets/collection/second/item_one"); g_strfreev (paths); - ret = gsecret_service_search_paths_sync (test->service, attributes, NULL, - NULL, NULL, &error); + ret = gsecret_service_search_for_paths_sync (test->service, attributes, NULL, + NULL, NULL, &error); g_assert_no_error (error); g_assert (ret == TRUE); - gsecret_service_search_paths (test->service, attributes, NULL, - on_complete_get_result, &result); + gsecret_service_search_for_paths (test->service, attributes, NULL, + on_complete_get_result, &result); egg_test_wait (); g_assert (G_IS_ASYNC_RESULT (result)); - ret = gsecret_service_search_paths_finish (test->service, result, - &paths, NULL, &error); + ret = gsecret_service_search_for_paths_finish (test->service, result, + &paths, NULL, &error); g_assert_no_error (error); g_assert (ret == TRUE); g_assert (paths != NULL); @@ -231,12 +231,12 @@ test_search_paths_nulls (Test *test, g_strfreev (paths); g_clear_object (&result); - gsecret_service_search_paths (test->service, attributes, NULL, - on_complete_get_result, &result); + gsecret_service_search_for_paths (test->service, attributes, NULL, + on_complete_get_result, &result); egg_test_wait (); g_assert (G_IS_ASYNC_RESULT (result)); - ret = gsecret_service_search_paths_finish (test->service, result, - NULL, &paths, &error); + ret = gsecret_service_search_for_paths_finish (test->service, result, + NULL, &paths, &error); g_assert_no_error (error); g_assert (ret == TRUE); g_assert (paths != NULL); @@ -244,12 +244,12 @@ test_search_paths_nulls (Test *test, g_strfreev (paths); g_clear_object (&result); - gsecret_service_search_paths (test->service, attributes, NULL, - on_complete_get_result, &result); + gsecret_service_search_for_paths (test->service, attributes, NULL, + on_complete_get_result, &result); egg_test_wait (); g_assert (G_IS_ASYNC_RESULT (result)); - ret = gsecret_service_search_paths_finish (test->service, result, - NULL, NULL, &error); + ret = gsecret_service_search_for_paths_finish (test->service, result, + NULL, NULL, &error); g_assert_no_error (error); g_assert (ret == TRUE); g_clear_object (&result); @@ -257,6 +257,154 @@ test_search_paths_nulls (Test *test, g_hash_table_unref (attributes); } +static void +test_secret_for_path (Test *test, + gconstpointer used) +{ + GSecretValue *value; + GError *error = NULL; + const gchar *path; + const gchar *password; + gsize length; + + path = "/org/freedesktop/secrets/collection/collection/item_one"; + value = gsecret_service_get_secret_for_path_sync (test->service, path, NULL, &error); + g_assert_no_error (error); + g_assert (value != NULL); + + password = gsecret_value_get (value, &length); + g_assert_cmpuint (length, ==, 3); + g_assert_cmpstr (password, ==, "uno"); + + password = gsecret_value_get (value, NULL); + g_assert_cmpstr (password, ==, "uno"); + + gsecret_value_unref (value); +} + +static void +test_secret_for_path_async (Test *test, + gconstpointer used) +{ + GSecretValue *value; + GError *error = NULL; + const gchar *path; + const gchar *password; + GAsyncResult *result = NULL; + gsize length; + + path = "/org/freedesktop/secrets/collection/collection/item_one"; + gsecret_service_get_secret_for_path (test->service, path, NULL, + on_complete_get_result, &result); + g_assert (result == NULL); + egg_test_wait (); + + value = gsecret_service_get_secret_for_path_finish (test->service, result, &error); + g_assert_no_error (error); + g_assert (value != NULL); + g_object_unref (result); + + password = gsecret_value_get (value, &length); + g_assert_cmpuint (length, ==, 3); + g_assert_cmpstr (password, ==, "uno"); + + password = gsecret_value_get (value, NULL); + g_assert_cmpstr (password, ==, "uno"); + + gsecret_value_unref (value); +} + +static void +test_secrets_for_paths (Test *test, + gconstpointer used) +{ + const gchar *path_item_one = "/org/freedesktop/secrets/collection/collection/item_one"; + const gchar *path_item_two = "/org/freedesktop/secrets/collection/collection/item_two"; + const gchar *paths[] = { + path_item_one, + path_item_two, + + /* This one is locked, and not returned */ + "/org/freedesktop/secrets/collection/second/item_one", + NULL + }; + + GSecretValue *value; + GHashTable *values; + GError *error = NULL; + const gchar *password; + gsize length; + + values = gsecret_service_get_secrets_for_paths_sync (test->service, paths, NULL, &error); + g_assert_no_error (error); + + g_assert (values != NULL); + g_assert_cmpuint (g_hash_table_size (values), ==, 2); + + value = g_hash_table_lookup (values, path_item_one); + g_assert (value != NULL); + password = gsecret_value_get (value, &length); + g_assert_cmpuint (length, ==, 3); + g_assert_cmpstr (password, ==, "uno"); + + value = g_hash_table_lookup (values, path_item_two); + g_assert (value != NULL); + password = gsecret_value_get (value, &length); + g_assert_cmpuint (length, ==, 3); + g_assert_cmpstr (password, ==, "dos"); + + g_hash_table_unref (values); +} + +static void +test_secrets_for_paths_async (Test *test, + gconstpointer used) +{ + const gchar *path_item_one = "/org/freedesktop/secrets/collection/collection/item_one"; + const gchar *path_item_two = "/org/freedesktop/secrets/collection/collection/item_two"; + const gchar *paths[] = { + path_item_one, + path_item_two, + + /* This one is locked, and not returned */ + "/org/freedesktop/secrets/collection/second/item_one", + NULL + }; + + GSecretValue *value; + GHashTable *values; + GError *error = NULL; + const gchar *password; + GAsyncResult *result = NULL; + gsize length; + + gsecret_service_get_secrets_for_paths (test->service, paths, NULL, + on_complete_get_result, &result); + g_assert (result == NULL); + egg_test_wait (); + + values = gsecret_service_get_secrets_for_paths_finish (test->service, result, &error); + g_assert_no_error (error); + g_object_unref (result); + + g_assert (values != NULL); + g_assert_cmpuint (g_hash_table_size (values), ==, 2); + + value = g_hash_table_lookup (values, path_item_one); + g_assert (value != NULL); + password = gsecret_value_get (value, &length); + g_assert_cmpuint (length, ==, 3); + g_assert_cmpstr (password, ==, "uno"); + + value = g_hash_table_lookup (values, path_item_two); + g_assert (value != NULL); + password = gsecret_value_get (value, &length); + g_assert_cmpuint (length, ==, 3); + g_assert_cmpstr (password, ==, "dos"); + + g_hash_table_unref (values); +} + int main (int argc, char **argv) { @@ -265,9 +413,14 @@ main (int argc, char **argv) g_type_init (); g_test_add_func ("/service/instance", test_instance); - g_test_add ("/service/search-paths", Test, "mock-service-normal.py", setup, test_search_paths, teardown); - g_test_add ("/service/search-paths-async", Test, "mock-service-normal.py", setup, test_search_paths_async, teardown); - g_test_add ("/service/search-paths-nulls", Test, "mock-service-normal.py", setup, test_search_paths_nulls, teardown); + g_test_add ("/service/search-for-paths", Test, "mock-service-normal.py", setup, test_search_paths, teardown); + g_test_add ("/service/search-for-paths-async", Test, "mock-service-normal.py", setup, test_search_paths_async, teardown); + g_test_add ("/service/search-for-paths-nulls", Test, "mock-service-normal.py", setup, test_search_paths_nulls, teardown); + g_test_add ("/service/secret-for-path", Test, "mock-service-normal.py", setup, test_secret_for_path, teardown); + g_test_add ("/service/secret-for-path-plain", Test, "mock-service-only-plain.py", setup, test_secret_for_path, teardown); + g_test_add ("/service/secret-for-path-async", Test, "mock-service-normal.py", setup, test_secret_for_path_async, teardown); + g_test_add ("/service/secrets-for-paths", Test, "mock-service-normal.py", setup, test_secrets_for_paths, teardown); + g_test_add ("/service/secrets-for-paths-async", Test, "mock-service-normal.py", setup, test_secrets_for_paths_async, teardown); return egg_tests_run_with_loop (); } diff --git a/library/tests/test-session.c b/library/tests/test-session.c index 4e25019..5611939 100644 --- a/library/tests/test-session.c +++ b/library/tests/test-session.c @@ -60,7 +60,8 @@ teardown (Test *test, { GError *error = NULL; - g_clear_object (&test->service); + g_object_unref (test->service); + egg_assert_not_object (test->service); g_assert (test->pid); if (kill (test->pid, SIGTERM) < 0) @@ -69,7 +70,7 @@ teardown (Test *test, g_dbus_connection_flush_sync (test->connection, NULL, &error); g_assert_no_error (error); - g_clear_object (&test->connection); + g_object_unref (test->connection); } static void