summaryrefslogtreecommitdiff
path: root/pkcs11/secret-store/gkm-secret-textual.c
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2010-05-27 03:21:59 +0000
committerStef Walter <stef@memberwebs.com>2010-06-08 15:46:24 +0000
commitcf0192e26a095c7f1840cd34b3cd1476d696a7b9 (patch)
treec29d881a5395056862abc9c7b32bd84956683c2a /pkcs11/secret-store/gkm-secret-textual.c
parent86599c674c42d2c58ab40ebb1a75fc5aeb6819b8 (diff)
downloadgnome-keyring-cf0192e26a095c7f1840cd34b3cd1476d696a7b9.tar.gz
Massive cleanup of line endings and file names.
* Cleanup all line endings for pkcs11 code. * Rename C namespace to GKM This is big "rip off the bandaid fast" change in order to prevent later constant pain with git and naming.
Diffstat (limited to 'pkcs11/secret-store/gkm-secret-textual.c')
-rw-r--r--pkcs11/secret-store/gkm-secret-textual.c550
1 files changed, 550 insertions, 0 deletions
diff --git a/pkcs11/secret-store/gkm-secret-textual.c b/pkcs11/secret-store/gkm-secret-textual.c
new file mode 100644
index 00000000..c0168f42
--- /dev/null
+++ b/pkcs11/secret-store/gkm-secret-textual.c
@@ -0,0 +1,550 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gkm-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 "gkm-secret-collection.h"
+#include "gkm-secret-compat.h"
+#include "gkm-secret-data.h"
+#include "gkm-secret-fields.h"
+#include "gkm-secret-item.h"
+#include "gkm-secret-textual.h"
+
+#include "egg/egg-error.h"
+#include "egg/egg-hex.h"
+#include "egg/egg-secure-memory.h"
+
+#include "gkm/gkm-secret.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;
+}
+
+static void
+generate_attributes (GKeyFile *file, GkmSecretItem *item)
+{
+ GHashTable *attributes;
+ gchar *groupname;
+ GList *names, *l;
+ guint32 number;
+ gint index = 0;
+
+ attributes = gkm_secret_item_get_fields (item);
+ g_return_if_fail (attributes);
+
+ names = gkm_secret_fields_get_names (attributes);
+ for (l = names; l; l = g_list_next (l)) {
+ groupname = g_strdup_printf ("%s:attribute%d",
+ gkm_secret_object_get_identifier (GKM_SECRET_OBJECT (item)),
+ index);
+
+ g_key_file_set_string (file, groupname, "name", l->data);
+
+ /*
+ * 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 (gkm_secret_fields_get_compat_uint32 (attributes, l->data, &number)) {
+ g_key_file_set_string (file, groupname, "type", "uint32");
+ key_file_set_uint64 (file, groupname, "value", number);
+
+ /* A normal string attribute */
+ } else {
+ g_key_file_set_string (file, groupname, "type", "string");
+ g_key_file_set_string (file, groupname, "value", gkm_secret_fields_get (attributes, l->data));
+ }
+
+ g_free (groupname);
+ ++index;
+ }
+}
+
+static void
+parse_attributes (GKeyFile *file, GkmSecretItem *item, const gchar **groups)
+{
+ GHashTable *attributes;
+ const gchar *identifier;
+ const gchar **g;
+ gchar *prefix;
+ gchar *name, *type;
+ guint64 number;
+
+ /* Now do the attributes */
+
+ identifier = gkm_secret_object_get_identifier (GKM_SECRET_OBJECT (item));
+ prefix = g_strdup_printf ("%s:attribute", identifier);
+ attributes = gkm_secret_fields_new ();
+
+ 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)
+ continue;
+
+ type = g_key_file_get_string (file, *g, "type", NULL);
+
+ /* A uint32 type value */
+ if (type && g_str_equal (type, "uint32")) {
+ if (key_file_get_uint64 (file, *g, "value", &number))
+ gkm_secret_fields_add_compat_uint32 (attributes, name, number);
+ g_free (name);
+
+ /* A string type value */
+ } else {
+ gkm_secret_fields_take (attributes, name,
+ g_key_file_get_string (file, *g, "value", NULL));
+ }
+
+ g_free (type);
+ }
+
+ gkm_secret_item_set_fields (item, attributes);
+ g_hash_table_unref (attributes);
+ g_free (prefix);
+}
+
+static void
+generate_acl (GKeyFile *file, GkmSecretItem *item)
+{
+ const gchar *identifier;
+ GkmSecretAccess *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 = gkm_secret_object_get_identifier (GKM_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 & GKM_SECRET_ACCESS_READ);
+ g_key_file_set_boolean (file, groupname, "write-access",
+ ac->types_allowed & GKM_SECRET_ACCESS_WRITE);
+ g_key_file_set_boolean (file, groupname, "remove-access",
+ ac->types_allowed & GKM_SECRET_ACCESS_REMOVE);
+
+ g_free (groupname);
+ }
+}
+
+static void
+parse_acl (GKeyFile *file, GkmSecretItem *item, const gchar **groups)
+{
+ GkmSecretAccessType access_type;
+ GkmSecretAccess *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 = gkm_secret_object_get_identifier (GKM_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 |= GKM_SECRET_ACCESS_READ;
+ g_clear_error (&err);
+
+ if (g_key_file_get_boolean (file, *g, "write-access", &err) && !err)
+ access_type |= GKM_SECRET_ACCESS_WRITE;
+ g_clear_error (&err);
+
+ if (g_key_file_get_boolean (file, *g, "remove-access", &err) && !err)
+ access_type |= GKM_SECRET_ACCESS_REMOVE;
+ g_clear_error (&err);
+
+ ac = g_new0 (GkmSecretAccess, 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, gkm_secret_compat_acl_free);
+ g_free (prefix);
+}
+
+static void
+generate_item (GKeyFile *file, GkmSecretItem *item, GkmSecretData *sdata)
+{
+ GkmSecretObject *obj;
+ GHashTable *attributes;
+ const gchar *value;
+ const gchar *identifier;
+ const guchar *secret;
+ gsize n_secret;
+ gchar *hex;
+
+ g_assert (file);
+ g_assert (GKM_IS_SECRET_ITEM (item));
+ g_assert (GKM_IS_SECRET_DATA (sdata));
+
+ obj = GKM_SECRET_OBJECT (item);
+ identifier = gkm_secret_object_get_identifier (obj);
+ attributes = gkm_secret_item_get_fields (item);
+
+ value = gkm_secret_item_get_schema (item);
+ g_key_file_set_integer (file, identifier, "item-type",
+ gkm_secret_compat_parse_item_type (value));
+
+ value = gkm_secret_object_get_label (obj);
+ if (value != NULL)
+ g_key_file_set_string (file, identifier, "display-name", value);
+
+ secret = gkm_secret_data_get_raw (sdata, identifier, &n_secret);
+ if (secret != NULL) {
+ /* A textual secret. Note that secrets are always null-terminated. */
+ if (g_utf8_validate ((gchar*)secret, n_secret, NULL)) {
+ g_key_file_set_value (file, identifier, "secret", (gchar*)secret);
+
+ /* A non-textual secret */
+ } else {
+ hex = egg_hex_encode (secret, n_secret);
+ g_key_file_set_value (file, identifier, "binary-secret", hex);
+ g_free (hex);
+ }
+ }
+
+ key_file_set_uint64 (file, identifier, "mtime", gkm_secret_object_get_modified (obj));
+ key_file_set_uint64 (file, identifier, "ctime", gkm_secret_object_get_created (obj));
+
+ generate_attributes (file, item);
+ generate_acl (file, item);
+}
+
+static void
+parse_item (GKeyFile *file, GkmSecretItem *item, GkmSecretData *sdata,
+ const gchar **groups)
+{
+ GkmSecretObject *obj;
+ GHashTable *attributes;
+ const gchar *identifier;
+ GError *err = NULL;
+ GkmSecret *secret;
+ guchar *binary;
+ gsize n_binary;
+ gchar *val;
+ guint64 num;
+ gint type;
+
+ /* First the main item data */
+
+ obj = GKM_SECRET_OBJECT (item);
+ identifier = gkm_secret_object_get_identifier (obj);
+ attributes = gkm_secret_item_get_fields (item);
+
+ type = g_key_file_get_integer (file, identifier, "item-type", &err);
+ if (err) {
+ g_clear_error (&err);
+ type = 0;
+ }
+ gkm_secret_item_set_schema (item, gkm_secret_compat_format_item_type (type));
+
+ val = g_key_file_get_string (file, identifier, "display-name", NULL);
+ gkm_secret_object_set_label (obj, val);
+ g_free (val);
+
+ if (sdata) {
+ secret = NULL;
+
+ /* A textual secret */
+ val = g_key_file_get_string (file, identifier, "secret", NULL);
+ if (val != NULL) {
+ secret = gkm_secret_new_from_password (val);
+ g_free (val);
+
+ /* A binary secret */
+ } else {
+ val = g_key_file_get_string (file, identifier, "binary-secret", NULL);
+ if (val != NULL) {
+ binary = egg_hex_decode (val, -1, &n_binary);
+ secret = gkm_secret_new (binary, n_binary);
+ g_free (binary);
+ g_free (val);
+ }
+ }
+
+ /* Put the secret in the right place */
+ if (secret == NULL) {
+ gkm_secret_data_remove_secret (sdata, identifier);
+ } else {
+ gkm_secret_data_set_secret (sdata, identifier, secret);
+ g_object_unref (secret);
+ }
+ }
+
+ num = 0;
+ if (key_file_get_uint64 (file, identifier, "mtime", &num))
+ gkm_secret_object_set_modified (obj, num);
+ num = 0;
+ if (key_file_get_uint64 (file, identifier, "ctime", &num))
+ gkm_secret_object_set_created (obj, num);
+
+ /* Now the other stuff */
+ parse_attributes (file, item, groups);
+ parse_acl (file, item, groups);
+}
+
+GkmDataResult
+gkm_secret_textual_write (GkmSecretCollection *collection, GkmSecretData *sdata,
+ guchar **data, gsize *n_data)
+{
+ GkmSecretObject *obj;
+ GList *items, *l;
+ const gchar *value;
+ GKeyFile *file;
+ GError *err = NULL;
+ gint idle_timeout;
+
+ g_return_val_if_fail (GKM_IS_SECRET_COLLECTION (collection), GKM_DATA_FAILURE);
+ g_return_val_if_fail (GKM_IS_SECRET_DATA (sdata), GKM_DATA_LOCKED);
+ g_return_val_if_fail (data && n_data, GKM_DATA_FAILURE);
+
+ obj = GKM_SECRET_OBJECT (collection);
+ file = g_key_file_new ();
+
+ value = gkm_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", gkm_secret_object_get_created (obj));
+ key_file_set_uint64 (file, "keyring", "mtime", gkm_secret_object_get_modified (obj));
+
+ idle_timeout = gkm_secret_collection_get_lock_idle (collection);
+ g_key_file_set_boolean (file, "keyring", "lock-on-idle", idle_timeout > 0);
+ if (idle_timeout)
+ g_key_file_set_integer (file, "keyring", "lock-timeout", idle_timeout);
+ idle_timeout = gkm_secret_collection_get_lock_after (collection);
+ g_key_file_set_boolean (file, "keyring", "lock-after", idle_timeout > 0);
+ if (idle_timeout)
+ g_key_file_set_integer (file, "keyring", "lock-timeout", idle_timeout);
+
+ items = gkm_secret_collection_get_items (collection);
+ for (l = items; l; l = g_list_next (l))
+ generate_item (file, l->data, sdata);
+ g_list_free (items);
+
+ *data = (guchar*)g_key_file_to_data (file, n_data, &err);
+ g_key_file_free (file);
+
+ if (!*data) {
+ g_warning ("couldn't generate textual keyring file: %s", egg_error_message (err));
+ return GKM_DATA_FAILURE;
+ }
+
+ return GKM_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 */
+
+ GkmSecretCollection *collection = GKM_SECRET_COLLECTION (user_data);
+ GkmSecretItem *item;
+
+ g_assert (GKM_IS_SECRET_COLLECTION (collection));
+
+ item = gkm_secret_collection_get_item (collection, key);
+ if (item != NULL)
+ gkm_secret_collection_remove_item (collection, item);
+}
+
+GkmDataResult
+gkm_secret_textual_read (GkmSecretCollection *collection, GkmSecretData *sdata,
+ const guchar *data, gsize n_data)
+{
+ GkmSecretObject *obj;
+ GkmSecretItem *item;
+ GList *items, *l;
+ GError *err = NULL;
+ GKeyFile *file = NULL;
+ gchar **groups = NULL;
+ GkmDataResult res = GKM_DATA_FAILURE;
+ gchar *start = NULL;
+ const gchar *identifier;
+ GHashTable *checks = NULL;
+ gint lock_timeout;
+ gchar *value;
+ guint64 num;
+ gchar **g;
+
+ g_return_val_if_fail (GKM_IS_SECRET_COLLECTION (collection), GKM_DATA_FAILURE);
+ g_return_val_if_fail (!sdata || GKM_IS_SECRET_DATA (sdata), GKM_DATA_FAILURE);
+
+ file = g_key_file_new ();
+ obj = GKM_SECRET_OBJECT (collection);
+
+ if (!n_data) {
+ res = GKM_DATA_UNRECOGNIZED;
+ goto done;
+ }
+
+ 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 = GKM_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);
+ gkm_secret_object_set_label (obj, value);
+ g_free (value);
+
+ num = 0;
+ key_file_get_uint64 (file, "keyring", "ctime", &num);
+ gkm_secret_object_set_created (obj, num);
+
+ num = 0;
+ key_file_get_uint64 (file, "keyring", "mtime", &num);
+ gkm_secret_object_set_modified (obj, num);
+
+ /* Not currently used :( */
+ lock_timeout = g_key_file_get_integer (file, "keyring", "lock-timeout", NULL);
+ if (g_key_file_get_boolean (file, "keyring", "lock-after", NULL))
+ gkm_secret_collection_set_lock_idle (collection, lock_timeout);
+ else if (g_key_file_get_boolean (file, "keyring", "lock-on-idle", NULL))
+ gkm_secret_collection_set_lock_idle (collection, lock_timeout);
+
+ 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 = gkm_secret_collection_get_items (collection);
+ for (l = items; l; l = g_list_next (l)) {
+ identifier = gkm_secret_object_get_identifier (l->data);
+ g_hash_table_replace (checks, g_strdup (identifier), "unused");
+ }
+ g_list_free (items);
+
+ groups = g_key_file_get_groups (file, NULL);
+ for (g = groups; *g; ++g) {
+ identifier = *g;
+ if (g_str_equal (identifier, "keyring") || strchr (identifier, ':'))
+ continue;
+
+ /* We've seen this id */
+ g_hash_table_remove (checks, identifier);
+
+ item = gkm_secret_collection_get_item (collection, identifier);
+ if (item == NULL)
+ item = gkm_secret_collection_new_item (collection, identifier);
+ parse_item (file, item, sdata, (const gchar**)groups);
+ }
+
+ g_hash_table_foreach (checks, (GHFunc)remove_unavailable_item, collection);
+ res = GKM_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;
+}