Implement gsecret_service_get_secrets_for_paths() and friends

* Lots of testing, fine tuning and other bits too.
This commit is contained in:
Stef Walter 2011-11-12 08:08:12 +01:00 committed by Stef Walter
parent 09a9d856d2
commit 17fade3173
24 changed files with 1053 additions and 129 deletions

7
.gitignore vendored
View File

@ -31,14 +31,13 @@ stamp*
/build/coverage* /build/coverage*
/build/m4 /build/m4
/build/valgrind-built.supp /build/valgrind-suppressions
/po/POTFILES /po/POTFILES
/po/gsecret.pot /po/gsecret.pot
/egg/tests/test-dh /egg/tests/test-*
/egg/tests/test-hkdf !/egg/tests/test-*.c
/egg/tests/test-secmem
/library/gsecret-dbus-generated.[ch] /library/gsecret-dbus-generated.[ch]
/library/tests/test-* /library/tests/test-*

View File

@ -28,17 +28,3 @@ check-memory:
test -d $(builddir)/$$subdir/tests && \ test -d $(builddir)/$$subdir/tests && \
make -C $(builddir)/$$subdir/tests check-memory; \ make -C $(builddir)/$$subdir/tests check-memory; \
done 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

View File

@ -1,11 +1,28 @@
NULL = 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 \ @for test in $(TEST_PROGS); do \
G_SLICE=always-malloc libtool --mode=execute \ G_SLICE=always-malloc libtool --mode=execute \
valgrind --trace-children=no --gen-suppressions=all \ 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 \ --leak-check=full --show-reachable=yes --num-callers=16 \
--quiet --error-exitcode=33 \ --quiet --error-exitcode=33 \
$(builddir)/$$test; \ $(builddir)/$$test; \
done 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

View File

@ -9,11 +9,11 @@ SUPPRESSIONS = \
pthread.supp \ pthread.supp \
unknown.supp unknown.supp
valgrind-built.supp: $(SUPPRESSIONS) valgrind-suppressions: $(SUPPRESSIONS)
$(AM_V_GEN) cat $(SUPPRESSIONS) > $@ $(AM_V_GEN) cat $(SUPPRESSIONS) > $@
EXTRA_DIST = \ EXTRA_DIST = \
$(VALGRIND_CONTRIB) \ $(VALGRIND_CONTRIB) \
$(SUPPRESSIONS) $(SUPPRESSIONS)
all-local: valgrind-built.supp all-local: valgrind-suppressions

View File

@ -238,6 +238,15 @@
... ...
fun:g_test_run_suite 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 g_test_run_suite__strconcat
Memcheck:Leak Memcheck:Leak
@ -301,3 +310,10 @@
... ...
fun:g_static_mutex_get_mutex_impl 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
}

View File

@ -344,3 +344,12 @@
... ...
fun:gdbus_shared_thread_func fun:gdbus_shared_thread_func
} }
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:g_main_context_add_poll_unlocked
...
fun:g_main_loop_run
fun:gdbus_shared_thread_func
}

View File

@ -19,7 +19,11 @@ ENCRYPTION_SRCS =
endif endif
libegg_la_SOURCES = \ libegg_la_SOURCES = \
egg-hex.c egg-hex.h \
egg-secure-memory.c egg-secure-memory.h \ egg-secure-memory.c egg-secure-memory.h \
egg-testing.c egg-testing.h \ egg-testing.c egg-testing.h \
$(ENCRYPTION_SRCS) \ $(ENCRYPTION_SRCS) \
$(BUILT_SOURCES) $(BUILT_SOURCES)
check-memory:
make -C tests check-memory

150
egg/egg-hex.c Normal file
View File

@ -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 <string.h>
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);
}

46
egg/egg-hex.h Normal file
View File

@ -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 <glib.h>
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_ */

View File

