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