cpw.ljfisher.ssl_client_auth: 20a0deb1: Added private key API and implementation...

lucas.fisher at gmail.com lucas.fisher at gmail.com
Sat Apr 16 19:35:54 EDT 2011


----------------------------------------------------------------------
Revision: 20a0deb10ba361332544898d3f178f9cfd77518b
Parent:   3a04ab1fbc926699834c544430efd357ac3edd9e
Author:   lucas.fisher at gmail.com
Date:     04/16/11 16:12:10
Branch:   im.pidgin.cpw.ljfisher.ssl_client_auth
URL: http://d.pidgin.im/viewmtn/revision/info/20a0deb10ba361332544898d3f178f9cfd77518b

Changelog: 

Added private key API and implementation for use with SSL/TSL client-side certificate authentication

Changes against parent 3a04ab1fbc926699834c544430efd357ac3edd9e

  added    libpurple/privatekey.c
  added    libpurple/privatekey.h

-------------- next part --------------
============================================================
--- /dev/null	
+++ libpurple/privatekey.c	69f526767a11e5ecf5b35e066de74e5df07288d0
@@ -0,0 +1,909 @@
+/**
+ * @file privatekey.c Private-Key API
+ * @ingroup core
+ */
+
+/*
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program 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.
+ *
+ * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#include "internal.h"
+#include "privatekey.h"
+#include "dbus-maybe.h"
+#include "debug.h"
+#include "request.h"
+#include "signals.h"
+#include "util.h"
+
+/** List holding pointers to all registered private key schemes */
+static GList *key_schemes = NULL;
+/** List of registered Pools */
+static GList *key_pools = NULL;
+
+
+/*****************************************************************************
+ *  Purple Private Key API                                                   *
+ *****************************************************************************/
+
+PurplePrivateKey *
+purple_privatekey_copy(PurplePrivateKey *key)
+{
+	g_return_val_if_fail(key, NULL);
+	g_return_val_if_fail(key->scheme, NULL);
+	g_return_val_if_fail(key->scheme->copy_key, NULL);
+
+	return (key->scheme->copy_key)(key);
+}
+
+void
+purple_privatekey_destroy (PurplePrivateKey *key)
+{
+	PurplePrivateKeyScheme *scheme;
+
+	if (NULL == key) return;
+
+	scheme = key->scheme;
+
+	(scheme->destroy_key)(key);
+}
+
+PurplePrivateKey *
+purple_privatekey_import(PurplePrivateKeyScheme *scheme, const gchar *filename,
+						 const gchar *password)
+{
+	g_return_val_if_fail(scheme, NULL);
+	g_return_val_if_fail(scheme->import_key, NULL);
+	g_return_val_if_fail(filename, NULL);
+
+	return (scheme->import_key)(filename, password);
+}
+
+gboolean
+purple_privatekey_export(const gchar *filename, PurplePrivateKey *key,
+						 const gchar *password)
+{
+	PurplePrivateKeyScheme *scheme;
+
+	g_return_val_if_fail(filename, FALSE);
+	g_return_val_if_fail(key, FALSE);
+	g_return_val_if_fail(key->scheme, FALSE);
+
+	scheme = key->scheme;
+	g_return_val_if_fail(scheme->export_key, FALSE);
+
+	return (scheme->export_key)(filename, key, password);
+}
+
+gchar *
+purple_privatekey_get_unique_id(PurplePrivateKey *key)
+{
+	g_return_val_if_fail(key, NULL);
+	g_return_val_if_fail(key->scheme, NULL);
+	g_return_val_if_fail(key->scheme->get_unique_id, NULL);
+
+	return (key->scheme->get_unique_id)(key);
+}
+
+
+
+
+/*****************************************************************************
+ * Purple Private Key Pool API                                               *
+ *****************************************************************************/
+
+gchar *
+purple_privatekey_pool_mkpath(PurplePrivateKeyPool *pool, const gchar *id)
+{
+	gchar *path;
+	gchar *esc_scheme_name, *esc_name, *esc_id;
+
+	g_return_val_if_fail(pool, NULL);
+	g_return_val_if_fail(pool->scheme_name, NULL);
+	g_return_val_if_fail(pool->name, NULL);
+
+	/* Escape all the elements for filesystem-friendliness */
+	esc_scheme_name = pool ? g_strdup(purple_escape_filename(pool->scheme_name)) : NULL;
+	esc_name = pool ? g_strdup(purple_escape_filename(pool->name)) : NULL;
+	esc_id = id ? g_strdup(purple_escape_filename(id)) : NULL;
+
+	path = g_build_filename(purple_user_dir(),
+				"privatekeys", /* TODO: constantize this? */
+				esc_scheme_name,
+				esc_name,
+				esc_id,
+				NULL);
+
+	g_free(esc_scheme_name);
+	g_free(esc_name);
+	g_free(esc_id);
+	return path;
+}
+
+#if 0
+gboolean
+purple_privatekey_pool_set_password(PurplePrivateKeyPool *pool, const gchar* password)
+{
+	g_return_val_if_fail(pool, FALSE);
+	g_return_val_if_fail(password, FALSE);
+
+	(pool->set_password)(password);
+
+	return TRUE;
+}
+
+const gchar*
+purple_privatekey_pool_get_password(PurplePrivateKeyPool *pool)
+{
+	g_returen_val_if_fail(pool, FALSE);
+
+	return (pool->get_password)();
+}
+#endif
+
+gboolean
+purple_privatekey_pool_usable(PurplePrivateKeyPool *pool)
+{
+	g_return_val_if_fail(pool, FALSE);
+	g_return_val_if_fail(pool->scheme_name, FALSE);
+
+	/* Check that the pool's scheme is loaded */
+	if (purple_privatekey_find_scheme(pool->scheme_name) == NULL) {
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+PurplePrivateKeyScheme *
+purple_privatekey_pool_get_scheme(PurplePrivateKeyPool *pool)
+{
+	g_return_val_if_fail(pool, NULL);
+	g_return_val_if_fail(pool->scheme_name, NULL);
+
+	return purple_privatekey_find_scheme(pool->scheme_name);
+}
+
+gboolean
+purple_privatekey_pool_contains(PurplePrivateKeyPool *pool, const gchar *id)
+{
+	g_return_val_if_fail(pool, FALSE);
+	g_return_val_if_fail(id, FALSE);
+	g_return_val_if_fail(pool->key_in_pool, FALSE);
+
+	return (pool->key_in_pool)(id);
+}
+
+/******************************************************************************
+ * Purple Private Key retrieve and store that prompt user for password
+ */
+
+typedef struct {
+	PurplePrivateKeyPool *pool;
+	PurplePrivateKey *key;
+	const gchar* id;
+	GCallback ok_cb;
+	GCallback cancel_cb;
+	void *user_data;
+} privatekey_pool_req_data;
+
+static void
+purple_privatekey_pool_request_password(
+		const gchar* msg,
+		PurplePrivateKeyPool *pool,
+		const gchar* id,
+		GCallback  ok_cb,
+		GCallback cancel_cb,
+		void *user_data)
+{
+	PurpleRequestFieldGroup *group;
+	PurpleRequestField *field;
+	PurpleRequestFields *fields;
+
+	/* Close any previous password request windows */
+	purple_request_close_with_handle(pool);
+
+	fields = purple_request_fields_new();
+	group = purple_request_field_group_new(NULL);
+	purple_request_fields_add_group(fields, group);
+
+	field = purple_request_field_string_new("password", _("Enter Password"), NULL, FALSE);
+	purple_request_field_string_set_masked(field, TRUE);
+	purple_request_field_set_required(field, TRUE);
+	purple_request_field_group_add_field(group, field);
+/*
+	field = purple_request_field_bool_new("remember", _("Save password"), FALSE);
+	purple_request_field_group_add_field(group, field);
+*/
+	purple_request_fields((void*)id,
+                        NULL,
+                        msg,
+                        NULL,
+                        fields,
+                        _("OK"), ok_cb,
+                        _("Cancel"), cancel_cb,
+			NULL, NULL, NULL,
+                        user_data);
+}
+
+
+static void
+privatekey_pool_req_cancel_cb(privatekey_pool_req_data *data, PurpleRequestFields *fields)
+{
+	((PurplePrivateKeyPoolCancelCb)(data->cancel_cb))(data->user_data);
+	g_free(data);
+}
+
+static void
+privatekey_pool_req_retrieve_ok_cb(privatekey_pool_req_data *data, PurpleRequestFields *fields)
+{
+	const char *entry;
+	gboolean remember;
+	PurplePrivateKey *key;
+
+	entry = purple_request_fields_get_string(fields, "password");
+/*	remember = purple_request_fields_get_bool(fields, "remember");*/
+
+	if (!entry || !*entry)
+	{
+		/* TODO: can we use any pointer for the handle here? */
+		purple_notify_error((void*)data->id, NULL, _("Password is required to access your private keys."), NULL);
+		return;
+	}
+/*
+	if(remember)
+		purple_account_set_remember_password(account, TRUE);
+*/
+
+	key = (data->pool->get_key)(data->id, entry);
+
+	((PurplePrivateKeyPoolRetrieveRequestOkCb)data->ok_cb)(key, data->user_data);
+	g_free(data);
+}
+
+void purple_privatekey_pool_retrieve_request(
+	PurplePrivateKeyPool *pool, const gchar *id,
+//	PurplePrivateKeyPoolRetrieveRequestOkCb ok_cb,
+//	PurplePrivateKeyPoolCancelCb cancel_cb,
+	GCallback ok_cb,
+	GCallback cancel_cb,
+	void* user_data)
+{
+	gchar* msg;
+
+	privatekey_pool_req_data *data;
+
+	g_return_if_fail(pool);
+	g_return_if_fail(id);
+	g_return_if_fail(ok_cb);
+	g_return_if_fail(cancel_cb);
+	
+
+	data = g_new0(privatekey_pool_req_data, 1);
+	g_return_if_fail(data);
+
+	data->key = NULL;
+	data->pool = pool;
+	data->id = id;
+	data->ok_cb = G_CALLBACK(ok_cb);
+	data->cancel_cb = G_CALLBACK(cancel_cb);
+	data->user_data = user_data;
+
+	msg = g_strdup_printf(_("Enter the password protecting the key named \"%s\""), id);
+	purple_privatekey_pool_request_password(
+		msg,
+		pool,
+		id,
+		G_CALLBACK(privatekey_pool_req_retrieve_ok_cb),
+		G_CALLBACK(privatekey_pool_req_cancel_cb),
+		data);
+	g_free(msg); /* TODO: not sure this is safe */
+}
+
+static void
+privatekey_pool_req_store_ok_cb(privatekey_pool_req_data *data, PurpleRequestFields *fields)
+{
+	const char *entry;
+	gboolean remember;
+	gboolean result;
+
+	entry = purple_request_fields_get_string(fields, "password");
+/*	remember = purple_request_fields_get_bool(fields, "remember");*/
+
+	if (!entry || !*entry)
+	{
+		/* TODO: can we use any pointer for the handle here? */
+		purple_notify_error((void*)data->id, NULL, _("Password is required to protect your private keys."), NULL);
+		return;
+	}
+/*
+	if(remember)
+		purple_account_set_remember_password(account, TRUE);
+*/
+
+	result = (data->pool->put_key)(data->id, data->key, entry);
+
+	((PurplePrivateKeyPoolStoreRequestOkCb)data->ok_cb)(result, data->user_data);
+	g_free(data);
+}
+
+void 
+purple_privatekey_pool_store_request(
+	PurplePrivateKeyPool *pool, const gchar *id, PurplePrivateKey *key,
+//	PurplePrivateKeyPoolStoreRequestOkCb ok_cb,
+//	PurplePrivateKeyPoolCancelCb cancel_cb,
+	GCallback ok_cb,
+	GCallback cancel_cb,
+	void* user_data)
+{
+	gchar* msg;
+	privatekey_pool_req_data *data;
+
+	g_return_if_fail(pool);
+	g_return_if_fail(id);
+	g_return_if_fail(ok_cb);
+	g_return_if_fail(cancel_cb);
+	
+
+	data = g_new0(privatekey_pool_req_data, 1);
+	g_return_if_fail(data);
+
+	data->key = key;
+	data->pool = pool;
+	data->id = id;
+	data->ok_cb = G_CALLBACK(ok_cb);
+	data->cancel_cb = G_CALLBACK(cancel_cb);
+	data->user_data = user_data;
+
+	msg = g_strdup_printf(_("Enter a password to protect the key named \"%s\""), id);
+	purple_privatekey_pool_request_password(
+		msg,
+		pool,
+		id,
+		G_CALLBACK(privatekey_pool_req_store_ok_cb),
+		G_CALLBACK(privatekey_pool_req_cancel_cb),
+		data);
+	g_free(msg); /* TODO: not sure this is safe */
+}
+
+/******************************************************************************
+ * Purple Private Key Pool direct retrieve and store 
+ */
+
+PurplePrivateKey *
+purple_privatekey_pool_retrieve(PurplePrivateKeyPool *pool, const gchar *id, const gchar *password)
+{
+	g_return_val_if_fail(pool, NULL);
+	g_return_val_if_fail(id, NULL);
+	g_return_val_if_fail(pool->get_key, NULL);
+
+	return (pool->get_key)(id, password);
+}
+
+gboolean
+purple_privatekey_pool_store(PurplePrivateKeyPool *pool, const gchar *id, PurplePrivateKey *key, const gchar* password)
+{
+	gboolean ret = FALSE;
+
+	g_return_val_if_fail(pool, FALSE);
+	g_return_val_if_fail(id, FALSE);
+	g_return_val_if_fail(pool->put_key, FALSE);
+
+	/* Whether key->scheme matches find_scheme(pool->scheme_name) is not
+	   relevant... I think... */
+	g_return_val_if_fail(
+		g_ascii_strcasecmp(pool->scheme_name, key->scheme->name) == 0,
+		FALSE);
+
+	ret = (pool->put_key)(id, key, password);
+
+	/* Signal that the certificate was stored if success*/
+	if (ret) {
+		purple_signal_emit(pool, "privatekey-stored",
+				   pool, id);
+	}
+
+	return ret;
+}
+
+gboolean
+purple_privatekey_pool_delete(PurplePrivateKeyPool *pool, const gchar *id)
+{
+	gboolean ret = FALSE;
+
+	g_return_val_if_fail(pool, FALSE);
+	g_return_val_if_fail(id, FALSE);
+	g_return_val_if_fail(pool->delete_key, FALSE);
+
+	ret = (pool->delete_key)(id);
+
+	/* Signal that the key was deleted if success */
+	if (ret) {
+		purple_signal_emit(pool, "key-deleted",
+				   pool, id);
+	}
+
+	return ret;
+}
+
+GList *
+purple_privatekey_pool_get_idlist(PurplePrivateKeyPool *pool)
+{
+	g_return_val_if_fail(pool, NULL);
+	g_return_val_if_fail(pool->get_idlist, NULL);
+
+	return (pool->get_idlist)();
+}
+
+void
+purple_privatekey_pool_destroy_idlist(GList *idlist)
+{
+	GList *l;
+
+	/* Iterate through and free them strings */
+	for ( l = idlist; l; l = l->next ) {
+		g_free(l->data);
+	}
+
+	g_list_free(idlist);
+}
+
+
+/****************************************************************************/
+/* Builtin Pools                                                            */
+/****************************************************************************/
+
+/***** Cache of user's keys *****/
+static PurplePrivateKeyPool x509_user_keys;
+
+typedef struct x509_user_data {
+	gchar *password;
+} x509_user_data_t;
+
+static GList* x509_user_key_paths = NULL;
+
+static gboolean
+x509_user_init(void)
+{
+	gchar *poolpath;
+	int ret;
+
+	x509_user_keys.data = g_new0(x509_user_data_t, 1);
+
+	/* Set up key cache here if it isn't already done */
+	poolpath = purple_privatekey_pool_mkpath(&x509_user_keys, NULL);
+	ret = purple_build_dir(poolpath, 0700); /* Make it this user only */
+
+	if (ret != 0)
+		purple_debug_info("x509_user/keys",
+				"Could not create %s.  Keys will not be saved.\n",
+				poolpath);
+
+	g_free(poolpath);
+
+	return TRUE;
+}
+
+static void
+x509_user_uninit(void)
+{
+	if (x509_user_keys.data != NULL)
+		g_free(x509_user_keys.data);
+}
+/*
+static void 
+x509_user_set_password(const gchar *password)
+{
+	x509_user_data_t *user_data = (x509_user_data_t*)x509_user_keys.data;
+
+	user_data->password = g_strdup(password);
+	purple_debug_info("x509_user/keys", "Setting password\n");
+}
+*/
+static gboolean
+x509_user_key_in_pool(const gchar *id)
+{
+	gchar *keypath;
+	gboolean ret = FALSE;
+
+	g_return_val_if_fail(id, FALSE);
+
+	keypath = purple_privatekey_pool_mkpath(&x509_user_keys, id);
+
+	ret = g_file_test(keypath, G_FILE_TEST_IS_REGULAR);
+
+	g_free(keypath);
+	return ret;
+}
+
+static PurplePrivateKey *
+x509_user_get_key(const gchar *id, const gchar* password)
+{
+	PurplePrivateKeyScheme *x509;
+	PurplePrivateKey *key;
+	gchar *keypath;
+
+	g_return_val_if_fail(id, NULL);
+
+	/* Is it in the pool? */
+	if ( !x509_user_key_in_pool(id) ) {
+		return NULL;
+	}
+
+	/* Look up the X.509 scheme */
+	x509 = purple_privatekey_find_scheme("x509");
+	g_return_val_if_fail(x509, NULL);
+
+	/* Okay, now find and load that key */
+	keypath = purple_privatekey_pool_mkpath(&x509_user_keys, id);
+	key = purple_privatekey_import(x509, keypath, password);
+
+	g_free(keypath);
+
+	return key;
+}
+
+static gboolean
+x509_user_put_key(const gchar *id, PurplePrivateKey *key, const gchar* password)
+{
+	gboolean ret = FALSE;
+	gchar *keypath;
+
+	g_return_val_if_fail(key, FALSE);
+	g_return_val_if_fail(key->scheme, FALSE);
+	/* Make sure that this is some kind of X.509 certificate */
+	/* TODO: Perhaps just check key->scheme->name instead? */
+	g_return_val_if_fail(key->scheme == purple_privatekey_find_scheme(x509_user_keys.scheme_name), FALSE);
+
+	/* Work out the filename and export */
+	keypath = purple_privatekey_pool_mkpath(&x509_user_keys, id);
+	ret = purple_privatekey_export(keypath, key, password);
+
+	g_free(keypath);
+	return ret;
+}
+
+static gboolean
+x509_user_delete_key(const gchar *id)
+{
+	gboolean ret = FALSE;
+	gchar *keypath;
+
+	g_return_val_if_fail(id, FALSE);
+
+	/* Is the id even in the pool? */
+	if (!x509_user_key_in_pool(id)) {
+		purple_debug_warning("x509_user/keys",
+				     "Id %s wasn't in the key pool\n",
+				     id);
+		return FALSE;
+	}
+
+	/* OK, so work out the keypath and delete the thing */
+	keypath = purple_privatekey_pool_mkpath(&x509_user_keys, id);
+	if ( unlink(keypath) != 0 ) {
+		purple_debug_error("x509_user/keys",
+				   "Unlink of %s failed!\n",
+				   keypath);
+		ret = FALSE;
+	} else {
+		ret = TRUE;
+	}
+
+	g_free(keypath);
+	return ret;
+}
+
+static GList *
+x509_user_get_idlist(void)
+{
+	GList *idlist = NULL;
+	GDir *dir;
+	const gchar *entry;
+	gchar *poolpath;
+
+	/* Get a handle on the pool directory */
+	poolpath = purple_privatekey_pool_mkpath(&x509_user_keys, NULL);
+	dir = g_dir_open(poolpath,
+			 0,     /* No flags */
+			 NULL); /* Not interested in what the error is */
+	g_free(poolpath);
+
+	g_return_val_if_fail(dir, NULL);
+
+	/* Traverse the directory listing and create an idlist */
+	while ( (entry = g_dir_read_name(dir)) != NULL ) {
+		/* Unescape the filename */
+		const char *unescaped = purple_unescape_filename(entry);
+
+		/* Copy the entry name into our list (GLib owns the original
+		   string) */
+		idlist = g_list_prepend(idlist, g_strdup(unescaped));
+	}
+
+	/* Release the directory */
+	g_dir_close(dir);
+
+	return idlist;
+}
+
+static PurplePrivateKeyPool x509_user_keys = {
+	"x509",                       /* Scheme name */
+	"user",                       /* Pool name */
+	N_("My Private Keys"),        /* User-friendly name */
+	NULL,                         /* Internal data */
+	x509_user_init,               /* init */
+	x509_user_uninit,             /* uninit */
+	x509_user_key_in_pool,  /* Certificate exists? */
+	x509_user_get_key,      /* Cert retriever */
+	x509_user_put_key,      /* Cert writer */
+	x509_user_delete_key,   /* Cert remover */
+	x509_user_get_idlist,   /* idlist retriever */
+
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+/****************************************************************************/
+/* Subsystem                                                                */
+/****************************************************************************/
+void
+purple_privatekey_init(void)
+{
+	/* Register builtins */
+	purple_privatekey_register_pool(&x509_user_keys);
+}
+
+void
+purple_privatekey_uninit(void)
+{
+	/* Unregister all Pools */
+	g_list_foreach(key_pools, (GFunc)purple_privatekey_unregister_pool, NULL);
+}
+
+gpointer
+purple_privatekey_get_handle(void)
+{
+	static gint handle;
+	return &handle;
+}
+
+PurplePrivateKeyScheme *
+purple_privatekey_find_scheme(const gchar *name)
+{
+	PurplePrivateKeyScheme *scheme = NULL;
+	GList *l;
+
+	g_return_val_if_fail(name, NULL);
+
+	/* Traverse the list of registered schemes and locate the
+	   one whose name matches */
+	for(l = key_schemes; l; l = l->next) {
+		scheme = (PurplePrivateKeyScheme *)(l->data);
+
+		/* Name matches? that's our man */
+		if(!g_ascii_strcasecmp(scheme->name, name))
+			return scheme;
+	}
+
+	purple_debug_warning("privatekey",
+			     "PrivateKeyScheme %s requested but not found.\n",
+			     name);
+
+	/* TODO: Signalling and such? */
+
+	return NULL;
+}
+
+GList *
+purple_privatekey_get_schemes(void)
+{
+	return key_schemes;
+}
+
+gboolean
+purple_privatekey_register_scheme(PurplePrivateKeyScheme *scheme)
+{
+	g_return_val_if_fail(scheme != NULL, FALSE);
+
+	/* Make sure no scheme is registered with the same name */
+	if (purple_privatekey_find_scheme(scheme->name) != NULL) {
+		return FALSE;
+	}
+
+	/* Okay, we're golden. Register it. */
+	key_schemes = g_list_prepend(key_schemes, scheme);
+
+	/* TODO: Signalling and such? */
+
+	purple_debug_info("privatekey",
+			  "PrivateKeyScheme %s registered\n",
+			  scheme->name);
+
+	return TRUE;
+}
+
+gboolean
+purple_privatekey_unregister_scheme(PurplePrivateKeyScheme *scheme)
+{
+	if (NULL == scheme) {
+		purple_debug_warning("privatekey",
+				     "Attempting to unregister NULL scheme\n");
+		return FALSE;
+	}
+
+	/* TODO: signalling? */
+
+	/* TODO: unregister all PrivateKeyPools for this scheme? */
+	/* Neither of the above should be necessary, though */
+	key_schemes = g_list_remove(key_schemes, scheme);
+
+	purple_debug_info("privatekey",
+			  "PrivateKeyScheme %s unregistered\n",
+			  scheme->name);
+
+
+	return TRUE;
+}
+
+PurplePrivateKeyPool *
+purple_privatekey_find_pool(const gchar *scheme_name, const gchar *pool_name)
+{
+	PurplePrivateKeyPool *pool = NULL;
+	GList *l;
+
+	g_return_val_if_fail(scheme_name, NULL);
+	g_return_val_if_fail(pool_name, NULL);
+
+	/* Traverse the list of registered pools and locate the
+	   one whose name matches */
+	for(l = key_pools; l; l = l->next) {
+		pool = (PurplePrivateKeyPool *)(l->data);
+
+		/* Scheme and name match? */
+		if(!g_ascii_strcasecmp(pool->scheme_name, scheme_name) &&
+		   !g_ascii_strcasecmp(pool->name, pool_name))
+			return pool;
+	}
+
+	purple_debug_warning("privatekey",
+			     "PrivateKeyPool %s, %s requested but not found.\n",
+			     scheme_name, pool_name);
+
+	/* TODO: Signalling and such? */
+
+	return NULL;
+
+}
+
+GList *
+purple_privatekey_get_pools(void)
+{
+	return key_pools;
+}
+
+gboolean
+purple_privatekey_register_pool(PurplePrivateKeyPool *pool)
+{
+	g_return_val_if_fail(pool, FALSE);
+	g_return_val_if_fail(pool->scheme_name, FALSE);
+	g_return_val_if_fail(pool->name, FALSE);
+	g_return_val_if_fail(pool->fullname, FALSE);
+
+	/* Make sure no pools are registered under this name */
+	if (purple_privatekey_find_pool(pool->scheme_name, pool->name)) {
+		return FALSE;
+	}
+
+	/* Initialize the pool if needed */
+	if (pool->init) {
+		gboolean success;
+
+		success = pool->init();
+		if (!success)
+			return FALSE;
+	}
+
+	/* Register the Pool */
+	key_pools = g_list_prepend(key_pools, pool);
+
+	/* TODO: Emit a signal that the pool got registered */
+
+	PURPLE_DBUS_REGISTER_POINTER(pool, PurplePrivateKeyPool);
+	purple_signal_register(pool, /* Signals emitted from pool */
+			       "privatekey-stored",
+			       purple_marshal_VOID__POINTER_POINTER,
+			       NULL, /* No callback return value */
+			       2,    /* Two non-data arguments */
+			       purple_value_new(PURPLE_TYPE_SUBTYPE,
+						PURPLE_SUBTYPE_PRIVATEKEYPOOL),
+			       purple_value_new(PURPLE_TYPE_STRING));
+
+	purple_signal_register(pool, /* Signals emitted from pool */
+			       "privatekey-deleted",
+			       purple_marshal_VOID__POINTER_POINTER,
+			       NULL, /* No callback return value */
+			       2,    /* Two non-data arguments */
+			       purple_value_new(PURPLE_TYPE_SUBTYPE,
+						PURPLE_SUBTYPE_PRIVATEKEYPOOL),
+			       purple_value_new(PURPLE_TYPE_STRING));
+
+	purple_debug_info("privatekey",
+		  "PrivateKeyPool %s registered\n",
+		  pool->name);
+
+	return TRUE;
+}
+
+gboolean
+purple_privatekey_unregister_pool(PurplePrivateKeyPool *pool)
+{
+	if (NULL == pool) {
+		purple_debug_warning("privatekey",
+				     "Attempting to unregister NULL pool\n");
+		return FALSE;
+	}
+
+	/* Check that the pool is registered */
+	if (!g_list_find(key_pools, pool)) {
+		purple_debug_warning("privatekey",
+				     "Pool to unregister isn't registered!\n");
+
+		return FALSE;
+	}
+
+	/* Uninit the pool if needed */
+	PURPLE_DBUS_UNREGISTER_POINTER(pool);
+	if (pool->uninit) {
+		pool->uninit();
+	}
+
+	key_pools = g_list_remove(key_pools, pool);
+
+	/* TODO: Signalling? */
+	purple_signal_unregister(pool, "privatekey-stored");
+	purple_signal_unregister(pool, "privatekey-deleted");
+
+	purple_debug_info("privatekey",
+			  "PrivateKeyPool %s unregistered\n",
+			  pool->name);
+	return TRUE;
+}
+
+/****************************************************************************/
+/* Scheme-specific functions                                                */
+/****************************************************************************/
+
+#if 0
+static void
+purple_privatekey_display_x509(PurplePrivateKey *key)
+{
+}
+#endif
+
+void purple_privatekey_add_key_search_path(const char *path)
+{
+	if (g_list_find_custom(x509_user_key_paths, path, (GCompareFunc)strcmp))
+		return;
+	x509_user_key_paths = g_list_append(x509_user_key_paths, g_strdup(path));
+}
============================================================
--- /dev/null	
+++ libpurple/privatekey.h	1af336d71d294c9a3d2bf3504408958a23eb1d4d
@@ -0,0 +1,524 @@
+/**
+ * @file privatekey.h Private-Key API
+ * @ingroup core
+ * @see @ref privatekey-signals
+ * @since 2.2.0
+ */
+
+/*
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program 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.
+ *a
+ * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#ifndef _PURPLE_PRIVATEKEY_H
+#define _PURPLE_PRIVATEKEY_H
+
+#include <time.h>
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _PurplePrivateKey PurplePrivateKey;
+typedef struct _PurplePrivateKeyPool PurplePrivateKeyPool;
+typedef struct _PurplePrivateKeyScheme PurplePrivateKeyScheme;
+
+/** A private key instance
+ *
+ *  An opaque data structure representing a single private key under some
+ *  PrivateKeyScheme
+ */
+struct _PurplePrivateKey
+{
+	/** Scheme this private key is under */
+	PurplePrivateKeyScheme * scheme;
+	/** Opaque pointer to internal data */
+	gpointer data;
+};
+
+/**
+ * Database for retrieval or storage of PrivateKeys
+ *
+ * More or less a hash table; all lookups and writes are controlled by a string
+ * key.
+ */
+struct _PurplePrivateKeyPool
+{
+	/** Scheme this Pool operates for */
+	gchar *scheme_name;
+	/** Internal name to refer to the pool by */
+	gchar *name;
+
+	/** User-friendly name for this type
+	 *  ex: N_("SSL Servers")
+	 *  When this is displayed anywhere, it should be i18ned
+	 *  ex: _(pool->fullname)
+	 */
+	gchar *fullname;
+
+	/** Internal pool data */
+	gpointer data;
+
+	/**
+	 * Set up the Pool's internal state
+	 *
+	 * Upon calling purple_privatekey_register_pool() , this function will
+	 * be called. May be NULL.
+	 * @return TRUE if the initialization succeeded, otherwise FALSE
+	 */
+	gboolean (* init)(void);
+
+	/**
+	 * Uninit the Pool's internal state
+	 *
+	 * Will be called by purple_privatekey_unregister_pool() . May be NULL
+	 */
+	void (* uninit)(void);
+	
+	/**
+	 * Use password to protect the keys on disk on the pool using the password.
+	 *
+	 * @param password 
+	 */
+	//void (* set_password)(const gchar* password);
+
+	/** Check for presence of a private key in the pool using unique ID */
+	gboolean (* key_in_pool)(const gchar *id);
+
+	/** Retrieve a PurplePrivateKey from the pool */
+	PurplePrivateKey * (* get_key)(const gchar *id, const gchar* password);
+
+	/** Add a private key to the pool. Must overwrite any other
+	 *  keys sharing the same ID in the pool.
+	 *  @return TRUE if the operation succeeded, otherwise FALSE
+	 */
+	gboolean (* put_key)(const gchar *id, PurplePrivateKey *crt, const gchar* password);
+
+	/** Delete a key from the pool */
+	gboolean (* delete_key)(const gchar *id);
+
+	/** Returns a list of IDs stored in the pool */
+	GList * (* get_idlist)(void);
+
+	void (*_purple_reserved1)(void);
+	void (*_purple_reserved2)(void);
+	void (*_purple_reserved3)(void);
+	void (*_purple_reserved4)(void);
+};
+
+/** A private key type
+ *
+ *  A PrivateKeyScheme must implement all of the fields in the structure,
+ *  and register it using purple_privatekey_register_scheme()
+ *
+ *  There may be only ONE PrivateKeyScheme provided for each certificate
+ *  type, as specified by the "name" field.
+ */
+struct _PurplePrivateKeyScheme
+{
+	/** Name of the private key type
+	 *  ex: "x509", "pgp", etc.
+	 *  This must be globally unique - you may not register more than one
+	 *  PrivateKeyScheme of the same name at a time.
+	 */
+	gchar * name;
+
+	/** User-friendly name for this type
+	 *  ex: N_("X.509 PrivateKeys")
+	 *  When this is displayed anywhere, it should be i18ned
+	 *  ex: _(scheme->fullname)
+	 */
+	gchar * fullname;
+
+	/** Imports a private key from a file
+	 *
+	 *  @param filename   File to import the private key from
+	 *  @param password   Password to decrypt the key file.
+	 *  @return           Pointer to the newly allocated PrivateKey struct
+	 *                    or NULL on failure.
+	 */
+	PurplePrivateKey * (* import_key)(const gchar * filename, const gchar * password);
+
+	/**
+	 * Exports a private key to a file
+	 *
+	 * @param filename    File to export the private key to
+	 * @param key         PrivateKey to export
+	 * @param password    Password t encrypt the key file
+	 * @return TRUE if the export succeeded, otherwise FALSE
+	 * @see purple_privatekey_export()
+	 */
+	gboolean (* export_key)(const gchar *filename, PurplePrivateKey *key, const gchar* password);
+
+	/**
+	 * Duplicates a private key
+	 *
+	 * Keys are generally assumed to be read-only, so feel free to
+	 * do any sort of reference-counting magic you want here. If this ever
+	 * changes, please remember to change the magic accordingly.
+	 * @return Reference to the new copy
+	 */
+	PurplePrivateKey * (* copy_key)(PurplePrivateKey *key);
+
+	/** Destroys and frees a PrviateKey structure
+	 *
+	 *  Destroys a PrivateKey's internal data structures and calls
+	 *  free(key)
+	 *
+	 *  @param key  PrviateKey instance to be destroyed. It WILL NOT be
+	 *              destroyed if it is not of the correct
+	 *              PrivateKeyScheme. Can be NULL
+	 */
+	void (* destroy_key)(PurplePrivateKey * key);
+
+	/**
+	 * Retrieves a unique key identifier
+	 *
+	 * @param key PrivateKey instance
+	 * @return Newly allocated string that can be used to uniquely
+	 *         identify the key.
+	 */
+	gchar* (* get_unique_id)(PurplePrivateKey *key);
+
+	void (*_purple_reserved1)(void);
+	void (*_purple_reserved2)(void);
+	void (*_purple_reserved3)(void);
+};
+
+/*@}*/
+
+/*****************************************************************************/
+/** @name Private Key Functions                                              */
+/*****************************************************************************/
+/*@{*/
+
+/**
+ * Makes a duplicate of a private key
+ *
+ * @param key        Instance to duplicate
+ * @return Pointer to new instance
+ */
+PurplePrivateKey *
+purple_privatekey_copy(PurplePrivateKey *key);
+
+/**
+ * Destroys and free()'s a PrivateKey
+ *
+ * @param key        Instance to destroy. May be NULL.
+ */
+void
+purple_privatekey_destroy (PurplePrivateKey *key);
+
+/**
+ * Imports a PurplePrivateKey from a file
+ *
+ * @param scheme      Scheme to import under
+ * @param filename    File path to import from
+ * @param password    Password to protecting the key on disk
+ * @return Pointer to a new PurplePrivateKey, or NULL on failure
+ */
+PurplePrivateKey *
+purple_privatekey_import(PurplePrivateKeyScheme *scheme, const gchar *filename, const gchar *password);
+
+/**
+ * Exports a PurplePrivateKey to a file
+ *
+ * @param filename    File to export the key to
+ * @param key         Key to export
+ * @param password    Password to protect the key on disk
+ * @return TRUE if the export succeeded, otherwise FALSE
+ */
+gboolean
+purple_privatekey_export(const gchar *filename, PurplePrivateKey *key, const gchar *password);
+
+/**
+ * Get a unique identifier for the private key
+ *
+ * @param key        PrivateKey instance
+ * @return String representing the key uniquely. Must be g_free()'ed
+ */
+gchar *
+purple_privatekey_get_unique_id(PurplePrivateKey *key);
+
+/*@}*/
+
+/*****************************************************************************/
+/** @name Private Key Pool Functions                                         */
+/*****************************************************************************/
+/*@{*/
+/**
+ * Helper function for generating file paths in ~/.purple/certificates for
+ * PrivateKeyPools that use them.
+ *
+ * All components will be escaped for filesystem friendliness.
+ *
+ * @param pool   PrivateKeyPool to build a path for
+ * @param id     Key to look up a PrivateKey by. May be NULL.
+ * @return A newly allocated path of the form
+ *         ~/.purple/certificates/scheme_name/pool_name/unique_id
+ */
+gchar *
+purple_privatekey_pool_mkpath(PurplePrivateKeyPool *pool, const gchar *id);
+
+/**
+ * Determines whether a pool can be used.
+ *
+ * Checks whether the associated PrivateKeyScheme is loaded.
+ *
+ * @param pool   Pool to check
+ *
+ * @return TRUE if the pool can be used, otherwise FALSE
+ */
+gboolean
+purple_privatekey_pool_usable(PurplePrivateKeyPool *pool);
+
+gboolean
+purple_privatekey_pool_set_password(PurplePrivateKeyPool *pool, const gchar* password);
+
+const gchar* purple_privatekey_pool_get_password(PurplePrivateKeyPool *pool);
+
+/**
+ * Looks up the scheme the pool operates under
+ *
+ * @param pool   Pool to get the scheme of
+ *
+ * @return Pointer to the pool's scheme, or NULL if it isn't loaded.
+ * @see purple_privatekey_pool_usable()
+ */
+PurplePrivateKeyScheme *
+purple_privatekey_pool_get_scheme(PurplePrivateKeyPool *pool);
+
+/**
+ * Check for presence of an ID in a pool.
+ * @param pool   Pool to look in
+ * @param id     ID to look for
+ * @return TRUE if the ID is in the pool, else FALSE
+ */
+gboolean
+purple_privatekey_pool_contains(PurplePrivateKeyPool *pool, const gchar *id);
+
+
+typedef void (*PurplePrivateKeyPoolCancelCb)(void* data);
+typedef void (*PurplePrivateKeyPoolRetrieveRequestOkCb)(PurplePrivateKey *key, void* data);
+typedef void (*PurplePrivateKeyPoolStoreRequestOkCb)(gboolean result, void* data);
+
+/**
+ * Retrieve a key from a pool and prompt user for the password protecting the key.
+ *
+ * @param pool      Pool to get key from
+ * @param id        ID of key to retrieve
+ * @param ok_cb     Called if the user clicks ok in the password prompt.
+ *                  The key parameter to the callback is non-null if the key was successfully
+ *                  retrieved from the pool and null otherwise.
+ * @param cancel_cb Called if the user cancels the password dialog.
+ * @param user_data Pointer to caller defined data structure to be passed to callbacks.
+ */
+void 
+purple_privatekey_pool_retrieve_request(
+	PurplePrivateKeyPool *pool, const gchar *id,
+	GCallback ok_cb,
+	GCallback cancel_cb,
+	void* user_data);
+
+/**
+ * Store a key in the given pool and prompt user for a password to protect the key.
+ *
+ * @param pool      Pool to store key in
+ * @param id        ID of key to store
+ * @param key       Key to store.
+ * @param ok_cb     Called if the user clicks ok in the password prompt.
+ *                  The result parameter to the callback is true if the key was successfully
+ *                  stored in the pool and false otherwise.
+ * @param cancel_cb Called if the user cancels the password dialog.
+ * @param user_data Pointer to caller defined data structure to be passed to callbacks.
+ */
+void 
+purple_privatekey_pool_store_request(
+	PurplePrivateKeyPool *pool, const gchar *id, PurplePrivateKey *key,
+	GCallback ok_cb,
+	GCallback cancel_cb,
+	void* user_data);
+
+/**
+ * Retrieve a key from a pool.
+ * @param pool     Pool to fish in
+ * @param id       ID to look up
+ * @param password Password used to encrypt key
+ * @return Retrieved key, or NULL if it wasn't there
+ */
+PurplePrivateKey *
+purple_privatekey_pool_retrieve(PurplePrivateKeyPool *pool, const gchar *id, const gchar *password);
+
+/**
+ * Add a key to a pool
+ *
+ * Any pre-existing key of the same ID will be overwritten.
+ *
+ * @param pool     Pool to add to
+ * @param id       ID to store the key with
+ * @param key      Key to store
+ * @param password Password to decrypt key
+ * @return TRUE if the operation succeeded, otherwise FALSE
+ */
+gboolean
+purple_privatekey_pool_store(PurplePrivateKeyPool *pool, const gchar *id, PurplePrivateKey *key, const gchar *password);
+
+/**
+ * Remove a key from a pool
+ *
+ * @param pool   Pool to remove from
+ * @param id     ID to remove
+ * @return TRUE if the operation succeeded, otherwise FALSE
+ */
+gboolean
+purple_privatekey_pool_delete(PurplePrivateKeyPool *pool, const gchar *id);
+
+/**
+ * Get the list of IDs currently in the pool.
+ *
+ * @param pool   Pool to enumerate
+ * @return GList pointing to newly-allocated id strings. Free using
+ *         purple_privatekey_pool_destroy_idlist()
+ */
+GList *
+purple_privatekey_pool_get_idlist(PurplePrivateKeyPool *pool);
+
+/**
+ * Destroys the result given by purple_privatekey_pool_get_idlist()
+ *
+ * @param idlist ID List to destroy
+ */
+void
+purple_privatekey_pool_destroy_idlist(GList *idlist);
+
+/*@}*/
+
+/*****************************************************************************/
+/** @name PrivateKey Subsystem API                                           */
+/*****************************************************************************/
+/*@{*/
+
+/**
+ * Initialize the private key system
+ */
+void
+purple_privatekey_init(void);
+
+/**
+ * Un-initialize the private key system
+ */
+void
+purple_privatekey_uninit(void);
+
+/**
+ * Get the PrivateKey subsystem handle for signalling purposes
+ */
+gpointer
+purple_privatekey_get_handle(void);
+
+/** Look up a registered PrivateKeyScheme by name
+ * @param name   The scheme name. Case insensitive.
+ * @return Pointer to the located Scheme, or NULL if it isn't found.
+ */
+PurplePrivateKeyScheme *
+purple_privatekey_find_scheme(const gchar *name);
+
+/**
+ * Get all registered PrivateKeySchemes
+ *
+ * @return GList pointing to all registered PrivateKeySchemes . This value
+ *         is owned by libpurple
+ */
+GList *
+purple_privatekey_get_schemes(void);
+
+/** Register a PrivateKeyScheme with libpurple
+ *
+ * No two schemes can be registered with the same name; this function enforces
+ * that.
+ *
+ * @param scheme  Pointer to the scheme to register.
+ * @return TRUE if the scheme was successfully added, otherwise FALSE
+ */
+gboolean
+purple_privatekey_register_scheme(PurplePrivateKeyScheme *scheme);
+
+/** Unregister a PrivateKeyScheme from libpurple
+ *
+ * @param scheme    Scheme to unregister.
+ *                  If the scheme is not registered, this is a no-op.
+ *
+ * @return TRUE if the unregister completed successfully
+ */
+gboolean
+purple_privatekey_unregister_scheme(PurplePrivateKeyScheme *scheme);
+
+/** Look up a registered PurplePrivateKeyPool by scheme and name
+ * @param scheme_name  Scheme name. Case insensitive.
+ * @param pool_name    Pool name. Case insensitive.
+ * @return Pointer to the located Pool, or NULL if it isn't found.
+ */
+PurplePrivateKeyPool *
+purple_privatekey_find_pool(const gchar *scheme_name, const gchar *pool_name);
+
+/**
+ * Get the list of registered Pools
+ *
+ * @return GList of all registered PurplePrivateKeyPools. This value
+ *         is owned by libpurple
+ */
+GList *
+purple_privatekey_get_pools(void);
+
+/**
+ * Register a PrivateKeyPool with libpurple and call its init function
+ *
+ * @param pool   Pool to register.
+ * @return TRUE if the register succeeded, otherwise FALSE
+ */
+gboolean
+purple_privatekey_register_pool(PurplePrivateKeyPool *pool);
+
+/**
+ * Unregister a PrivateKeyPool with libpurple and call its uninit function
+ *
+ * @param pool   Pool to unregister.
+ * @return TRUE if the unregister succeeded, otherwise FALSE
+ */
+gboolean
+purple_privatekey_unregister_pool(PurplePrivateKeyPool *pool);
+
+/*@}*/
+
+/**
+ * Add a search path for keys.
+ *
+ * @param path   Path to search for keys.
+ */
+void purple_privatekey_add_key_search_path(const char *path);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _PURPLE_PRIVATEKEY_H */


More information about the Commits mailing list