[gnome-keyring/dbus-api] [secrets] Port over some parts of the keyring parsing.
- From: Stefan Walter <stefw src gnome org>
- To: svn-commits-list gnome org
- Subject: [gnome-keyring/dbus-api] [secrets] Port over some parts of the keyring parsing.
- Date: Mon, 27 Jul 2009 15:13:45 +0000 (UTC)
commit 6a9310cc435346b75147743dec9e80ab133457e8
Author: Stef Walter <stef memberwebs com>
Date: Sun Jul 26 15:07:14 2009 +0000
[secrets] Port over some parts of the keyring parsing.
The code compiles, but does not run, and is not tested.
egg/egg-buffer.c | 8 +-
egg/egg-buffer.h | 15 +-
pkcs11/gck/gck-login.h | 4 +-
pkcs11/secret-store/Makefile.am | 4 +-
pkcs11/secret-store/gck-secret-binary.c | 929 +++++++++++++++++++++++++++
pkcs11/secret-store/gck-secret-binary.h | 37 ++
pkcs11/secret-store/gck-secret-collection.c | 64 ++-
pkcs11/secret-store/gck-secret-collection.h | 24 +-
pkcs11/secret-store/gck-secret-compat.h | 41 ++
pkcs11/secret-store/gck-secret-fields.c | 31 +
pkcs11/secret-store/gck-secret-fields.h | 12 +-
pkcs11/secret-store/gck-secret-item.h | 3 +-
pkcs11/secret-store/gck-secret-module.c | 356 ++++++++++
pkcs11/secret-store/gck-secret-module.h | 44 ++
pkcs11/secret-store/gck-secret-object.h | 11 +-
pkcs11/secret-store/gck-secret-search.h | 7 +-
pkcs11/secret-store/gck-secret-store.h | 29 +
pkcs11/secret-store/gck-secret-textual.c | 545 ++++++++++++++++
pkcs11/secret-store/gck-secret-textual.h | 37 ++
pkcs11/secret-store/gck-secret-types.h | 31 +
20 files changed, 2207 insertions(+), 25 deletions(-)
---
diff --git a/egg/egg-buffer.c b/egg/egg-buffer.c
index 16eb12f..960d01a 100644
--- a/egg/egg-buffer.c
+++ b/egg/egg-buffer.c
@@ -60,15 +60,15 @@ egg_buffer_init_full (EggBuffer *buffer, size_t reserve, EggBufferAllocator allo
}
void
-egg_buffer_init_static (EggBuffer* buffer, unsigned char *buf, size_t len)
+egg_buffer_init_static (EggBuffer* buffer, const unsigned char *buf, size_t len)
{
memset (buffer, 0, sizeof (*buffer));
-
- buffer->buf = buf;
+
+ buffer->buf = (unsigned char*)buf;
buffer->len = len;
buffer->allocated_len = len;
buffer->failures = 0;
-
+
/* A null allocator, and the buffer can't change in size */
buffer->allocator = NULL;
}
diff --git a/egg/egg-buffer.h b/egg/egg-buffer.h
index 1bc0f80..9249574 100644
--- a/egg/egg-buffer.h
+++ b/egg/egg-buffer.h
@@ -72,19 +72,22 @@ int egg_buffer_init (EggBuffer *buffer, size_t reser
int egg_buffer_init_full (EggBuffer *buffer,
size_t reserve,
EggBufferAllocator allocator);
-
-void egg_buffer_init_static (EggBuffer *buffer,
- unsigned char *buf,
+
+void egg_buffer_init_static (EggBuffer *buffer,
+ const unsigned char *buf,
size_t len);
-void egg_buffer_init_allocated (EggBuffer *buffer,
- unsigned char *buf,
+void egg_buffer_init_allocated (EggBuffer *buffer,
+ unsigned char *buf,
size_t len,
EggBufferAllocator allocator);
void egg_buffer_uninit (EggBuffer *buffer);
-int egg_buffer_set_allocator (EggBuffer *buffer,
+unsigned char* egg_buffer_uninit_steal (EggBuffer *buffer,
+ size_t *n_result);
+
+int egg_buffer_set_allocator (EggBuffer *buffer,
EggBufferAllocator allocator);
void egg_buffer_reset (EggBuffer *buffer);
diff --git a/pkcs11/gck/gck-login.h b/pkcs11/gck/gck-login.h
index c6fc1f9..8f83451 100644
--- a/pkcs11/gck/gck-login.h
+++ b/pkcs11/gck/gck-login.h
@@ -46,7 +46,9 @@ GType gck_login_get_type (void);
GckLogin* gck_login_new (CK_UTF8CHAR_PTR pin,
CK_ULONG n_pin);
-const gchar* gck_login_get_password (GckLogin *self,
+GckLogin* gck_login_new_from_password (const gchar *password);
+
+const gchar* gck_login_get_password (GckLogin *self,
gsize *n_pin);
gboolean gck_login_equals (GckLogin *self,
diff --git a/pkcs11/secret-store/Makefile.am b/pkcs11/secret-store/Makefile.am
index e3fdd33..1c1224a 100644
--- a/pkcs11/secret-store/Makefile.am
+++ b/pkcs11/secret-store/Makefile.am
@@ -13,10 +13,12 @@ noinst_LTLIBRARIES = \
libgck-secret-store.la
libgck_secret_store_la_SOURCES = \
+ gck-secret-binary.c gck-secret-binary.h \
gck-secret-collection.h gck-secret-collection.c \
gck-secret-fields.h gck-secret-fields.c \
gck-secret-item.h gck-secret-item.c \
gck-secret-object.h gck-secret-object.c \
- gck-secret-search.h gck-secret-search.c
+ gck-secret-search.h gck-secret-search.c \
+ gck-secret-textual.c gck-secret-textual.h
# -------------------------------------------------------------------------------
diff --git a/pkcs11/secret-store/gck-secret-binary.c b/pkcs11/secret-store/gck-secret-binary.c
new file mode 100644
index 0000000..073b6c2
--- /dev/null
+++ b/pkcs11/secret-store/gck-secret-binary.c
@@ -0,0 +1,929 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-secret-binary.c - The binary encrypted format of a keyring
+
+ Copyright (C) 2003 Red Hat, Inc
+ Copyright (C) 2007, 2009 Stefan Walter
+
+ Gnome keyring is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ Gnome keyring 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Author: Alexander Larsson <alexl redhat com>
+ Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "config.h"
+
+#include "gck-secret-binary.h"
+#include "gck-secret-collection.h"
+#include "gck-secret-compat.h"
+#include "gck-secret-fields.h"
+#include "gck-secret-item.h"
+
+#include "egg/egg-buffer.h"
+#include "egg/egg-symkey.h"
+#include "egg/egg-secure-memory.h"
+
+#include "gck/gck-login.h"
+
+#include <glib.h>
+
+#include <gcrypt.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+/* -----------------------------------------------------------------------------
+ * DECLARATIONS
+ */
+
+#define LOCK_ON_IDLE_FLAG (1<<0)
+
+typedef struct {
+ /* unencrypted: */
+ guint32 id;
+ gchar *identifier;
+ guint32 type;
+ GHashTable *hashed_attributes;
+
+ /* encrypted: */
+ char *display_name;
+ char *secret;
+ time_t ctime;
+ time_t mtime;
+ GHashTable *attributes;
+ GList *acl;
+} ItemInfo;
+
+#define KEYRING_FILE_HEADER "GnomeKeyring\n\r\0\n"
+#define KEYRING_FILE_HEADER_LEN 16
+
+/* -----------------------------------------------------------------------------
+ * BUFFER UTILITY FUNCTIONS
+ */
+
+static gboolean
+buffer_get_bytes (EggBuffer *buffer, gsize offset, gsize *next_offset,
+ guchar *out, gsize n_bytes)
+{
+ if (buffer->len < n_bytes || offset > buffer->len - n_bytes)
+ return FALSE;
+ memcpy (out, buffer->buf + offset, n_bytes);
+ *next_offset = offset + n_bytes;
+ return TRUE;
+}
+
+static gboolean
+buffer_add_time (EggBuffer *buffer, glong time)
+{
+ guint64 val = time;
+ return egg_buffer_add_uint32 (buffer, ((val >> 32) & 0xffffffff)) &&
+ egg_buffer_add_uint32 (buffer, (val & 0xffffffff));
+}
+
+static gboolean
+buffer_get_time (EggBuffer *buffer, gsize offset, gsize *next_offset, glong *time)
+{
+ guint32 a, b;
+ guint64 val;
+
+ if (!egg_buffer_get_uint32 (buffer, offset, &offset, &a) ||
+ !egg_buffer_get_uint32 (buffer, offset, &offset, &b))
+ return FALSE;
+
+ val = ((guint64)a) << 32 | b;
+ *next_offset = offset;
+ *time = (time_t) val;
+ return TRUE;
+}
+
+static gboolean
+buffer_add_utf8_string (EggBuffer *buffer, const char *str)
+{
+ if (str && !g_utf8_validate (str, -1, NULL))
+ return FALSE;
+ return egg_buffer_add_string (buffer, str);
+}
+
+static gboolean
+buffer_get_utf8_string (EggBuffer *buffer, gsize offset, gsize *next_offset,
+ char **str_ret)
+{
+ gsize len;
+ char *str;
+
+ if (!egg_buffer_get_string (buffer, offset, &offset, &str,
+ (EggBufferAllocator)g_realloc))
+ return FALSE;
+ len = str ? strlen (str) : 0;
+
+ if (str != NULL) {
+ if (!g_utf8_validate (str, len, NULL)) {
+ g_free (str);
+ return FALSE;
+ }
+ }
+
+ if (next_offset != NULL) {
+ *next_offset = offset;
+ }
+ if (str_ret != NULL) {
+ *str_ret = str;
+ } else {
+ g_free (str);
+ }
+ return TRUE;
+}
+
+static gboolean
+buffer_get_raw_secret (EggBuffer *buffer, gsize offset, gsize *next_offset,
+ guchar **secret, gsize *n_secret)
+{
+ const guchar* ptr;
+ if (!egg_buffer_get_byte_array (buffer, offset, next_offset, &ptr, n_secret))
+ return FALSE;
+
+ if (ptr == NULL || *n_secret == 0) {
+ *secret = NULL;
+ *n_secret = 0;
+ return TRUE;
+ }
+
+ *secret = egg_secure_alloc (*n_secret + 1);
+ memcpy (*secret, ptr, *n_secret);
+ (*secret)[*n_secret] = 0;
+ return TRUE;
+}
+
+typedef struct _AttributesCtx {
+ EggBuffer *buffer;
+ GHashTable *attributes;
+} AttributesCtx;
+
+static void
+add_each_attribute (gpointer key, gpointer value, gpointer user_data)
+{
+ AttributesCtx *ctx = user_data;
+ guint32 number;
+ gchar *end;
+
+ buffer_add_utf8_string (ctx->buffer, key);
+
+ /*
+ * COMPATIBILITY:
+ *
+ * Our new Secrets API doesn't support integer attributes. However, to have
+ * compatibility with old keyring code reading this file, we need to set
+ * the uint32 type attribute appropriately where expected.
+ *
+ * If there's an extra compat-uint32 attribute and the name of this attribute
+ * is contained in that list, then write as a uint32.
+ */
+
+ /* Determine if it's a uint32 compatible value, and store as such if it is */
+ if (gck_secret_fields_has_word (ctx->attributes, "compat-uint32", key)) {
+ number = strtoul (value, &end, 10);
+ if (!*end) {
+ egg_buffer_add_uint32 (ctx->buffer, 1);
+ egg_buffer_add_uint32 (ctx->buffer, number);
+ return;
+ }
+ }
+
+ /* A standard string attribute */
+ egg_buffer_add_uint32 (ctx->buffer, 0);
+ buffer_add_utf8_string (ctx->buffer, value);
+}
+
+
+static gboolean
+buffer_add_attributes (EggBuffer *buffer, GHashTable *attributes)
+{
+ AttributesCtx ctx;
+ if (attributes == NULL) {
+ egg_buffer_add_uint32 (buffer, 0);
+ } else {
+ ctx.buffer = buffer;
+ ctx.attributes = attributes;
+ egg_buffer_add_uint32 (buffer, g_hash_table_size (attributes));
+ g_hash_table_foreach (attributes, add_each_attribute, &ctx);
+ }
+
+ return !egg_buffer_has_error (buffer);
+}
+
+static gboolean
+buffer_get_attributes (EggBuffer *buffer, gsize offset, gsize *next_offset,
+ GHashTable **attributes_out)
+{
+ guint32 list_size;
+ GHashTable *attributes;
+ GString *compat_uint32;
+ char *name;
+ guint32 type;
+ char *str;
+ guint32 val;
+ int i;
+
+ attributes = NULL;
+ compat_uint32 = g_string_new ("");
+
+ if (!egg_buffer_get_uint32 (buffer, offset, &offset, &list_size))
+ goto bail;
+
+ attributes = gck_secret_fields_new ();
+ for (i = 0; i < list_size; i++) {
+ if (!buffer_get_utf8_string (buffer, offset, &offset, &name))
+ goto bail;
+ if (!egg_buffer_get_uint32 (buffer, offset, &offset, &type)) {
+ g_free (name);
+ goto bail;
+ }
+ switch (type) {
+ case 0: /* A string */
+ if (!buffer_get_utf8_string (buffer, offset, &offset, &str)) {
+ g_free (name);
+ goto bail;
+ }
+ g_hash_table_replace (attributes, name, str);
+ break;
+ case 1: /* A uint32 */
+ if (!egg_buffer_get_uint32 (buffer, offset,
+ &offset, &val)) {
+ g_free (name);
+ goto bail;
+ }
+ g_string_append_c (compat_uint32, ' ');
+ g_string_append (compat_uint32, name);
+ str = g_strdup_printf ("%u", val);
+ g_hash_table_replace (attributes, name, str);
+ break;
+ default:
+ g_free (name);
+ goto bail;
+ }
+ }
+
+ if (compat_uint32->len)
+ g_hash_table_replace (attributes, "compat-uint32", g_string_free (compat_uint32, FALSE));
+ else
+ g_string_free (compat_uint32, TRUE);
+
+ *attributes_out = attributes;
+ *next_offset = offset;
+
+ return TRUE;
+
+bail:
+ g_string_free (compat_uint32, TRUE);
+ g_hash_table_unref (attributes);
+ return FALSE;
+}
+
+static gboolean
+convert_to_integer (const gchar *string, guint32 *result)
+{
+ gchar *end;
+ *result = strtoul (string, &end, 10);
+ return *end == 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * BINARY ENCRYPTED FILE FORMAT
+ */
+
+static gboolean
+encrypt_buffer (EggBuffer *buffer,
+ const char *password,
+ guchar salt[8],
+ int iterations)
+{
+ gcry_cipher_hd_t cih;
+ gcry_error_t gerr;
+ guchar *key, *iv;
+ size_t pos;
+
+ g_assert (buffer->len % 16 == 0);
+ g_assert (16 == gcry_cipher_get_algo_blklen (GCRY_CIPHER_AES128));
+ g_assert (16 == gcry_cipher_get_algo_keylen (GCRY_CIPHER_AES128));
+
+ if (!egg_symkey_generate_simple (GCRY_CIPHER_AES128, GCRY_MD_SHA256,
+ password, -1, salt, 8, iterations, &key, &iv))
+ return FALSE;
+
+ gerr = gcry_cipher_open (&cih, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0);
+ if (gerr) {
+ g_warning ("couldn't create aes cipher context: %s",
+ gcry_strerror (gerr));
+ egg_secure_free (key);
+ g_free (iv);
+ return FALSE;
+ }
+
+ /* 16 = 128 bits */
+ gerr = gcry_cipher_setkey (cih, key, 16);
+ g_return_val_if_fail (!gerr, FALSE);
+ egg_secure_free (key);
+
+ /* 16 = 128 bits */
+ gerr = gcry_cipher_setiv (cih, iv, 16);
+ g_return_val_if_fail (!gerr, FALSE);
+ g_free (iv);
+
+ for (pos = 0; pos < buffer->len; pos += 16) {
+ /* In place encryption */
+ gerr = gcry_cipher_encrypt (cih, buffer->buf + pos, 16, NULL, 0);
+ g_return_val_if_fail (!gerr, FALSE);
+ }
+
+ gcry_cipher_close (cih);
+
+ return TRUE;
+}
+
+static gboolean
+decrypt_buffer (EggBuffer *buffer,
+ const char *password,
+ guchar salt[8],
+ int iterations)
+{
+ gcry_cipher_hd_t cih;
+ gcry_error_t gerr;
+ guchar *key, *iv;
+ size_t pos;
+
+ g_assert (buffer->len % 16 == 0);
+ g_assert (16 == gcry_cipher_get_algo_blklen (GCRY_CIPHER_AES128));
+ g_assert (16 == gcry_cipher_get_algo_keylen (GCRY_CIPHER_AES128));
+
+ if (!egg_symkey_generate_simple (GCRY_CIPHER_AES128, GCRY_MD_SHA256,
+ password, -1, salt, 8, iterations, &key, &iv))
+ return FALSE;
+
+ gerr = gcry_cipher_open (&cih, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0);
+ if (gerr) {
+ g_warning ("couldn't create aes cipher context: %s",
+ gcry_strerror (gerr));
+ egg_secure_free (key);
+ g_free (iv);
+ return FALSE;
+ }
+
+ /* 16 = 128 bits */
+ gerr = gcry_cipher_setkey (cih, key, 16);
+ g_return_val_if_fail (!gerr, FALSE);
+ egg_secure_free (key);
+
+ /* 16 = 128 bits */
+ gerr = gcry_cipher_setiv (cih, iv, 16);
+ g_return_val_if_fail (!gerr, FALSE);
+ g_free (iv);
+
+ for (pos = 0; pos < buffer->len; pos += 16) {
+ /* In place encryption */
+ gerr = gcry_cipher_decrypt (cih, buffer->buf + pos, 16, NULL, 0);
+ g_return_val_if_fail (!gerr, FALSE);
+ }
+
+ gcry_cipher_close (cih);
+
+ return TRUE;
+}
+
+static gboolean
+verify_decrypted_buffer (EggBuffer *buffer)
+{
+ guchar digest[16];
+
+ /* In case the world changes on us... */
+ g_return_val_if_fail (gcry_md_get_algo_dlen (GCRY_MD_MD5) == sizeof (digest), 0);
+
+ gcry_md_hash_buffer (GCRY_MD_MD5, (void*)digest,
+ (guchar*)buffer->buf + 16, buffer->len - 16);
+
+ return memcmp (buffer->buf, digest, 16) == 0;
+}
+
+static gboolean
+generate_acl_data (EggBuffer *buffer, GList *acl)
+{
+ GList *l;
+ GckSecretAccess *ac;
+
+ egg_buffer_add_uint32 (buffer, g_list_length (acl));
+
+ for (l = acl; l != NULL; l = l->next) {
+ ac = l->data;
+
+ egg_buffer_add_uint32 (buffer, ac->types_allowed);
+ if (!buffer_add_utf8_string (buffer, ac->display_name) ||
+ !buffer_add_utf8_string (buffer, ac->pathname))
+ return FALSE;
+
+ /* Reserved: */
+ if (!buffer_add_utf8_string (buffer, NULL))
+ return FALSE;
+
+ egg_buffer_add_uint32 (buffer, 0);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+generate_encrypted_data (EggBuffer *buffer, GckSecretCollection *collection)
+{
+ GckSecretObject *obj;
+ GckSecretItem *item;
+ GList *items, *l;
+ GHashTable *attributes;
+ const gchar *label;
+ GckLogin *secret;
+ const gchar *password;
+ gsize n_password;
+ GList *acl;
+ int i;
+
+ /* Make sure we're using non-pageable memory */
+ egg_buffer_set_allocator (buffer, egg_secure_realloc);
+
+ items = gck_secret_collection_get_items (collection);
+ for (l = items; l && !egg_buffer_has_error(buffer); l = g_list_next (l)) {
+ item = GCK_SECRET_ITEM (l->data);
+ obj = GCK_SECRET_OBJECT (l->data);
+
+ label = gck_secret_object_get_label (obj);
+ buffer_add_utf8_string (buffer, label);
+
+ secret = gck_secret_item_get_secret (item);
+ password = NULL;
+ if (secret != NULL)
+ password = gck_login_get_password (secret, &n_password);
+ /* TODO: Need to support binary secrets somehow */
+ buffer_add_utf8_string (buffer, password);
+
+ if (!buffer_add_time (buffer, gck_secret_object_get_created (obj)) ||
+ !buffer_add_time (buffer, gck_secret_object_get_modified (obj)))
+ break;
+
+ /* reserved: */
+ if (!buffer_add_utf8_string (buffer, NULL))
+ break;
+ for (i = 0; i < 4; i++)
+ egg_buffer_add_uint32 (buffer, 0);
+
+ /* Null attributes = empty attribute array */
+ attributes = gck_secret_item_get_fields (item);
+ if (!buffer_add_attributes (buffer, attributes))
+ break;
+
+ acl = g_object_get_data (G_OBJECT (item), "compat-acl");
+ if (!generate_acl_data (buffer, acl))
+ break;
+ }
+
+ g_list_free (items);
+
+ /* Iteration completed prematurely == fail */
+ return (l == NULL);
+}
+
+static gboolean
+generate_hashed_attributes (GckSecretCollection *collection, EggBuffer *buffer)
+{
+ GHashTable *attributes;
+ GHashTable *hashed;
+ const gchar *value;
+ GList *items, *l;
+ guint32 id, type;
+
+ items = gck_secret_collection_get_items (collection);
+ egg_buffer_add_uint32 (buffer, g_list_length (items));
+
+ for (l = items; l; l = g_list_next (l)) {
+
+ value = gck_secret_object_get_identifier (l->data);
+ if (!convert_to_integer (value, &id))
+ continue;
+ egg_buffer_add_uint32 (buffer, id);
+
+ attributes = gck_secret_item_get_fields (l->data);
+ value = g_hash_table_lookup (attributes, "compat-item-type");
+ if (!value || !convert_to_integer (value, &type))
+ type = 0;
+ egg_buffer_add_uint32 (buffer, type);
+
+ hashed = gck_secret_fields_hash (attributes);
+ buffer_add_attributes (buffer, hashed);
+ g_hash_table_unref (hashed);
+ }
+
+ g_list_free (items);
+ return !egg_buffer_has_error (buffer);
+}
+
+GckDataResult
+gck_secret_binary_write (GckSecretCollection *collection, guchar **data, gsize *n_data)
+{
+ guint flags;
+ const gchar *password;
+ GckSecretObject *obj;
+ EggBuffer to_encrypt;
+ guchar digest[16];
+ EggBuffer buffer;
+ gint hash_iterations;
+ gint lock_timeout;
+ guchar salt[8];
+ int i;
+
+ /* In case the world changes on us... */
+ g_return_val_if_fail (gcry_md_get_algo_dlen (GCRY_MD_MD5) == sizeof (digest), GCK_DATA_FAILURE);
+ g_return_val_if_fail (gck_secret_collection_get_state (collection) == GCK_SECRET_COMPLETE, GCK_DATA_FAILURE);
+
+ egg_buffer_init_full (&buffer, 256, g_realloc);
+ obj = GCK_SECRET_OBJECT (collection);
+
+ /* Prepare the keyring for encryption */
+ hash_iterations = 1000 + (int) (1000.0 * rand() / (RAND_MAX + 1.0));
+ gcry_create_nonce (salt, sizeof (salt));
+
+ egg_buffer_append (&buffer, (guchar*)KEYRING_FILE_HEADER, KEYRING_FILE_HEADER_LEN);
+ egg_buffer_add_byte (&buffer, 0); /* Major version */
+ egg_buffer_add_byte (&buffer, 0); /* Minor version */
+ egg_buffer_add_byte (&buffer, 0); /* crypto (0 == AEL) */
+ egg_buffer_add_byte (&buffer, 0); /* hash (0 == MD5) */
+
+ buffer_add_utf8_string (&buffer, gck_secret_object_get_label (obj));
+ buffer_add_time (&buffer, gck_secret_object_get_modified (obj));
+ buffer_add_time (&buffer, gck_secret_object_get_created (obj));
+
+ flags = 0;
+ if (g_object_get_data (G_OBJECT (collection), "lock-on-idle"))
+ flags |= 1;
+ egg_buffer_add_uint32 (&buffer, flags);
+
+ lock_timeout = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (collection), "lock-on-idle"));
+ egg_buffer_add_uint32 (&buffer, lock_timeout);
+ egg_buffer_add_uint32 (&buffer, hash_iterations);
+ egg_buffer_append (&buffer, salt, 8);
+
+ /* Reserved: */
+ for (i = 0; i < 4; i++)
+ egg_buffer_add_uint32 (&buffer, 0);
+
+ /* Hashed items: */
+ generate_hashed_attributes (collection, &buffer);
+
+ /* Encrypted data. Use non-pageable memory */
+ egg_buffer_init_full (&to_encrypt, 4096, egg_secure_realloc);
+
+ egg_buffer_append (&to_encrypt, (guchar*)digest, 16); /* Space for hash */
+
+ if (!generate_encrypted_data (&to_encrypt, collection)) {
+ egg_buffer_uninit (&to_encrypt);
+ egg_buffer_uninit (&buffer);
+ return GCK_DATA_FAILURE;
+ }
+
+ /* Pad with zeros to multiple of 16 bytes */
+ while (to_encrypt.len % 16 != 0)
+ egg_buffer_add_byte (&to_encrypt, 0);
+
+ gcry_md_hash_buffer (GCRY_MD_MD5, (void*)digest,
+ (guchar*)to_encrypt.buf + 16, to_encrypt.len - 16);
+ memcpy (to_encrypt.buf, digest, 16);
+
+ password = gck_secret_collection_get_master_password (collection);
+ if (!encrypt_buffer (&to_encrypt, password, salt, hash_iterations)) {
+ egg_buffer_uninit (&buffer);
+ egg_buffer_uninit (&to_encrypt);
+ return GCK_DATA_FAILURE;
+ }
+
+ if (egg_buffer_has_error (&to_encrypt) || egg_buffer_has_error (&buffer)) {
+ egg_buffer_uninit (&buffer);
+ egg_buffer_uninit (&to_encrypt);
+ return GCK_DATA_FAILURE;
+ }
+
+ egg_buffer_add_uint32 (&buffer, to_encrypt.len);
+ egg_buffer_append (&buffer, to_encrypt.buf, to_encrypt.len);
+ egg_buffer_uninit (&to_encrypt);
+ *data = egg_buffer_uninit_steal (&buffer, n_data);
+
+ return GCK_DATA_SUCCESS;
+}
+
+static gboolean
+decode_acl (EggBuffer *buffer, gsize offset, gsize *offset_out, GList **out)
+{
+ GList *acl;
+ guint32 num_acs;
+ guint32 x, y;
+ int i;
+ GckSecretAccess *ac;
+ char *name, *path, *reserved;
+
+ acl = NULL;
+
+ if (!egg_buffer_get_uint32 (buffer, offset, &offset, &num_acs))
+ return FALSE;
+ for (i = 0; i < num_acs; i++) {
+ if (!egg_buffer_get_uint32 (buffer, offset, &offset, &x)) {
+ goto bail;
+ }
+ if (!buffer_get_utf8_string (buffer, offset, &offset, &name)) {
+ goto bail;
+ }
+ if (!buffer_get_utf8_string (buffer, offset, &offset, &path)) {
+ g_free (name);
+ goto bail;
+ }
+ reserved = NULL;
+ if (!buffer_get_utf8_string (buffer, offset, &offset, &reserved)) {
+ g_free (name);
+ g_free (path);
+ goto bail;
+ }
+ g_free (reserved);
+ if (!egg_buffer_get_uint32 (buffer, offset, &offset, &y)) {
+ g_free (name);
+ g_free (path);
+ goto bail;
+ }
+
+ ac = g_new0 (GckSecretAccess, 1);
+ ac->display_name = name;
+ ac->pathname = path;
+ ac->types_allowed = x;
+
+ acl = g_list_prepend (acl, ac);
+ }
+
+ *offset_out = offset;
+ *out = g_list_reverse (acl);
+ return TRUE;
+
+bail:
+ gck_secret_acl_free (acl);
+ return FALSE;
+}
+
+static void
+remove_unavailable_item (gpointer key, gpointer dummy, gpointer user_data)
+{
+ /* Called to remove items from a keyring that no longer exist */
+
+ GckSecretCollection *collection = user_data;
+ GckSecretItem *item;
+
+ g_assert (GCK_IS_SECRET_COLLECTION (collection));
+
+ item = gck_secret_collection_get_item (collection, key);
+ if (item != NULL)
+ gck_secret_collection_remove_item (collection, item);
+}
+
+static void
+setup_item_from_info (GckSecretItem *item, gboolean locked, ItemInfo *info)
+{
+ GckSecretObject *obj = GCK_SECRET_OBJECT (item);
+ GckLogin *secret;
+ gchar *type;
+
+ gck_secret_object_set_label (obj, info->display_name);
+ gck_secret_object_set_created (obj, info->ctime);
+ gck_secret_object_set_modified (obj, info->mtime);
+
+ type = g_strdup_printf ("%u", info->type);
+
+ if (locked) {
+ gck_secret_fields_add (info->hashed_attributes, "compat-item-type", type);
+ gck_secret_item_set_fields (item, info->hashed_attributes);
+ g_object_set_data (G_OBJECT (item), "compat-acl", NULL);
+ gck_secret_item_set_secret (item, NULL);
+
+ } else {
+ gck_secret_fields_add (info->attributes, "compat-item-type", type);
+ gck_secret_item_set_fields (item, info->attributes);
+ secret = gck_login_new_from_password (info->secret);
+ gck_secret_item_set_secret (item, secret);
+ g_object_unref (secret);
+ g_object_set_data_full (G_OBJECT (item), "compat-acl", info->acl, gck_secret_acl_free);
+ info->acl = NULL;
+ }
+
+ g_free (type);
+}
+
+static void
+free_item_info (ItemInfo *info)
+{
+ g_free (info->identifier);
+ g_hash_table_unref (info->hashed_attributes);
+ g_free (info->display_name);
+ egg_secure_free (info->secret);
+ g_hash_table_unref (info->attributes);
+ gck_secret_acl_free (info->acl);
+}
+
+gint
+gck_secret_binary_read (GckSecretCollection *collection, const guchar *data, gsize n_data)
+{
+ gsize offset;
+ guchar major, minor, crypto, hash;
+ guint32 flags;
+ guint32 lock_timeout;
+ time_t mtime, ctime;
+ char *display_name;
+ gsize n_secret;
+ int i, j;
+ guint32 tmp;
+ guint32 num_items;
+ guint32 crypto_size;
+ guint32 hash_iterations;
+ guchar salt[8];
+ ItemInfo *items;
+ const gchar *password;
+ GckSecretObject *obj;
+ EggBuffer to_decrypt = EGG_BUFFER_EMPTY;
+ GckDataResult res = GCK_DATA_FAILURE;
+ GHashTable *checks = NULL;
+ GckSecretItem *item;
+ EggBuffer buffer;
+ char *reserved;
+ gchar *identifier;
+ GList *l, *iteml;
+
+ display_name = NULL;
+ items = 0;
+ obj = GCK_SECRET_OBJECT (collection);
+
+ /* The buffer we read from */
+ egg_buffer_init_static (&buffer, data, n_data);
+
+ if (buffer.len < KEYRING_FILE_HEADER_LEN ||
+ memcmp (buffer.buf, KEYRING_FILE_HEADER, KEYRING_FILE_HEADER_LEN) != 0) {
+ egg_buffer_uninit (&buffer);
+ return GCK_DATA_UNRECOGNIZED;
+ }
+
+ offset = KEYRING_FILE_HEADER_LEN;
+ major = buffer.buf[offset++];
+ minor = buffer.buf[offset++];
+ crypto = buffer.buf[offset++];
+ hash = buffer.buf[offset++];
+
+ if (major != 0 || minor != 0 || crypto != 0 || hash != 0) {
+ egg_buffer_uninit (&buffer);
+ return GCK_DATA_UNRECOGNIZED;
+ }
+
+ /* We're decrypting this, so use secure memory */
+ egg_buffer_set_allocator (&to_decrypt, egg_secure_realloc);
+
+ if (!buffer_get_utf8_string (&buffer, offset, &offset, &display_name) ||
+ !buffer_get_time (&buffer, offset, &offset, &ctime) ||
+ !buffer_get_time (&buffer, offset, &offset, &mtime) ||
+ !egg_buffer_get_uint32 (&buffer, offset, &offset, &flags) ||
+ !egg_buffer_get_uint32 (&buffer, offset, &offset, &lock_timeout) ||
+ !egg_buffer_get_uint32 (&buffer, offset, &offset, &hash_iterations) ||
+ !buffer_get_bytes (&buffer, offset, &offset, salt, 8))
+ goto bail;
+
+ for (i = 0; i < 4; i++) {
+ if (!egg_buffer_get_uint32 (&buffer, offset, &offset, &tmp))
+ goto bail;
+ }
+
+ if (!egg_buffer_get_uint32 (&buffer, offset, &offset, &num_items))
+ goto bail;
+
+ items = g_new0 (ItemInfo, num_items);
+
+ for (i = 0; i < num_items; i++) {
+ if (!egg_buffer_get_uint32 (&buffer, offset, &offset, &items[i].id) ||
+ !egg_buffer_get_uint32 (&buffer, offset, &offset, &items[i].type) ||
+ !buffer_get_attributes (&buffer, offset, &offset, &items[i].hashed_attributes))
+ goto bail;
+ identifier = g_strdup_printf ("%u", items[i].id);
+ }
+
+ if (!egg_buffer_get_uint32 (&buffer, offset, &offset, &crypto_size))
+ goto bail;
+
+ /* Make the crypted part is the right size */
+ if (crypto_size % 16 != 0)
+ goto bail;
+
+ /* Copy the data into to_decrypt into non-pageable memory */
+ egg_buffer_init_static (&to_decrypt, buffer.buf + offset, crypto_size);
+
+ password = gck_secret_collection_get_master_password (collection);
+ if (password != NULL) {
+
+ if (!decrypt_buffer (&to_decrypt, password, salt, hash_iterations))
+ goto bail;
+ if (!verify_decrypted_buffer (&to_decrypt)) {
+ res = GCK_DATA_LOCKED;
+ goto bail;
+ } else {
+ offset += 16; /* Skip hash */
+ for (i = 0; i < num_items; i++) {
+ if (!buffer_get_utf8_string (&buffer, offset, &offset,
+ &items[i].display_name)) {
+ goto bail;
+ }
+ if (!buffer_get_raw_secret (&buffer, offset, &offset,
+ (guchar**)(&items[i].secret), &n_secret)) {
+ goto bail;
+ }
+ /* We don't support binary secrets yet, skip */
+ if (!g_utf8_validate ((gchar*)items[i].secret, n_secret, NULL)) {
+ g_message ("discarding item with unsupported non-textual secret: %s",
+ items[i].display_name);
+ free (items[i].display_name);
+ free (items[i].secret);
+ continue;
+ }
+ if (!buffer_get_time (&buffer, offset, &offset, &items[i].ctime) ||
+ !buffer_get_time (&buffer, offset, &offset, &items[i].mtime))
+ goto bail;
+ reserved = NULL;
+ if (!buffer_get_utf8_string (&buffer, offset, &offset, &reserved))
+ goto bail;
+ g_free (reserved);
+ for (j = 0; j < 4; j++) {
+ guint32 tmp;
+ if (!egg_buffer_get_uint32 (&buffer, offset, &offset, &tmp))
+ goto bail;
+ }
+ if (!buffer_get_attributes (&buffer, offset, &offset, &items[i].attributes))
+ goto bail;
+ if (!decode_acl (&buffer, offset, &offset, &items[i].acl))
+ goto bail;
+ }
+ }
+ }
+
+ /* Correctly read all data, possibly including the decrypted data.
+ * Now update the keyring and items: */
+
+ gck_secret_object_set_label (obj, display_name);
+ gck_secret_object_set_modified (obj, mtime);
+ gck_secret_object_set_created (obj, ctime);
+ g_object_set_data (G_OBJECT (collection), "lock-on-idle", GINT_TO_POINTER (!!(flags & LOCK_ON_IDLE_FLAG)));
+ g_object_set_data (G_OBJECT (collection), "lock-timeout", GINT_TO_POINTER (lock_timeout));
+
+ /* Build a Hash table where we can track ids we haven't yet seen */
+ checks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ iteml = gck_secret_collection_get_items (collection);
+ for (l = iteml; l; l = g_list_next (l))
+ g_hash_table_insert (checks, g_strdup (gck_secret_object_get_identifier (l->data)), "unused");
+ g_list_free (iteml);
+
+ for (i = 0; i < num_items; i++) {
+
+ /* We've seen this id */
+ g_hash_table_remove (checks, items[i].identifier);
+
+ item = gck_secret_collection_get_item (collection, items[i].identifier);
+ if (item == NULL)
+ item = gck_secret_collection_create_item (collection, items[i].identifier);
+
+ setup_item_from_info (item, password == NULL, &items[i]);
+ }
+
+ g_hash_table_foreach (checks, remove_unavailable_item, collection);
+ res = GCK_DATA_SUCCESS;
+
+bail:
+ egg_buffer_uninit (&to_decrypt);
+ if (checks)
+ g_hash_table_destroy (checks);
+ g_free (display_name);
+
+ for (i = 0; items && i < num_items; i++)
+ free_item_info (&items[i]);
+ g_free (items);
+
+ return res;
+}
diff --git a/pkcs11/secret-store/gck-secret-binary.h b/pkcs11/secret-store/gck-secret-binary.h
new file mode 100644
index 0000000..f87c1d7
--- /dev/null
+++ b/pkcs11/secret-store/gck-secret-binary.h
@@ -0,0 +1,37 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.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 Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 __GCK_SECRET_BINARY_H__
+#define __GCK_SECRET_BINARY_H__
+
+#include "gck-secret-collection.h"
+
+#include "gck/gck-data-types.h"
+
+GckDataResult gck_secret_binary_read (GckSecretCollection *collection,
+ const guchar *data,
+ gsize n_data);
+
+GckDataResult gck_secret_binary_write (GckSecretCollection *collection,
+ guchar **data,
+ gsize *n_data);
+
+#endif /* __GCK_SECRET_BINARY_H__ */
diff --git a/pkcs11/secret-store/gck-secret-collection.c b/pkcs11/secret-store/gck-secret-collection.c
index d4bf163..5c41849 100644
--- a/pkcs11/secret-store/gck-secret-collection.c
+++ b/pkcs11/secret-store/gck-secret-collection.c
@@ -21,7 +21,13 @@
#include "config.h"
+#include "gck-secret-binary.h"
#include "gck-secret-collection.h"
+#include "gck-secret-textual.h"
+
+#include "egg/egg-buffer.h"
+
+#include "gck/gck-serializable.h"
#include <glib/gi18n.h>
@@ -32,17 +38,23 @@ enum {
struct _GckSecretCollection {
GckSecretObject parent;
GHashTable *secrets;
+ GList *items;
+ gchar *password;
};
-G_DEFINE_TYPE (GckSecretCollection, gck_secret_collection, GCK_TYPE_SECRET_OBJECT);
+static void gck_secret_collection_serializable (GckSerializableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GckSecretCollection, gck_secret_collection, GCK_TYPE_SECRET_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (GCK_TYPE_SERIALIZABLE, gck_secret_collection_serializable));
/* -----------------------------------------------------------------------------
- * INTERNAL
+ * INTERNAL
*/
+
/* -----------------------------------------------------------------------------
- * OBJECT
+ * OBJECT
*/
static CK_RV
@@ -139,6 +151,52 @@ gck_secret_collection_class_init (GckSecretCollectionClass *klass)
gck_class->get_attribute = gck_secret_collection_get_attribute;
}
+static gboolean
+gck_secret_collection_real_load (GckSerializable *base, GckLogin *login, const guchar *data, gsize n_data)
+{
+ GckSecretCollection *self = GCK_SECRET_COLLECTION (base);
+ GckDataResult res;
+
+ g_return_val_if_fail (GCK_IS_SECRET_COLLECTION (self), FALSE);
+ g_return_val_if_fail (data, FALSE);
+ g_return_val_if_fail (n_data, FALSE);
+
+ res = gck_secret_binary_read (self, data, n_data);
+ if (res == GCK_DATA_UNRECOGNIZED)
+ res = gck_secret_textual_read (self, data, n_data);
+
+ /* TODO: This doesn't transfer knowledge of 'wrong password' back up */
+ return (res == GCK_DATA_SUCCESS);
+}
+
+static gboolean
+gck_secret_collection_real_save (GckSerializable *base, GckLogin *login, guchar **data, gsize *n_data)
+{
+ GckSecretCollection *self = GCK_SECRET_COLLECTION (base);
+ GckDataResult res;
+
+ g_return_val_if_fail (GCK_IS_SECRET_COLLECTION (self), FALSE);
+ g_return_val_if_fail (data, FALSE);
+ g_return_val_if_fail (n_data, FALSE);
+
+ if (self->password == NULL)
+ res = gck_secret_textual_write (self, data, n_data);
+ else
+ res = gck_secret_binary_write (self, data, n_data);
+
+ /* TODO: This doesn't transfer knowledge of 'no password' back up */
+ return (res == GCK_DATA_SUCCESS);
+}
+
+static void
+gck_secret_collection_serializable (GckSerializableIface *iface)
+{
+ iface->extension = ".keyring";
+ iface->load = gck_secret_collection_real_load;
+ iface->save = gck_secret_collection_real_save;
+}
+
+
/* -----------------------------------------------------------------------------
* PUBLIC
*/
diff --git a/pkcs11/secret-store/gck-secret-collection.h b/pkcs11/secret-store/gck-secret-collection.h
index 02492d7..b2616d2 100644
--- a/pkcs11/secret-store/gck-secret-collection.h
+++ b/pkcs11/secret-store/gck-secret-collection.h
@@ -33,16 +33,36 @@
#define GCK_IS_SECRET_COLLECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_SECRET_COLLECTION))
#define GCK_SECRET_COLLECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_SECRET_COLLECTION, GckSecretCollectionClass))
-typedef struct _GckSecretCollection GckSecretCollection;
typedef struct _GckSecretCollectionClass GckSecretCollectionClass;
-
+
struct _GckSecretCollectionClass {
GckSecretObjectClass parent_class;
};
+typedef enum _GckSecretState {
+ GCK_SECRET_EMPTY = 0,
+ GCK_SECRET_PARTIAL = 1,
+ GCK_SECRET_COMPLETE = 2
+} GckSecretState;
+
GType gck_secret_collection_get_type (void);
+GckSecretState gck_secret_collection_get_state (GckSecretCollection *self);
+
+GList* gck_secret_collection_get_items (GckSecretCollection *self);
+
+GckSecretItem* gck_secret_collection_get_item (GckSecretCollection *self,
+ const gchar *identifier);
+
+GckSecretItem* gck_secret_collection_create_item (GckSecretCollection *self,
+ const gchar *identifier);
+
+void gck_secret_collection_remove_item (GckSecretCollection *self,
+ GckSecretItem *item);
+
GckLogin* gck_secret_collection_lookup_secret (GckSecretCollection *self,
const gchar *identifier);
+const gchar * gck_secret_collection_get_master_password (GckSecretCollection *self);
+
#endif /* __GCK_SECRET_COLLECTION_H__ */
diff --git a/pkcs11/secret-store/gck-secret-compat.h b/pkcs11/secret-store/gck-secret-compat.h
new file mode 100644
index 0000000..302e21c
--- /dev/null
+++ b/pkcs11/secret-store/gck-secret-compat.h
@@ -0,0 +1,41 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.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 Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 __GCK_SECRET_COMPAT_H__
+#define __GCK_SECRET_COMPAT_H__
+
+#include <glib.h>
+
+typedef enum {
+ GCK_SECRET_ACCESS_READ = 1 << 0,
+ GCK_SECRET_ACCESS_WRITE = 1 << 1,
+ GCK_SECRET_ACCESS_REMOVE = 1 << 2
+} GckSecretAccessType;
+
+typedef struct _GckSecretAccess {
+ char *display_name;
+ char *pathname;
+ GckSecretAccessType types_allowed;
+} GckSecretAccess;
+
+void gck_secret_acl_free (gpointer acl);
+
+#endif /* __GCK_SECRET_COMPAT_H__ */
diff --git a/pkcs11/secret-store/gck-secret-fields.c b/pkcs11/secret-store/gck-secret-fields.c
index ee8d541..dfc6068 100644
--- a/pkcs11/secret-store/gck-secret-fields.c
+++ b/pkcs11/secret-store/gck-secret-fields.c
@@ -25,6 +25,7 @@
#include "gck/gck-attributes.h"
+#include <ctype.h>
#include <string.h>
GType
@@ -168,3 +169,33 @@ gck_secret_fields_match (GHashTable *haystack, GHashTable *needle)
return TRUE;
}
+
+gboolean
+gck_secret_fields_has_word (GHashTable *fields, const gchar *name, const gchar *word)
+{
+ const gchar *string;
+ const gchar *at;
+ gsize len = strlen (word);
+
+ if (len == 0)
+ return FALSE;
+
+ string = g_hash_table_lookup (fields, name);
+ if (!string)
+ return FALSE;
+
+ for (;;) {
+ at = strstr (string, word);
+ if (at == NULL)
+ return FALSE;
+
+ /* The word exists, is at beginning or end, or spaces around it */
+ if ((at == string || isspace (*(at - 1))) &&
+ (*(at + len) == 0 || isspace (*(at + len))))
+ return TRUE;
+
+ string = at + len;
+ }
+
+ g_assert_not_reached ();
+}
diff --git a/pkcs11/secret-store/gck-secret-fields.h b/pkcs11/secret-store/gck-secret-fields.h
index 060f2d9..fe4d593 100644
--- a/pkcs11/secret-store/gck-secret-fields.h
+++ b/pkcs11/secret-store/gck-secret-fields.h
@@ -33,6 +33,10 @@ GType gck_secret_fields_boxed_type (void);
GHashTable* gck_secret_fields_new (void);
+void gck_secret_fields_add (GHashTable *fields,
+ const gchar *name,
+ const gchar *value);
+
CK_RV gck_secret_fields_parse (CK_ATTRIBUTE_PTR attr,
GHashTable **fields);
@@ -41,5 +45,11 @@ CK_RV gck_secret_fields_serialize (CK_ATTRIBUTE_PTR attr,
gboolean gck_secret_fields_match (GHashTable *haystack,
GHashTable *needle);
-
+
+GHashTable* gck_secret_fields_hash (GHashTable *fields);
+
+gboolean gck_secret_fields_has_word (GHashTable *fields,
+ const gchar *name,
+ const gchar *word);
+
#endif /* __GCK_SECRET_FIELDS_H__ */
diff --git a/pkcs11/secret-store/gck-secret-item.h b/pkcs11/secret-store/gck-secret-item.h
index 707e6cb..1366e0c 100644
--- a/pkcs11/secret-store/gck-secret-item.h
+++ b/pkcs11/secret-store/gck-secret-item.h
@@ -34,9 +34,8 @@
#define GCK_IS_SECRET_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_SECRET_ITEM))
#define GCK_SECRET_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_SECRET_ITEM, GckSecretItemClass))
-typedef struct _GckSecretItem GckSecretItem;
typedef struct _GckSecretItemClass GckSecretItemClass;
-
+
struct _GckSecretItemClass {
GckSecretObjectClass parent_class;
};
diff --git a/pkcs11/secret-store/gck-secret-module.c b/pkcs11/secret-store/gck-secret-module.c
new file mode 100644
index 0000000..0fc5d6f
--- /dev/null
+++ b/pkcs11/secret-store/gck-secret-module.c
@@ -0,0 +1,356 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.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 Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 "gck-secret-collection.h"
+#include "gck-secret-module.h"
+#include "gck-secret-store.h"
+
+#include "gck/gck-file-tracker.h"
+#include "gck/gck-serializable.h"
+
+#include <string.h>
+
+struct _GckSecretModule {
+ GckModule parent;
+ GckFileTracker *tracker;
+ GHashTable *collections;
+ gchar *directory;
+};
+
+static const CK_SLOT_INFO gck_secret_module_slot_info = {
+ "Secret Store",
+ "Gnome Keyring",
+ CKF_TOKEN_PRESENT,
+ { 0, 0 },
+ { 0, 0 }
+};
+
+static const CK_TOKEN_INFO gck_secret_module_token_info = {
+ "Secret Store",
+ "Gnome Keyring",
+ "1.0",
+ "1:SECRET:DEFAULT", /* Unique serial number for manufacturer */
+ CKF_TOKEN_INITIALIZED | CKF_WRITE_PROTECTED,
+ CK_EFFECTIVELY_INFINITE,
+ CK_EFFECTIVELY_INFINITE,
+ CK_EFFECTIVELY_INFINITE,
+ CK_EFFECTIVELY_INFINITE,
+ 1024,
+ 1,
+ CK_UNAVAILABLE_INFORMATION,
+ CK_UNAVAILABLE_INFORMATION,
+ CK_UNAVAILABLE_INFORMATION,
+ CK_UNAVAILABLE_INFORMATION,
+ { 0, 0 },
+ { 0, 0 },
+ ""
+};
+
+G_DEFINE_TYPE (GckSecretModule, gck_secret_module, GCK_TYPE_MODULE);
+
+/* -----------------------------------------------------------------------------
+ * ACTUAL PKCS#11 Module Implementation
+ */
+
+/* Include all the module entry points */
+#include "gck/gck-module-ep.h"
+GCK_DEFINE_MODULE (gck_secret_module, GCK_TYPE_SECRET_MODULE);
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static GckCertificate*
+add_certificate_for_data (GckSecretModule *self, const guchar *data,
+ gsize n_data, const gchar *path)
+{
+ GckCertificate *cert;
+ GckManager *manager;
+ gchar *hash, *unique;
+
+ g_assert (GCK_IS_SECRET_MODULE (self));
+ g_assert (data);
+ g_assert (path);
+
+ manager = gck_module_get_manager (GCK_MODULE (self));
+ g_return_val_if_fail (manager, NULL);
+
+ /* Hash the certificate */
+ hash = g_compute_checksum_for_data (G_CHECKSUM_MD5, data, n_data);
+ unique = g_strdup_printf ("%s:%s", path, hash);
+ g_free (hash);
+
+ /* Try and find a certificate */
+ cert = GCK_CERTIFICATE (gck_manager_find_one_by_string_property (manager, "unique", unique));
+ if (cert != NULL) {
+ g_free (unique);
+ return cert;
+ }
+
+ /* Create a new certificate object */
+ cert = GCK_CERTIFICATE (gck_secret_certificate_new (GCK_MODULE (self), unique, path));
+
+ if (!gck_serializable_load (GCK_SERIALIZABLE (cert), NULL, data, n_data)) {
+ g_message ("couldn't parse certificate(s): %s", path);
+ g_object_unref (cert);
+ return NULL;
+ }
+
+ /* Setup the right manager on the certificates */
+ gck_manager_register_object (manager, GCK_OBJECT (cert));
+ gck_manager_register_object (manager, GCK_OBJECT (gck_secret_certificate_get_netscape_trust (GCK_SECRET_CERTIFICATE (cert))));
+
+ /* And add to our wonderful table */
+ g_hash_table_insert (self->certificates, cert, cert);
+ return cert;
+}
+
+static void
+parsed_pem_block (GQuark type, const guchar *data, gsize n_data,
+ GHashTable *headers, gpointer user_data)
+{
+ static GQuark PEM_CERTIFICATE;
+ static volatile gsize quarks_inited = 0;
+
+ ParsePrivate *ctx = (ParsePrivate*)user_data;
+ GckCertificate *cert;
+
+ g_assert (ctx);
+
+ /* Initialize the first time through */
+ if (g_once_init_enter (&quarks_inited)) {
+ PEM_CERTIFICATE = g_quark_from_static_string ("CERTIFICATE");
+ g_once_init_leave (&quarks_inited, 1);
+ }
+
+ if (type == PEM_CERTIFICATE) {
+ cert = add_certificate_for_data (ctx->module, data, n_data, ctx->path);
+ if (cert != NULL) {
+ g_hash_table_remove (ctx->checks, cert);
+ ++ctx->count;
+ }
+ }
+}
+
+static void
+remove_each_certificate (gpointer key, gpointer value, gpointer user_data)
+{
+ GckSecretModule *self = user_data;
+ g_assert (GCK_IS_SECRET_MODULE (self));
+ if (!g_hash_table_remove (self->certificates, value))
+ g_return_if_reached ();
+}
+
+static void
+file_load (GckFileTracker *tracker, const gchar *path, GckSecretModule *self)
+{
+ ParsePrivate ctx;
+ GckManager *manager;
+ GckCertificate *cert;
+ guchar *data;
+ GList *objects, *l;
+ GError *error = NULL;
+ gsize n_data;
+ guint num;
+
+ manager = gck_module_get_manager (GCK_MODULE (self));
+ g_return_if_fail (manager);
+
+ /* Read in the public key */
+ if (!g_file_get_contents (path, (gchar**)&data, &n_data, &error)) {
+ g_warning ("couldn't load root certificates: %s: %s",
+ path, error && error->message ? error->message : "");
+ return;
+ }
+
+ memset (&ctx, 0, sizeof (ctx));
+ ctx.path = path;
+ ctx.module = self;
+ ctx.count = 0;
+
+ /* Checks for what was at this path */
+ ctx.checks = g_hash_table_new (g_direct_hash, g_direct_equal);
+ objects = gck_manager_find_by_string_property (manager, "path", path);
+ for (l = objects; l; l = g_list_next (l))
+ g_hash_table_insert (ctx.checks, l->data, l->data);
+ g_list_free (objects);
+
+ /* Try and parse the PEM */
+ num = egg_openssl_pem_parse (data, n_data, parsed_pem_block, &ctx);
+
+ /* If no PEM data, try to parse directly as DER */
+ if (ctx.count == 0) {
+ cert = add_certificate_for_data (self, data, n_data, path);
+ if (cert != NULL)
+ g_hash_table_remove (ctx.checks, cert);
+ }
+
+ g_hash_table_foreach (ctx.checks, remove_each_certificate, self);
+ g_hash_table_destroy (ctx.checks);
+
+ g_free (data);
+}
+
+static void
+file_remove (GckFileTracker *tracker, const gchar *path, GckSecretModule *self)
+{
+ GList *objects, *l;
+ GckManager *manager;
+
+ g_return_if_fail (path);
+ g_return_if_fail (GCK_IS_SECRET_MODULE (self));
+
+ manager = gck_module_get_manager (GCK_MODULE (self));
+ g_return_if_fail (manager);
+
+ objects = gck_manager_find_by_string_property (manager, "path", path);
+ for (l = objects; l; l = g_list_next (l))
+ if (!g_hash_table_remove (self->certificates, l->data))
+ g_return_if_reached ();
+ g_list_free (objects);
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static const CK_SLOT_INFO*
+gck_secret_module_real_get_slot_info (GckModule *self)
+{
+ return &gck_secret_module_slot_info;
+}
+
+static const CK_TOKEN_INFO*
+gck_secret_module_real_get_token_info (GckModule *self)
+{
+ return &gck_secret_module_token_info;
+}
+
+static void
+gck_secret_module_real_parse_argument (GckModule *base, const gchar *name, const gchar *value)
+{
+ GckSecretModule *self = GCK_SECRET_MODULE (base);
+ if (g_str_equal (name, "directory")) {
+ g_free (self->directory);
+ self->directory = g_strdup (value);
+ }
+}
+
+static CK_RV
+gck_secret_module_real_refresh_token (GckModule *base)
+{
+ GckSecretModule *self = GCK_SECRET_MODULE (base);
+ if (self->tracker)
+ gck_file_tracker_refresh (self->tracker, FALSE);
+ return CKR_OK;
+}
+
+static GObject*
+gck_secret_module_constructor (GType type, guint n_props, GObjectConstructParam *props)
+{
+ GckSecretModule *self = GCK_SECRET_MODULE (G_OBJECT_CLASS (gck_secret_module_parent_class)->constructor(type, n_props, props));
+ GckManager *manager;
+
+ g_return_val_if_fail (self, NULL);
+
+#ifdef ROOT_CERTIFICATES
+ if (!self->directory)
+ self->directory = g_strdup (ROOT_CERTIFICATES);
+#endif
+ if (self->directory) {
+ self->tracker = gck_file_tracker_new (self->directory, "*", "*.0");
+ g_signal_connect (self->tracker, "file-added", G_CALLBACK (file_load), self);
+ g_signal_connect (self->tracker, "file-changed", G_CALLBACK (file_load), self);
+ g_signal_connect (self->tracker, "file-removed", G_CALLBACK (file_remove), self);
+ }
+
+ manager = gck_module_get_manager (GCK_MODULE (self));
+ gck_manager_add_property_index (manager, "unique", TRUE);
+ gck_manager_add_property_index (manager, "path", FALSE);
+
+ return G_OBJECT (self);
+}
+
+static void
+gck_secret_module_init (GckSecretModule *self)
+{
+ self->certificates = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
+
+}
+
+static void
+gck_secret_module_dispose (GObject *obj)
+{
+ GckSecretModule *self = GCK_SECRET_MODULE (obj);
+
+ if (self->tracker)
+ g_object_unref (self->tracker);
+ self->tracker = NULL;
+
+ g_hash_table_remove_all (self->certificates);
+
+ G_OBJECT_CLASS (gck_secret_module_parent_class)->dispose (obj);
+}
+
+static void
+gck_secret_module_finalize (GObject *obj)
+{
+ GckSecretModule *self = GCK_SECRET_MODULE (obj);
+
+ g_assert (self->tracker == NULL);
+
+ g_hash_table_destroy (self->certificates);
+ self->certificates = NULL;
+
+ g_free (self->directory);
+ self->directory = NULL;
+
+ G_OBJECT_CLASS (gck_secret_module_parent_class)->finalize (obj);
+}
+
+static void
+gck_secret_module_class_init (GckSecretModuleClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GckModuleClass *module_class = GCK_MODULE_CLASS (klass);
+
+ gobject_class->constructor = gck_secret_module_constructor;
+ gobject_class->dispose = gck_secret_module_dispose;
+ gobject_class->finalize = gck_secret_module_finalize;
+
+ module_class->get_slot_info = gck_secret_module_real_get_slot_info;
+ module_class->get_token_info = gck_secret_module_real_get_token_info;
+ module_class->parse_argument = gck_secret_module_real_parse_argument;
+ module_class->refresh_token = gck_secret_module_real_refresh_token;
+}
+
+/* ---------------------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+CK_FUNCTION_LIST_PTR
+gck_secret_store_get_functions (void)
+{
+ gck_crypto_initialize ();
+ return gck_secret_module_function_list;
+}
diff --git a/pkcs11/secret-store/gck-secret-module.h b/pkcs11/secret-store/gck-secret-module.h
new file mode 100644
index 0000000..dc96da2
--- /dev/null
+++ b/pkcs11/secret-store/gck-secret-module.h
@@ -0,0 +1,44 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.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 Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 __GCK_SECRET_MODULE_H__
+#define __GCK_SECRET_MODULE_H__
+
+#include <glib-object.h>
+
+#include "gck/gck-module.h"
+
+#define GCK_TYPE_SECRET_MODULE (gck_secret_module_get_type ())
+#define GCK_SECRET_MODULE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_SECRET_MODULE, GckSecretModule))
+#define GCK_SECRET_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_SECRET_MODULE, GckSecretModuleClass))
+#define GCK_IS_SECRET_MODULE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_SECRET_MODULE))
+#define GCK_IS_SECRET_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_SECRET_MODULE))
+#define GCK_SECRET_MODULE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_SECRET_MODULE, GckSecretModuleClass))
+
+typedef struct _GckSecretModuleClass GckSecretModuleClass;
+
+struct _GckSecretModuleClass {
+ GckModuleClass parent_class;
+};
+
+GType gck_secret_module_get_type (void);
+
+#endif /* __GCK_SECRET_MODULE_H__ */
diff --git a/pkcs11/secret-store/gck-secret-object.h b/pkcs11/secret-store/gck-secret-object.h
index 9ec9a8f..f8c2262 100644
--- a/pkcs11/secret-store/gck-secret-object.h
+++ b/pkcs11/secret-store/gck-secret-object.h
@@ -22,10 +22,12 @@
#ifndef __GCK_SECRET_OBJECT_H__
#define __GCK_SECRET_OBJECT_H__
-#include <glib-object.h>
+#include "gck-secret-types.h"
#include "gck/gck-object.h"
+#include <glib-object.h>
+
#define GCK_TYPE_SECRET_OBJECT (gck_secret_object_get_type ())
#define GCK_SECRET_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_SECRET_OBJECT, GckSecretObject))
#define GCK_SECRET_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_SECRET_OBJECT, GckSecretObjectClass))
@@ -33,7 +35,6 @@
#define GCK_IS_SECRET_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_SECRET_OBJECT))
#define GCK_SECRET_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_SECRET_OBJECT, GckSecretObjectClass))
-typedef struct _GckSecretObject GckSecretObject;
typedef struct _GckSecretObjectClass GckSecretObjectClass;
typedef struct _GckSecretObjectPrivate GckSecretObjectPrivate;
@@ -60,8 +61,14 @@ void gck_secret_object_set_label (GckSecretObject *self,
glong gck_secret_object_get_created (GckSecretObject *self);
+void gck_secret_object_set_created (GckSecretObject *self,
+ glong value);
+
glong gck_secret_object_get_modified (GckSecretObject *self);
+void gck_secret_object_set_modified (GckSecretObject *self,
+ glong value);
+
void gck_secret_object_was_modified (GckSecretObject *self);
gboolean gck_secret_object_is_locked (GckSecretObject *self,
diff --git a/pkcs11/secret-store/gck-secret-search.h b/pkcs11/secret-store/gck-secret-search.h
index 450a808..b2d6a96 100644
--- a/pkcs11/secret-store/gck-secret-search.h
+++ b/pkcs11/secret-store/gck-secret-search.h
@@ -22,10 +22,12 @@
#ifndef __GCK_SECRET_SEARCH_H__
#define __GCK_SECRET_SEARCH_H__
-#include <glib-object.h>
+#include "gck-secret-types.h"
#include "gck/gck-object.h"
+#include <glib-object.h>
+
#define GCK_TYPE_SECRET_SEARCH (gck_secret_search_get_type ())
#define GCK_SECRET_SEARCH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_SECRET_SEARCH, GckSecretSearch))
#define GCK_SECRET_SEARCH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_SECRET_SEARCH, GckSecretSearchClass))
@@ -33,9 +35,8 @@
#define GCK_IS_SECRET_SEARCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_SECRET_SEARCH))
#define GCK_SECRET_SEARCH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_SECRET_SEARCH, GckSecretSearchClass))
-typedef struct _GckSecretSearch GckSecretSearch;
typedef struct _GckSecretSearchClass GckSecretSearchClass;
-
+
struct _GckSecretSearchClass {
GckObjectClass parent_class;
};
diff --git a/pkcs11/secret-store/gck-secret-store.h b/pkcs11/secret-store/gck-secret-store.h
new file mode 100644
index 0000000..461528c
--- /dev/null
+++ b/pkcs11/secret-store/gck-secret-store.h
@@ -0,0 +1,29 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.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 Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 __GCK_SECRET_STORE_H__
+#define __GCK_SECRET_STORE_H__
+
+#include "pkcs11/pkcs11.h"
+
+CK_FUNCTION_LIST_PTR gck_secret_store_get_functions (void);
+
+#endif /* __GCK_SECRET_STORE_H__ */
diff --git a/pkcs11/secret-store/gck-secret-textual.c b/pkcs11/secret-store/gck-secret-textual.c
new file mode 100644
index 0000000..2599b7e
--- /dev/null
+++ b/pkcs11/secret-store/gck-secret-textual.c
@@ -0,0 +1,545 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-secret-textual.c - Textual non-encrypted format for the keyring
+
+ Copyright (C) 2007, 2009 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 "gck-secret-collection.h"
+#include "gck-secret-compat.h"
+#include "gck-secret-fields.h"
+#include "gck-secret-item.h"
+#include "gck-secret-textual.h"
+
+#include "egg/egg-secure-memory.h"
+
+#include "gck/gck-login.h"
+
+#include <glib.h>
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void
+key_file_set_uint64 (GKeyFile *file, const gchar *group,
+ const gchar *key, guint64 value)
+{
+ gchar buffer[64];
+ g_snprintf (buffer, sizeof (buffer), "%llu",
+ (long long unsigned int)value);
+ g_key_file_set_value (file, group, key, buffer);
+}
+
+static gboolean
+key_file_get_uint64 (GKeyFile *file, const gchar *group,
+ const gchar *key, guint64 *value)
+{
+ gchar *str, *end;
+
+ str = g_key_file_get_value (file, group, key, NULL);
+ if (!str)
+ return FALSE;
+
+ *value = g_ascii_strtoull (str, &end, 10);
+ if (end[0]) {
+ g_free (str);
+ return FALSE;
+ }
+
+ g_free (str);
+ return TRUE;
+}
+
+typedef struct _AttributesCtx {
+ GckSecretItem *item;
+ gint index;
+ GKeyFile *file;
+ const gchar *compat_uint32;
+} AttributesCtx;
+
+static gboolean
+attribute_name_in_space_string (const gchar *string, const gchar *name)
+{
+ const gchar *at;
+ gsize len = strlen (name);
+
+ if (len == 0)
+ return FALSE;
+
+ for (;;) {
+ at = strstr (string, name);
+ if (at == NULL)
+ return FALSE;
+
+ /* The word exists, is at beginning or end, or spaces around it */
+ if ((at == string || isspace (*(at - 1))) &&
+ (*(at + len) == 0 || isspace (*(at + len))))
+ return TRUE;
+
+ string = at + len;
+ }
+
+ g_assert_not_reached ();
+}
+
+static void
+generate_each_attribute (gpointer key, gpointer value, gpointer user_data)
+{
+ AttributesCtx *ctx = user_data;
+ const gchar *name = key;
+ const gchar *string = value;
+ gchar *groupname;
+
+ groupname = g_strdup_printf ("%s:attribute%d",
+ gck_secret_object_get_identifier (GCK_SECRET_OBJECT (ctx->item)),
+ ctx->index);
+
+ g_key_file_set_string (ctx->file, groupname, "name", name);
+
+ /*
+ * COMPATIBILITY:
+ *
+ * Our new Secrets API doesn't support integer attributes. However, to have
+ * compatibility with old keyring code reading this file, we need to set
+ * the type=uint32 attribute appropriately where expected.
+ *
+ * If there's an extra compat-uint32 attribute and the name of this attribute
+ * is contained in that list, then write as a uint32.
+ */
+
+ /* Determine if it's a uint32 compatible value, and store as such if it is */
+ if (attribute_name_in_space_string (ctx->compat_uint32, name))
+ g_key_file_set_string (ctx->file, groupname, "type", "uint32");
+ else
+ g_key_file_set_string (ctx->file, groupname, "type", "string");
+
+ g_key_file_set_string (ctx->file, groupname, "value", string);
+
+ g_free (groupname);
+ ++ctx->index;
+}
+
+static void
+generate_attributes (GKeyFile *file, GckSecretItem *item)
+{
+ GHashTable *attributes;
+ AttributesCtx ctx;
+
+ attributes = gck_secret_item_get_fields (item);
+ if (!attributes)
+ return;
+
+ ctx.item = item;
+ ctx.index = 0;
+ ctx.file = file;
+ ctx.compat_uint32 = g_hash_table_lookup (attributes, "compat-uint32");
+ if (!ctx.compat_uint32)
+ ctx.compat_uint32 = "";
+
+ g_hash_table_foreach (attributes, generate_each_attribute, &ctx);
+}
+
+static void
+parse_attributes (GKeyFile *file, GckSecretItem *item, const gchar **groups)
+{
+ GHashTable *attributes;
+ GString *compat_uint32;
+ const gchar *identifier;
+ const gchar **g;
+ gchar *prefix;
+ gchar *name, *type;
+
+ /* Now do the attributes */
+
+ identifier = gck_secret_object_get_identifier (GCK_SECRET_OBJECT (item));
+ prefix = g_strdup_printf ("%s:attribute", identifier);
+ attributes = gck_secret_fields_new ();
+ compat_uint32 = NULL;
+
+ for (g = groups; *g; ++g) {
+ if (!g_str_has_prefix (*g, prefix))
+ continue;
+
+ name = g_key_file_get_string (file, *g, "name", NULL);
+ if (!name || g_key_file_has_key (file, *g, "value", NULL))
+ continue;
+
+ type = g_key_file_get_string (file, *g, "type", NULL);
+ if (type && g_str_equal (type, "uint32")) {
+ if (!compat_uint32)
+ compat_uint32 = g_string_new ("");
+ g_string_append (compat_uint32, name);
+ g_string_append_c (compat_uint32, ' ');
+ }
+
+ g_free (type);
+
+ g_hash_table_replace (attributes, name,
+ g_key_file_get_string (file, *g, "value", NULL));
+ }
+
+ if (compat_uint32)
+ g_hash_table_replace (attributes, g_strdup ("compat-uint32"),
+ g_string_free (compat_uint32, FALSE));
+
+ g_free (prefix);
+}
+
+static void
+generate_acl (GKeyFile *file, GckSecretItem *item)
+{
+ const gchar *identifier;
+ GckSecretAccess *ac;
+ gchar *groupname;
+ GList *acl;
+ gint i;
+
+ /*
+ * COMPATIBILITY: If we loaded ACLs and they're set on the item,
+ * then store them back in.
+ */
+
+ identifier = gck_secret_object_get_identifier (GCK_SECRET_OBJECT (item));
+ acl = g_object_get_data (G_OBJECT (item), "compat-acl");
+ for (i = 0; acl != NULL; acl = g_list_next (acl), ++i) {
+ ac = acl->data;
+
+ /* Build a group name */
+ groupname = g_strdup_printf ("%s:acl%d", identifier, i);
+
+ if (ac->display_name)
+ g_key_file_set_string (file, groupname, "display-name", ac->display_name);
+ if (ac->pathname)
+ g_key_file_set_string (file, groupname, "path", ac->pathname);
+
+ g_key_file_set_boolean (file, groupname, "read-access",
+ ac->types_allowed & GCK_SECRET_ACCESS_READ);
+ g_key_file_set_boolean (file, groupname, "write-access",
+ ac->types_allowed & GCK_SECRET_ACCESS_WRITE);
+ g_key_file_set_boolean (file, groupname, "remove-access",
+ ac->types_allowed & GCK_SECRET_ACCESS_REMOVE);
+
+ g_free (groupname);
+ }
+}
+
+static void
+parse_acl (GKeyFile *file, GckSecretItem *item, const gchar **groups)
+{
+ GckSecretAccessType access_type;
+ GckSecretAccess *ac;
+ const gchar *identifier;
+ const gchar **g;
+ gchar *prefix;
+ gchar *path, *display;
+ GError *err = NULL;
+ GList *acl;
+
+ /*
+ * COMPATIBILITY: We don't actually use ACLs, but if we find them in the
+ * file, then load them and save back later.
+ */
+
+ identifier = gck_secret_object_get_identifier (GCK_SECRET_OBJECT (item));
+ prefix = g_strdup_printf ("%s:acl", identifier);
+ acl = NULL;
+
+ for (g = groups; *g; ++g) {
+ if (!g_str_has_prefix (*g, prefix))
+ continue;
+ path = g_key_file_get_string (file, *g, "path", NULL);
+ if (!path)
+ continue;
+
+ display = g_key_file_get_string (file, *g, "display-name", NULL);
+
+ access_type = 0;
+
+ if (g_key_file_get_boolean (file, *g, "read-access", &err) && !err)
+ access_type |= GCK_SECRET_ACCESS_READ;
+ g_clear_error (&err);
+
+ if (g_key_file_get_boolean (file, *g, "write-access", &err) && !err)
+ access_type |= GCK_SECRET_ACCESS_WRITE;
+ g_clear_error (&err);
+
+ if (g_key_file_get_boolean (file, *g, "remove-access", &err) && !err)
+ access_type |= GCK_SECRET_ACCESS_REMOVE;
+ g_clear_error (&err);
+
+ ac = g_new0 (GckSecretAccess, 1);
+ ac->display_name = display;
+ ac->pathname = path;
+ ac->types_allowed = access_type;
+
+ acl = g_list_prepend (acl, ac);
+ }
+
+ g_object_set_data_full (G_OBJECT (item), "compat-acl", acl, gck_secret_acl_free);
+ g_free (prefix);
+}
+
+static void
+generate_item (GKeyFile *file, GckSecretItem *item)
+{
+ GckSecretObject *obj;
+ GHashTable *attributes;
+ const gchar *value;
+ const gchar *groupname;
+ GckLogin *secret;
+ const gchar *password;
+ gsize n_password;
+
+ obj = GCK_SECRET_OBJECT (item);
+ groupname = gck_secret_object_get_identifier (obj);
+ attributes = gck_secret_item_get_fields (item);
+
+ /*
+ * COMPATIBILITY: We no longer have the concept of an item type.
+ * The compat-item-type field serves that purpose.
+ */
+
+ value = g_hash_table_lookup (attributes, "compat-item-type");
+ if (value != NULL)
+ g_key_file_set_string (file, groupname, "item-type", value);
+
+ value = gck_secret_object_get_label (obj);
+ if (value != NULL)
+ g_key_file_set_string (file, groupname, "display-name", value);
+
+ secret = gck_secret_item_get_secret (item);
+ if (secret != NULL) {
+ password = gck_login_get_password (secret, &n_password);
+ /* TODO: What about non-textual passwords? */
+ if (password != NULL)
+ g_key_file_set_value (file, groupname, "secret", (gchar*)password);
+ }
+
+ key_file_set_uint64 (file, groupname, "mtime", gck_secret_object_get_modified (obj));
+ key_file_set_uint64 (file, groupname, "ctime", gck_secret_object_get_created (obj));
+
+ generate_attributes (file, item);
+ generate_acl (file, item);
+}
+
+static void
+parse_item (GKeyFile *file, GckSecretItem *item, const gchar **groups)
+{
+ GckSecretObject *obj;
+ GHashTable *attributes;
+ const gchar *groupname;
+ GckLogin *secret;
+ gchar *val;
+ guint64 num;
+
+ /* First the main item data */
+
+ obj = GCK_SECRET_OBJECT (item);
+ groupname = gck_secret_object_get_identifier (obj);
+ attributes = gck_secret_item_get_fields (item);
+
+ /*
+ * COMPATIBILITY: We no longer have the concept of an item type.
+ * The compat-item-type field serves that purpose.
+ */
+
+ val = g_key_file_get_string (file, groupname, "item-type", NULL);
+ if (val != NULL)
+ g_hash_table_replace (attributes, g_strdup ("compat-item-type"), val);
+
+ val = g_key_file_get_string (file, groupname, "display-name", NULL);
+ gck_secret_object_set_label (obj, val);
+ g_free (val);
+
+ val = g_key_file_get_string (file, groupname, "secret", NULL);
+ if (val == NULL) {
+ gck_secret_item_set_secret (item, NULL);
+ } else {
+ secret = gck_login_new ((guchar*)val, strlen (val));
+ gck_secret_item_set_secret (item, secret);
+ g_object_unref (secret);
+ g_free (val);
+ }
+
+ num = 0;
+ if (key_file_get_uint64 (file, groupname, "mtime", &num))
+ gck_secret_object_set_modified (obj, num);
+ num = 0;
+ if (key_file_get_uint64 (file, groupname, "ctime", &num))
+ gck_secret_object_set_created (obj, num);
+
+ /* Now the other stuff */
+ parse_attributes (file, item, groups);
+ parse_acl (file, item, groups);
+}
+
+GckDataResult
+gck_secret_textual_write (GckSecretCollection *collection, guchar **result, gsize *n_result)
+{
+ GckSecretObject *obj;
+ GList *items, *l;
+ const gchar *value;
+ GKeyFile *file;
+ GError *err = NULL;
+ gboolean idle_lock;
+ gint idle_timeout;
+
+ obj = GCK_SECRET_OBJECT (collection);
+ g_return_val_if_fail (!gck_secret_collection_get_state (collection) == GCK_SECRET_COMPLETE, FALSE);
+
+ file = g_key_file_new ();
+
+ value = gck_secret_object_get_label (obj);
+ if (value != NULL)
+ g_key_file_set_string (file, "keyring", "display-name", value);
+
+ key_file_set_uint64 (file, "keyring", "ctime", gck_secret_object_get_created (obj));
+ key_file_set_uint64 (file, "keyring", "mtime", gck_secret_object_get_modified (obj));
+
+ /* Not currently used :( */
+ idle_lock = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (collection), "lock-on-idle"));
+ g_key_file_set_boolean (file, "keyring", "lock-on-idle", idle_lock);
+ idle_timeout = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (collection), "lock-timeout"));
+ g_key_file_set_integer (file, "keyring", "lock-timeout", idle_timeout);
+
+ items = gck_secret_collection_get_items (collection);
+ for (l = items; l; l = g_list_next (l))
+ generate_item (file, l->data);
+ g_list_free (items);
+
+ *result = (guchar*)g_key_file_to_data (file, n_result, &err);
+ g_key_file_free (file);
+
+ if (!*result) {
+ g_warning ("couldn't generate textual keyring file: %s", err->message);
+ return GCK_DATA_FAILURE;
+ }
+
+ return GCK_DATA_SUCCESS;
+}
+
+static void
+remove_unavailable_item (gpointer key, gpointer dummy, gpointer user_data)
+{
+ /* Called to remove items from a keyring that no longer exist */
+
+ GckSecretCollection *collection = GCK_SECRET_COLLECTION (user_data);
+ GckSecretItem *item;
+
+ g_assert (GCK_IS_SECRET_COLLECTION (collection));
+
+ item = gck_secret_collection_get_item (collection, key);
+ if (item != NULL)
+ gck_secret_collection_remove_item (collection, item);
+}
+
+GckDataResult
+gck_secret_textual_read (GckSecretCollection *collection, const guchar *data, gsize n_data)
+{
+ GckSecretObject *obj;
+ GckSecretItem *item;
+ GList *items, *l;
+ GError *err = NULL;
+ GKeyFile *file = NULL;
+ gchar **groups = NULL;
+ GckDataResult res = GCK_DATA_FAILURE;
+ gchar *start = NULL;
+ const gchar *identifier;
+ GHashTable *checks = NULL;
+ gboolean lock_idle;
+ gint lock_timeout;
+ gchar *value;
+ guint64 num;
+ gchar **g;
+
+ file = g_key_file_new ();
+ obj = GCK_SECRET_OBJECT (collection);
+
+ if (!g_key_file_load_from_data (file, (const gchar*)data, n_data, G_KEY_FILE_NONE, &err)) {
+ if (g_error_matches (err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE))
+ res = GCK_DATA_UNRECOGNIZED;
+ goto done;
+ }
+
+ start = g_key_file_get_start_group (file);
+ if (!start || !g_str_equal (start, "keyring")) {
+ g_message ("invalid keyring file: wrong header group");
+ goto done;
+ }
+
+ value = g_key_file_get_string (file, "keyring", "display-name", NULL);
+ gck_secret_object_set_label (obj, value);
+ g_free (value);
+
+ num = 0;
+ key_file_get_uint64 (file, "keyring", "ctime", &num);
+ gck_secret_object_set_created (obj, num);
+
+ num = 0;
+ key_file_get_uint64 (file, "keyring", "mtime", &num);
+ gck_secret_object_set_modified (obj, num);
+
+ /* Not currently used :( */
+ lock_idle = g_key_file_get_boolean (file, "keyring", "lock-on-idle", NULL);
+ g_object_set_data (G_OBJECT (collection), "lock-on-idle", GINT_TO_POINTER (lock_idle));
+ lock_timeout = g_key_file_get_integer (file, "keyring", "lock-timeout", NULL);
+ g_object_set_data (G_OBJECT (collection), "lock-timeout", GINT_TO_POINTER (lock_timeout));
+
+ /* Build a Hash table where we can track ids we haven't yet seen */
+ checks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ items = gck_secret_collection_get_items (collection);
+ for (l = items; l; l = g_list_next (l)) {
+ identifier = gck_secret_object_get_identifier (l->data);
+ g_hash_table_replace (checks, g_strdup (identifier), "unused");
+ }
+
+ groups = g_key_file_get_groups (file, NULL);
+ for (g = groups; *g; ++g) {
+ identifier = *g;
+ if (g_str_equal (identifier, "keyring"))
+ continue;
+
+ /* We've seen this id */
+ g_hash_table_remove (checks, identifier);
+
+ item = gck_secret_collection_get_item (collection, identifier);
+ if (item == NULL)
+ item = gck_secret_collection_create_item (collection, identifier);
+ parse_item (file, item, (const gchar**)groups);
+ }
+
+ g_hash_table_foreach (checks, (GHFunc)remove_unavailable_item, collection);
+ res = GCK_DATA_SUCCESS;
+
+done:
+ if (checks)
+ g_hash_table_destroy (checks);
+ if (file)
+ g_key_file_free (file);
+ g_strfreev (groups);
+ g_free (start);
+ g_clear_error (&err);
+
+ return res;
+}
diff --git a/pkcs11/secret-store/gck-secret-textual.h b/pkcs11/secret-store/gck-secret-textual.h
new file mode 100644
index 0000000..6dc66cd
--- /dev/null
+++ b/pkcs11/secret-store/gck-secret-textual.h
@@ -0,0 +1,37 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.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 Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 __GCK_SECRET_TEXTUAL_H__
+#define __GCK_SECRET_TEXTUAL_H__
+
+#include "gck-secret-collection.h"
+
+#include "gck/gck-data-types.h"
+
+GckDataResult gck_secret_textual_read (GckSecretCollection *collection,
+ const guchar *data,
+ gsize n_data);
+
+GckDataResult gck_secret_textual_write (GckSecretCollection *collection,
+ guchar **data,
+ gsize *n_data);
+
+#endif /* __GCK_SECRET_TEXTUAL_H__ */
diff --git a/pkcs11/secret-store/gck-secret-types.h b/pkcs11/secret-store/gck-secret-types.h
new file mode 100644
index 0000000..603e128
--- /dev/null
+++ b/pkcs11/secret-store/gck-secret-types.h
@@ -0,0 +1,31 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.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 Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 __GCK_SECRET_TYPES_H__
+#define __GCK_SECRET_TYPES_H__
+
+typedef struct _GckSecretCollection GckSecretCollection;
+typedef struct _GckSecretItem GckSecretItem;
+typedef struct _GckSecretObject GckSecretObject;
+typedef struct _GckSecretModule GckSecretModule;
+typedef struct _GckSecretSearch GckSecretSearch;
+
+#endif /* __GCK_SECRET_TYPES_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]