@ -138,7 +138,6 @@ on_loop_wait_timeout (gpointer data)
static gboolean static gboolean
loop_wait_until (int timeout) loop_wait_until (int timeout)
{ {
gboolean ret = FALSE;
gboolean timed_out = FALSE; gboolean timed_out = FALSE;
guint source; guint source;
@ -149,16 +148,10 @@ loop_wait_until (int timeout)
g_main_loop_run (wait_loop); g_main_loop_run (wait_loop);
if (timed_out) {
g_source_remove (source); g_source_remove (source);
ret = FALSE;
} else {
ret = TRUE;
}
g_main_loop_unref (wait_loop); g_main_loop_unref (wait_loop);
wait_loop = NULL; wait_loop = NULL;
return ret; return !timed_out;
} }
gint gint

View File

@ -12,6 +12,7 @@ LDADD = \
$(GLIB_LIBS) $(GLIB_LIBS)
TEST_PROGS = \ TEST_PROGS = \
test-hex \
test-secmem test-secmem
if WITH_GCRYPT if WITH_GCRYPT

121
egg/tests/test-hex.c Normal file
View File

@ -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 <stef@memberwebs.com>
*/
#include "config.h"
#include "egg/egg-hex.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
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 ();
}

View File

@ -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] $(AM_V_GEN) sed -i -e 's/gsecret_gen_/_gsecret_gen_/g' gsecret-dbus-generated.[ch]
gsecret-dbus-generated.h: gsecret-dbus-generated.c gsecret-dbus-generated.h: gsecret-dbus-generated.c
check-memory:
make -C tests check-memory

View File

@ -20,6 +20,12 @@
G_BEGIN_DECLS G_BEGIN_DECLS
typedef struct {
GVariant *in;
GVariant *out;
GCancellable *cancellable;
} GSecretParams;
#define GSECRET_SERVICE_PATH "/org/freedesktop/secrets" #define GSECRET_SERVICE_PATH "/org/freedesktop/secrets"
#define GSECRET_SERVICE_BUS_NAME "org.freedesktop.Secret.Service" #define GSECRET_SERVICE_BUS_NAME "org.freedesktop.Secret.Service"
@ -28,6 +34,11 @@ G_BEGIN_DECLS
#define GSECRET_COLLECTION_INTERFACE "org.freedesktop.Secret.Collection" #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); gchar * _gsecret_util_parent_path (const gchar *path);
GVariant * _gsecret_util_variant_for_attributes (GHashTable *attributes); GVariant * _gsecret_util_variant_for_attributes (GHashTable *attributes);

View File

@ -24,6 +24,7 @@
#include "egg/egg-libgcrypt.h" #include "egg/egg-libgcrypt.h"
#endif #endif
#include "egg/egg-hex.h"
#include "egg/egg-secure-memory.h" #include "egg/egg-secure-memory.h"
#include <glib.h> #include <glib.h>
@ -209,9 +210,18 @@ request_open_session_aes (GSecretSession *session)
g_assert (session->publi == NULL); g_assert (session->publi == NULL);
/* Initialize our local parameters and values */ /* 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)) &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, if (!egg_dh_gen_pair (session->prime, base, 0,
&session->publi, &session->privat)) &session->publi, &session->privat))
g_return_val_if_reached (NULL); 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_return_val_if_fail (gcry == 0, FALSE);
g_variant_unref (argument); 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); ikm = egg_dh_gen_secret (peer, session->privat, session->prime, &n_ikm);
gcry_mpi_release (peer); gcry_mpi_release (peer);
#if 0
g_printerr (" lib ikm: %s\n", egg_hex_encode (ikm, n_ikm));
#endif
if (ikm == NULL) { if (ikm == NULL) {
g_warning ("couldn't negotiate a valid AES session key"); g_warning ("couldn't negotiate a valid AES session key");
g_free (session->path); g_free (session->path);
@ -633,9 +655,17 @@ service_decode_aes_secret (GSecretSession *session,
return NULL; 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); gcry = gcry_cipher_setiv (cih, param, n_param);
g_return_val_if_fail (gcry == 0, NULL); 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); gcry = gcry_cipher_setkey (cih, session->key, session->n_key);
g_return_val_if_fail (gcry == 0, NULL); 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)) { if (!pkcs7_unpad_bytes_in_place (padded, &n_padded)) {
egg_secure_clear (padded, n_padded); egg_secure_clear (padded, n_padded);
egg_secure_free (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; return FALSE;
} }
@ -900,7 +930,7 @@ on_search_items_complete (GObject *source,
} }
void void
gsecret_service_search_paths (GSecretService *self, gsecret_service_search_for_paths (GSecretService *self,
GHashTable *attributes, GHashTable *attributes,
GCancellable *cancellable, GCancellable *cancellable,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
@ -913,7 +943,7 @@ gsecret_service_search_paths (GSecretService *self,
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, 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_dbus_proxy_call (G_DBUS_PROXY (self), "SearchItems",
g_variant_new ("(@a{ss})", g_variant_new ("(@a{ss})",
@ -925,7 +955,7 @@ gsecret_service_search_paths (GSecretService *self,
} }
gboolean gboolean
gsecret_service_search_paths_finish (GSecretService *self, gsecret_service_search_for_paths_finish (GSecretService *self,
GAsyncResult *result, GAsyncResult *result,
gchar ***unlocked, gchar ***unlocked,
gchar ***locked, gchar ***locked,
@ -936,7 +966,7 @@ gsecret_service_search_paths_finish (GSecretService *self,
gchar **dummy = NULL; gchar **dummy = NULL;
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), 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); g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
res = G_SIMPLE_ASYNC_RESULT (result); res = G_SIMPLE_ASYNC_RESULT (result);
@ -957,7 +987,7 @@ gsecret_service_search_paths_finish (GSecretService *self,
} }
gboolean gboolean
gsecret_service_search_paths_sync (GSecretService *self, gsecret_service_search_for_paths_sync (GSecretService *self,
GHashTable *attributes, GHashTable *attributes,
GCancellable *cancellable, GCancellable *cancellable,
gchar ***unlocked, gchar ***unlocked,
@ -993,3 +1023,251 @@ gsecret_service_search_paths_sync (GSecretService *self,
return response != NULL; 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;
}

View File

@ -15,6 +15,8 @@
#include <gio/gio.h> #include <gio/gio.h>
#include "gsecret-value.h"
G_BEGIN_DECLS G_BEGIN_DECLS
#define GSECRET_TYPE_SERVICE (gsecret_service_get_type ()) #define GSECRET_TYPE_SERVICE (gsecret_service_get_type ())
@ -110,25 +112,55 @@ gboolean gsecret_service_search_sync (GSecretService *se
GError **error); GError **error);
#endif #endif
void gsecret_service_search_paths (GSecretService *self, void gsecret_service_search_for_paths (GSecretService *self,
GHashTable *attributes, GHashTable *attributes,
GCancellable *cancellable, GCancellable *cancellable,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data); gpointer user_data);
gboolean gsecret_service_search_paths_finish (GSecretService *self, gboolean gsecret_service_search_for_paths_finish (GSecretService *self,
GAsyncResult *result, GAsyncResult *result,
gchar ***unlocked, gchar ***unlocked,
gchar ***locked, gchar ***locked,
GError **error); GError **error);
gboolean gsecret_service_search_paths_sync (GSecretService *self, gboolean gsecret_service_search_for_paths_sync (GSecretService *self,
GHashTable *attributes, GHashTable *attributes,
GCancellable *cancellable, GCancellable *cancellable,
gchar ***unlocked, gchar ***unlocked,
gchar ***locked, gchar ***locked,
GError **error); 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 #if 0
void gsecret_service_lock (GSecretService *self, void gsecret_service_lock (GSecretService *self,
GList *objects, GList *objects,

View File

@ -31,6 +31,32 @@ gsecret_error_get_quark (void)
return quark; 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 (&params->cancellable);
if (params->in)
g_variant_unref (params->in);
if (params->out)
g_variant_unref (params->out);
g_slice_free (GSecretParams, params);
}
gchar * gchar *
_gsecret_util_parent_path (const gchar *path) _gsecret_util_parent_path (const gchar *path)
{ {

View File

@ -49,7 +49,7 @@ gsecret_value_new (const gchar *secret, gssize length, const gchar *content_type
{ {
gchar *copy; 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); g_return_val_if_fail (content_type, NULL);
if (length < 0) if (length < 0)
@ -67,14 +67,15 @@ gsecret_value_new_full (gchar *secret, gssize length,
{ {
GSecretValue *value; 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); g_return_val_if_fail (content_type, NULL);
if (length < 0) if (length < 0)
length = strlen (secret); length = strlen (secret);
value = g_slice_new0 (GSecretValue); 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->destroy = destroy;
value->length = length; value->length = length;
value->secret = secret; value->secret = secret;

View File

@ -3,15 +3,5 @@
import mock import mock
service = mock.SecretService() service = mock.SecretService()
service.add_standard_objects()
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.listen() service.listen()

View File

@ -3,5 +3,6 @@
import mock import mock
service = mock.SecretService() service = mock.SecretService()
service.add_standard_objects()
service.algorithms = { "plain": mock.PlainAlgorithm() } service.algorithms = { "plain": mock.PlainAlgorithm() }
service.listen() service.listen()

View File

@ -22,14 +22,14 @@
import math import math
import random import random
PRIME = '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC9\x0F\xDA\xA2\x21\x68\xC2\x34\xC4' \ 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' \
'\xC6\x62\x8B\x80\xDC\x1C\xD1\x29\x02\x4E\x08\x8A\x67\xCC\x74\x02\x0B\xBE' \ '\x29\x02\x4E\x08\x8A\x67\xCC\x74\x02\x0B\xBE\xA6\x3B\x13\x9B\x22\x51\x4A\x08\x79\x8E\x34\x04\xDD' \
'\xA6\x3B\x13\x9B\x22\x51\x4A\x08\x79\x8E\x34\x04\xDD\xEF\x95\x19\xB3\xCD' \ '\xEF\x95\x19\xB3\xCD\x3A\x43\x1B\x30\x2B\x0A\x6D\xF2\x5F\x14\x37\x4F\xE1\x35\x6D\x6D\x51\xC2\x45' \
'\x3A\x43\x1B\x30\x2B\x0A\x6D\xF2\x5F\x14\x37\x4F\xE1\x35\x6D\x6D\x51\xC2' \ '\xE4\x85\xB5\x76\x62\x5E\x7E\xC6\xF4\x4C\x42\xE9\xA6\x37\xED\x6B\x0B\xFF\x5C\xB6\xF4\x06\xB7\xED' \
'\x45\xE4\x85\xB5\x76\x62\x5E\x7E\xC6\xF4\x4C\x42\xE9\xA6\x37\xED\x6B\x0B' \ '\xEE\x38\x6B\xFB\x5A\x89\x9F\xA5\xAE\x9F\x24\x11\x7C\x4B\x1F\xE6\x49\x28\x66\x51\xEC\xE4\x5B\x3D' \
'\xFF\x5C\xB6\xF4\x06\xB7\xED\xEE\x38\x6B\xFB\x5A\x89\x9F\xA5\xAE\x9F\x24' \ '\xC2\x00\x7C\xB8\xA1\x63\xBF\x05\x98\xDA\x48\x36\x1C\x55\xD3\x9A\x69\x16\x3F\xA8\xFD\x24\xCF\x5F' \
'\x11\x7C\x4B\x1F\xE6\x49\x28\x66\x51\xEC\xE6\x53\x81\xFF\xFF\xFF\xFF\xFF' \ '\x83\x65\x5D\x23\xDC\xA3\xAD\x96\x1C\x62\xF3\x56\x20\x85\x52\xBB\x9E\xD5\x29\x07\x70\x96\x96\x6D' \
'\xFF\xFF\xFF' '\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): def num_bits(number):
if number == 0: if number == 0:
@ -67,6 +67,8 @@ def number_to_bytes(number):
def generate_pair(): def generate_pair():
prime = bytes_to_number (PRIME) prime = bytes_to_number (PRIME)
base = 2 base = 2
# print "mock prime: ", hex(prime)
# print " mock base: ", hex(base)
bits = num_bits(prime) bits = num_bits(prime)
privat = 0 privat = 0
while privat == 0: while privat == 0:
@ -77,4 +79,5 @@ def generate_pair():
def derive_key(privat, peer): def derive_key(privat, peer):
prime = bytes_to_number (PRIME) prime = bytes_to_number (PRIME)
key = pow(peer, privat, prime) key = pow(peer, privat, prime)
# print " mock ikm2: ", hex(key)
return number_to_bytes(key) return number_to_bytes(key)

View File

@ -25,7 +25,10 @@ import dbus.service
import dbus.glib import dbus.glib
import gobject import gobject
COLLECTION_PREFIX = "/org/freedesktop/secrets/collection/"
bus_name = 'org.freedesktop.Secret.MockService' bus_name = 'org.freedesktop.Secret.MockService'
objects = { }
class NotSupported(dbus.exceptions.DBusException): class NotSupported(dbus.exceptions.DBusException):
def __init__(self, msg): def __init__(self, msg):
@ -35,38 +38,75 @@ class InvalidArgs(dbus.exceptions.DBusException):
def __init__(self, msg): def __init__(self, msg):
dbus.exceptions.DBusException.__init__(self, msg, name="org.freedesktop.DBus.Error.InvalidArgs") 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 unique_identifier = 0
def next_identifier(): def next_identifier():
global unique_identifier global unique_identifier
unique_identifier += 1 unique_identifier += 1
return unique_identifier return unique_identifier
def hex_encode(string):
return "".join([hex(ord(c))[2:].zfill(2) for c in string])
class PlainAlgorithm(): class PlainAlgorithm():
def negotiate(self, service, sender, param): def negotiate(self, service, sender, param):
if type (param) != dbus.String: if type (param) != dbus.String:
raise InvalidArgs("invalid argument passed to OpenSession") 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) return (dbus.String("", variant_level=1), session)
def encrypt(self, key, data):
return ("", data)
class AesAlgorithm(): class AesAlgorithm():
def negotiate(self, service, sender, param): def negotiate(self, service, sender, param):
if type (param) != dbus.ByteArray: if type (param) != dbus.ByteArray:
raise InvalidArgs("invalid argument passed to OpenSession") raise InvalidArgs("invalid argument passed to OpenSession")
publi, privat = dh.generate_pair() privat, publi = dh.generate_pair()
peer = dh.bytes_to_number(param) peer = dh.bytes_to_number(param)
# print "mock publi: ", hex(publi)
# print " mock peer: ", hex(peer)
ikm = dh.derive_key(privat, peer) ikm = dh.derive_key(privat, peer)
# print " mock ikm: ", hex_encode(ikm)
key = hkdf.hkdf(ikm, 16) 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) 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): class SecretSession(dbus.service.Object):
def __init__(self, service, sender, key): def __init__(self, service, sender, algorithm, key):
self.sender = sender self.sender = sender
self.service = service self.service = service
self.algorithm = algorithm
self.key = key self.key = key
self.path = "/org/freedesktop/secrets/sessions/%d" % next_identifier() self.path = "/org/freedesktop/secrets/sessions/%d" % next_identifier()
dbus.service.Object.__init__(self, service.bus_name, self.path) dbus.service.Object.__init__(self, service.bus_name, self.path)
service.add_session(self) 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') @dbus.service.method('org.freedesktop.Secret.Session')
def Close(self): def Close(self):
@ -75,15 +115,19 @@ class SecretSession(dbus.service.Object):
class SecretItem(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.collection = collection
self.identifier = identifier self.identifier = identifier
self.label = label self.label = label
self.secret = secret self.secret = secret
self.attributes = attributes 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) dbus.service.Object.__init__(self, collection.service.bus_name, self.path)
collection.items[identifier] = self collection.items[identifier] = self
objects[self.path] = self
def match_attributes(self, attributes): def match_attributes(self, attributes):
for (key, value) in attributes.items(): for (key, value) in attributes.items():
@ -91,6 +135,15 @@ class SecretItem(dbus.service.Object):
return False return False
return True 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): class SecretCollection(dbus.service.Object):
def __init__(self, service, identifier, label="Collection", locked=False): def __init__(self, service, identifier, label="Collection", locked=False):
@ -99,9 +152,10 @@ class SecretCollection(dbus.service.Object):
self.label = label self.label = label
self.locked = locked self.locked = locked
self.items = { } 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) dbus.service.Object.__init__(self, service.bus_name, self.path)
service.collections[identifier] = self service.collections[identifier] = self
objects[self.path] = self
def search_items(self, attributes): def search_items(self, attributes):
results = [] results = []
@ -136,6 +190,17 @@ class SecretService(dbus.service.Object):
'NameOwnerChanged', 'NameOwnerChanged',
'org.freedesktop.DBus') '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): def listen(self):
loop = gobject.MainLoop() loop = gobject.MainLoop()
loop.run() loop.run()
@ -148,6 +213,13 @@ class SecretService(dbus.service.Object):
def remove_session(self, session): def remove_session(self, session):
self.sessions[session.sender].remove(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') @dbus.service.method('org.freedesktop.Secret.Service', byte_arrays=True, sender_keyword='sender')
def OpenSession(self, algorithm, param, sender=None): def OpenSession(self, algorithm, param, sender=None):
assert type(algorithm) == dbus.String assert type(algorithm) == dbus.String
@ -170,6 +242,17 @@ class SecretService(dbus.service.Object):
unlocked.extend(items) unlocked.extend(items)
return (dbus.Array(unlocked, "o"), dbus.Array(locked, "o")) 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): def parse_options(args):
global bus_name global bus_name

View File

@ -57,7 +57,7 @@ static void
teardown (Test *test, teardown (Test *test,
gconstpointer unused) gconstpointer unused)
{ {
g_clear_object (&test->service); g_object_unref (test->service);
egg_assert_not_object (test->service); egg_assert_not_object (test->service);
g_clear_object (&test->connection); g_clear_object (&test->connection);
@ -129,7 +129,7 @@ test_search_paths (Test *test,
attributes = g_hash_table_new (g_str_hash, g_str_equal); attributes = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (attributes, "number", "1"); g_hash_table_insert (attributes, "number", "1");
ret = gsecret_service_search_paths_sync (test->service, attributes, NULL, ret = gsecret_service_search_for_paths_sync (test->service, attributes, NULL,
&unlocked, &locked, &error); &unlocked, &locked, &error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert (ret == TRUE); g_assert (ret == TRUE);
@ -160,12 +160,12 @@ test_search_paths_async (Test *test,
attributes = g_hash_table_new (g_str_hash, g_str_equal); attributes = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (attributes, "number", "1"); g_hash_table_insert (attributes, "number", "1");
gsecret_service_search_paths (test->service, attributes, NULL, gsecret_service_search_for_paths (test->service, attributes, NULL,
on_complete_get_result, &result); on_complete_get_result, &result);
egg_test_wait (); egg_test_wait ();
g_assert (G_IS_ASYNC_RESULT (result)); g_assert (G_IS_ASYNC_RESULT (result));
ret = gsecret_service_search_paths_finish (test->service, result, ret = gsecret_service_search_for_paths_finish (test->service, result,
&unlocked, &locked, &unlocked, &locked,
&error); &error);
g_assert_no_error (error); g_assert_no_error (error);
@ -197,7 +197,7 @@ test_search_paths_nulls (Test *test,
attributes = g_hash_table_new (g_str_hash, g_str_equal); attributes = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (attributes, "number", "1"); g_hash_table_insert (attributes, "number", "1");
ret = gsecret_service_search_paths_sync (test->service, attributes, NULL, ret = gsecret_service_search_for_paths_sync (test->service, attributes, NULL,
&paths, NULL, &error); &paths, NULL, &error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert (ret == TRUE); g_assert (ret == TRUE);
@ -205,7 +205,7 @@ test_search_paths_nulls (Test *test,
g_assert_cmpstr (paths[0], ==, "/org/freedesktop/secrets/collection/collection/item_one"); g_assert_cmpstr (paths[0], ==, "/org/freedesktop/secrets/collection/collection/item_one");
g_strfreev (paths); g_strfreev (paths);
ret = gsecret_service_search_paths_sync (test->service, attributes, NULL, ret = gsecret_service_search_for_paths_sync (test->service, attributes, NULL,
NULL, &paths, &error); NULL, &paths, &error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert (ret == TRUE); g_assert (ret == TRUE);
@ -213,16 +213,16 @@ test_search_paths_nulls (Test *test,
g_assert_cmpstr (paths[0], ==, "/org/freedesktop/secrets/collection/second/item_one"); g_assert_cmpstr (paths[0], ==, "/org/freedesktop/secrets/collection/second/item_one");
g_strfreev (paths); g_strfreev (paths);
ret = gsecret_service_search_paths_sync (test->service, attributes, NULL, ret = gsecret_service_search_for_paths_sync (test->service, attributes, NULL,
NULL, NULL, &error); NULL, NULL, &error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert (ret == TRUE); g_assert (ret == TRUE);
gsecret_service_search_paths (test->service, attributes, NULL, gsecret_service_search_for_paths (test->service, attributes, NULL,
on_complete_get_result, &result); on_complete_get_result, &result);
egg_test_wait (); egg_test_wait ();
g_assert (G_IS_ASYNC_RESULT (result)); g_assert (G_IS_ASYNC_RESULT (result));
ret = gsecret_service_search_paths_finish (test->service, result, ret = gsecret_service_search_for_paths_finish (test->service, result,
&paths, NULL, &error); &paths, NULL, &error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert (ret == TRUE); g_assert (ret == TRUE);
@ -231,11 +231,11 @@ test_search_paths_nulls (Test *test,
g_strfreev (paths); g_strfreev (paths);
g_clear_object (&result); g_clear_object (&result);
gsecret_service_search_paths (test->service, attributes, NULL, gsecret_service_search_for_paths (test->service, attributes, NULL,
on_complete_get_result, &result); on_complete_get_result, &result);
egg_test_wait (); egg_test_wait ();
g_assert (G_IS_ASYNC_RESULT (result)); g_assert (G_IS_ASYNC_RESULT (result));
ret = gsecret_service_search_paths_finish (test->service, result, ret = gsecret_service_search_for_paths_finish (test->service, result,
NULL, &paths, &error); NULL, &paths, &error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert (ret == TRUE); g_assert (ret == TRUE);
@ -244,11 +244,11 @@ test_search_paths_nulls (Test *test,
g_strfreev (paths); g_strfreev (paths);
g_clear_object (&result); g_clear_object (&result);
gsecret_service_search_paths (test->service, attributes, NULL, gsecret_service_search_for_paths (test->service, attributes, NULL,
on_complete_get_result, &result); on_complete_get_result, &result);
egg_test_wait (); egg_test_wait ();
g_assert (G_IS_ASYNC_RESULT (result)); g_assert (G_IS_ASYNC_RESULT (result));
ret = gsecret_service_search_paths_finish (test->service, result, ret = gsecret_service_search_for_paths_finish (test->service, result,
NULL, NULL, &error); NULL, NULL, &error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert (ret == TRUE); g_assert (ret == TRUE);
@ -257,6 +257,154 @@ test_search_paths_nulls (Test *test,
g_hash_table_unref (attributes); 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 int
main (int argc, char **argv) main (int argc, char **argv)
{ {
@ -265,9 +413,14 @@ main (int argc, char **argv)
g_type_init (); g_type_init ();
g_test_add_func ("/service/instance", test_instance); 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-for-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-for-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-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 (); return egg_tests_run_with_loop ();
} }

View File

@ -60,7 +60,8 @@ teardown (Test *test,
{ {
GError *error = NULL; GError *error = NULL;
g_clear_object (&test->service); g_object_unref (test->service);
egg_assert_not_object (test->service);
g_assert (test->pid); g_assert (test->pid);
if (kill (test->pid, SIGTERM) < 0) if (kill (test->pid, SIGTERM) < 0)
@ -69,7 +70,7 @@ teardown (Test *test,
g_dbus_connection_flush_sync (test->connection, NULL, &error); g_dbus_connection_flush_sync (test->connection, NULL, &error);
g_assert_no_error (error); g_assert_no_error (error);
g_clear_object (&test->connection); g_object_unref (test->connection);
} }
static void static void