gnome-keyring r1419 - in trunk: . pkcs11 pkcs11/gck pkcs11/ssh-agent
- From: nnielsen svn gnome org
- To: svn-commits-list gnome org
- Subject: gnome-keyring r1419 - in trunk: . pkcs11 pkcs11/gck pkcs11/ssh-agent
- Date: Sat, 3 Jan 2009 23:00:21 +0000 (UTC)
Author: nnielsen
Date: Sat Jan 3 23:00:21 2009
New Revision: 1419
URL: http://svn.gnome.org/viewvc/gnome-keyring?rev=1419&view=rev
Log:
* pkcs11/gck/gck-memory-store.c:
* pkcs11/gck/gck-object.c:
* pkcs11/gck/gck-session.c:
* pkcs11/ssh-agent/gck-ssh-agent.c: (added)
* pkcs11/ssh-agent/gck-ssh-agent.h: (added)
* pkcs11/ssh-agent/gck-ssh-agent-ops.c: (added)
* pkcs11/ssh-agent/gck-ssh-agent-private.h: (added)
* pkcs11/ssh-agent/gck-ssh-agent-proto.c: (added)
* pkcs11/ssh-agent/gck-ssh-agent-standalone.c: (added)
* pkcs11/ssh-agent/Makefile.am: (added)
* configure.in: Add PKCS#11 based SSH agent.
Added:
trunk/pkcs11/ssh-agent/ (props changed)
trunk/pkcs11/ssh-agent/Makefile.am
trunk/pkcs11/ssh-agent/gck-ssh-agent-ops.c
trunk/pkcs11/ssh-agent/gck-ssh-agent-private.h
trunk/pkcs11/ssh-agent/gck-ssh-agent-proto.c
trunk/pkcs11/ssh-agent/gck-ssh-agent-standalone.c
trunk/pkcs11/ssh-agent/gck-ssh-agent.c
trunk/pkcs11/ssh-agent/gck-ssh-agent.h
Modified:
trunk/ChangeLog
trunk/configure.in
trunk/pkcs11/Makefile.am
trunk/pkcs11/gck/gck-memory-store.c
trunk/pkcs11/gck/gck-object.c
trunk/pkcs11/gck/gck-session.c
Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in (original)
+++ trunk/configure.in Sat Jan 3 23:00:21 2009
@@ -517,6 +517,7 @@
pkcs11/gck/tests/Makefile
pkcs11/roots/Makefile
pkcs11/roots/tests/Makefile
+pkcs11/ssh-agent/Makefile
pkcs11/ssh-keys/Makefile
pkcs11/ssh-keys/tests/Makefile
pkcs11/rpc/Makefile
Modified: trunk/pkcs11/Makefile.am
==============================================================================
--- trunk/pkcs11/Makefile.am (original)
+++ trunk/pkcs11/Makefile.am Sat Jan 3 23:00:21 2009
@@ -47,5 +47,5 @@
TESTS_DIR =
endif
-SUBDIRS = . rpc gck ssh-keys $(ROOTS_DIR) $(TESTS_DIR)
+SUBDIRS = . rpc gck ssh-agent ssh-keys $(ROOTS_DIR) $(TESTS_DIR)
Modified: trunk/pkcs11/gck/gck-memory-store.c
==============================================================================
--- trunk/pkcs11/gck/gck-memory-store.c (original)
+++ trunk/pkcs11/gck/gck-memory-store.c Sat Jan 3 23:00:21 2009
@@ -163,7 +163,7 @@
if (at != NULL && gck_attribute_equal (at, attr))
return;
- revert = g_new0 (Revert, 1);
+ revert = g_slice_new0 (Revert);
revert->attributes = g_hash_table_ref (attributes);
revert->type = attr->type;
revert->attr = at;
Modified: trunk/pkcs11/gck/gck-object.c
==============================================================================
--- trunk/pkcs11/gck/gck-object.c (original)
+++ trunk/pkcs11/gck/gck-object.c Sat Jan 3 23:00:21 2009
@@ -164,6 +164,9 @@
if (self->pv->manager)
gck_manager_unregister_object (self->pv->manager, self);
g_assert (self->pv->manager == NULL);
+
+ g_object_set (self, "store", NULL, NULL);
+ g_assert (self->pv->store == NULL);
G_OBJECT_CLASS (gck_object_parent_class)->dispose (obj);
}
Modified: trunk/pkcs11/gck/gck-session.c
==============================================================================
--- trunk/pkcs11/gck/gck-session.c (original)
+++ trunk/pkcs11/gck/gck-session.c Sat Jan 3 23:00:21 2009
@@ -920,10 +920,10 @@
CK_ULONG count)
{
gboolean also_private;
+ CK_RV rv = CKR_OK;
CK_BBOOL token;
GArray *found;
gboolean all;
- CK_RV rv;
g_return_val_if_fail (GCK_IS_SESSION (self), CKR_SESSION_HANDLE_INVALID);
if (!(template || !count))
Added: trunk/pkcs11/ssh-agent/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/pkcs11/ssh-agent/Makefile.am Sat Jan 3 23:00:21 2009
@@ -0,0 +1,48 @@
+
+INCLUDES = \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/pkcs11 \
+ $(GOBJECT_CFLAGS) \
+ $(GLIB_CFLAGS)
+
+
+# ------------------------------------------------------------------------------
+# The ssh-agent component code
+
+noinst_LTLIBRARIES = \
+ libgck-ssh-agent.la
+
+libgck_ssh_agent_la_SOURCES = \
+ gck-ssh-agent.c gck-ssh-agent.h \
+ gck-ssh-agent-private.h \
+ gck-ssh-agent-ops.c \
+ gck-ssh-agent-proto.c
+
+# ------------------------------------------------------------------------------
+# Standalone binary
+
+noinst_PROGRAMS = \
+ gck-ssh-agent-standalone
+
+gck_ssh_agent_standalone_SOURCES = \
+ gck-ssh-agent-standalone.c
+
+gck_ssh_agent_standalone_LDADD = \
+ libgck-ssh-agent.la \
+ $(top_builddir)/gp11/libgp11.la \
+ $(top_builddir)/common/libgkr-module-common.la \
+ $(GOBJECT_LIBS) \
+ $(GTHREAD_LIBS) \
+ $(LIBGCRYPT_LIBS) \
+ $(GLIB_LIBS)
+
+# -------------------------------------------------------------------------------
+
+# if WITH_TESTS
+# TESTS_DIR = tests
+# else
+# TESTS_DIR =
+# endif
+#
+# SUBDIRS = . $(TESTS_DIR)
Added: trunk/pkcs11/ssh-agent/gck-ssh-agent-ops.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/ssh-agent/gck-ssh-agent-ops.c Sat Jan 3 23:00:21 2009
@@ -0,0 +1,1152 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gkr-ssh-daemon-ops.h - SSH agent operations
+
+ Copyright (C) 2007 Stefan Walter
+
+ Gnome keyring is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ Gnome keyring is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "config.h"
+
+#include "gck-ssh-agent-private.h"
+
+#include "gp11/gp11.h"
+
+#include "common/gkr-secure-memory.h"
+
+#include <glib.h>
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+
+
+#define V1_LABEL "SSH1 RSA Key"
+
+/* ---------------------------------------------------------------------------- */
+
+static void
+copy_attribute (GP11Attributes *original, CK_ATTRIBUTE_TYPE type, GP11Attributes *dest)
+{
+ GP11Attribute *attr;
+
+ g_assert (original);
+ g_assert (dest);
+
+ attr = gp11_attributes_find (original, type);
+ if (attr)
+ gp11_attributes_add (dest, attr);
+}
+
+static GList*
+find_keys_for_attributes (GP11Session *session, GP11Attributes *attrs,
+ CK_OBJECT_CLASS klass)
+{
+ GP11Attributes *search;
+ GError *error = NULL;
+ gulong key_type;
+ GList *keys;
+
+ g_assert (GP11_IS_SESSION (session));
+ g_assert (attrs);
+
+ /* Determine the key type */
+ if (!gp11_attributes_find_ulong (attrs, CKA_KEY_TYPE, &key_type))
+ g_return_val_if_reached (NULL);
+
+ search = gp11_attributes_new ();
+ gp11_attributes_add_ulong (search, CKA_CLASS, klass);
+ copy_attribute (attrs, CKA_KEY_TYPE, search);
+ copy_attribute (attrs, CKA_TOKEN, search);
+
+ switch (key_type) {
+ case CKK_RSA:
+ copy_attribute (attrs, CKA_MODULUS, search);
+ copy_attribute (attrs, CKA_PUBLIC_EXPONENT, search);
+ break;
+
+ case CKK_DSA:
+ copy_attribute (attrs, CKA_PRIME, search);
+ copy_attribute (attrs, CKA_SUBPRIME, search);
+ copy_attribute (attrs, CKA_BASE, search);
+ copy_attribute (attrs, CKA_VALUE, search);
+ break;
+
+ default:
+ g_return_val_if_reached (NULL);
+ break;
+ }
+
+ keys = gp11_session_find_objects_full (session, search, NULL, &error);
+ gp11_attributes_unref (search);
+
+ if (error) {
+ g_warning ("couldn't search for matching keys: %s", error->message);
+ g_clear_error (&error);
+ return NULL;
+ }
+
+ return keys;
+}
+
+static GP11Object*
+public_key_for_attributes (GP11Session *session, GP11Attributes *attrs)
+{
+ GList *keys;
+ GP11Object *object;
+
+ g_assert (GP11_IS_SESSION (session));
+ g_assert (attrs);
+
+ keys = find_keys_for_attributes (session, attrs, CKO_PUBLIC_KEY);
+ if (!keys) {
+ g_message ("couldn't find matching public key");
+ return NULL;
+ }
+
+ object = g_object_ref (keys->data);
+ gp11_list_unref_free (keys);
+ return object;
+}
+
+static GP11Object*
+private_key_for_public (GP11Session *session, GP11Object *pub)
+{
+ GP11Object *priv = NULL;
+ GP11Attributes *attrs;
+ GError *error = NULL;
+ GList *objects;
+
+ g_assert (GP11_IS_SESSION (session));
+ g_assert (GP11_IS_OBJECT (pub));
+
+ gp11_object_set_session (pub, session);
+ attrs = gp11_object_get (pub, &error, CKA_ID, CKA_TOKEN,
+ GP11_INVALID);
+ if (error) {
+ g_warning ("couldn't lookup attributes for key: %s", error->message);
+ g_clear_error (&error);
+ return NULL;
+ }
+
+ /* Search for exactly the same attributes but with a private key class */
+ gp11_attributes_add_ulong (attrs, CKA_CLASS, CKO_PRIVATE_KEY);
+ objects = gp11_session_find_objects_full (session, attrs, NULL, &error);
+ gp11_attributes_unref (attrs);
+
+ if (error) {
+ g_warning ("couldn't search for related key: %s", error->message);
+ g_clear_error (&error);
+ return NULL;
+ }
+
+ if (objects)
+ priv = g_object_ref (objects->data);
+ gp11_list_unref_free (objects);
+
+ return priv;
+}
+
+static GP11Object*
+private_key_for_attributes (GP11Session *session, GP11Attributes *attrs)
+{
+ GP11Object *pub, *prv;
+
+ g_assert (GP11_IS_SESSION (session));
+ g_assert (attrs);
+
+ pub = public_key_for_attributes (session, attrs);
+ if (pub == NULL)
+ return NULL;
+
+ prv = private_key_for_public (session, pub);
+ g_object_unref (pub);
+
+ if (prv == NULL) {
+ g_message ("couldn't find matching private key");
+ return NULL;
+ }
+
+ return prv;
+}
+
+static void
+remove_key_pair (GP11Session *session, GP11Object *priv, GP11Object *pub)
+{
+ GError *error = NULL;
+
+ g_assert (GP11_IS_SESSION (session));
+
+ if (priv != NULL) {
+ gp11_object_set_session (priv, session);
+ gp11_object_destroy (priv, &error);
+
+ if (error) {
+ if (error->code != CKR_OBJECT_HANDLE_INVALID)
+ g_warning ("couldn't remove ssh private key: %s", error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ if (pub != NULL) {
+ gp11_object_set_session (pub, session);
+ gp11_object_destroy (pub, &error);
+
+ if (error) {
+ if (error->code != CKR_OBJECT_HANDLE_INVALID)
+ g_warning ("couldn't remove ssh public key: %s", error->message);
+ g_clear_error (&error);
+ }
+ }
+}
+
+static void
+lock_key_pair (GP11Session *session, GP11Object *priv, GP11Object *pub)
+{
+ /* TODO: Implement */
+}
+
+static void
+remove_by_public_key (GP11Session *session, GP11Object *pub)
+{
+ GP11Object *priv;
+
+ g_assert (GP11_IS_SESSION (session));
+ g_assert (GP11_IS_OBJECT (pub));
+
+ priv = private_key_for_public (session, pub);
+ remove_key_pair (session, priv, pub);
+ if (pub != NULL)
+ g_object_unref (priv);
+}
+
+static void
+remove_or_lock_by_public_key (GP11Session *session, GP11Object *pub)
+{
+ GP11Attributes *attrs;
+ GError *error = NULL;
+ GList *objects;
+ gboolean token;
+ gchar *label;
+
+ g_assert (GP11_IS_SESSION (session));
+ g_assert (GP11_IS_OBJECT (pub));
+
+ gp11_object_set_session (pub, session);
+ attrs = gp11_object_get (pub, &error,
+ CKA_LABEL, CKA_ID, CKA_TOKEN,
+ GP11_INVALID);
+ if (error) {
+ g_warning ("couldn't lookup attributes for key: %s", error->message);
+ g_clear_error (&error);
+ return;
+ }
+
+ /* Skip over SSH V1 keys */
+ if (gp11_attributes_find_string (attrs, CKA_LABEL, &label)) {
+ if (label && strcmp (label, V1_LABEL) == 0) {
+ gp11_attributes_unref (attrs);
+ g_free (label);
+ return;
+ }
+ }
+
+ /* Lock token objects, remove session objects */
+ if (!gp11_attributes_find_boolean (attrs, CKA_TOKEN, &token))
+ token = FALSE;
+
+ /* Search for exactly the same attributes but with a private key class */
+ gp11_attributes_add_ulong (attrs, CKA_CLASS, CKO_PRIVATE_KEY);
+ objects = gp11_session_find_objects_full (session, attrs, NULL, &error);
+ gp11_attributes_unref (attrs);
+
+ if (error) {
+ g_warning ("couldn't search for related key: %s", error->message);
+ g_clear_error (&error);
+ return;
+ }
+
+ /* Lock the token objects */
+ if (token && objects) {
+ lock_key_pair (session, objects->data, pub);
+ } else if (!token) {
+ remove_key_pair (session, objects->data, pub);
+ }
+
+ gp11_list_unref_free (objects);
+}
+
+static gboolean
+create_key_pair (GP11Session *session, GP11Attributes *priv, GP11Attributes *pub)
+{
+ GP11Object *priv_key, *pub_key;
+ GError *error = NULL;
+
+ g_assert (GP11_IS_SESSION (session));
+ g_assert (priv);
+ g_assert (pub);
+
+ priv_key = gp11_session_create_object_full (session, priv, NULL, &error);
+ if (error) {
+ g_warning ("couldn't create session private key: %s", error->message);
+ g_clear_error (&error);
+ return FALSE;
+ }
+
+ pub_key = gp11_session_create_object_full (session, pub, NULL, &error);
+ if (error) {
+ g_warning ("couldn't create session public key: %s", error->message);
+ g_clear_error (&error);
+
+ /* Failed, so remove private as well */
+ gp11_object_set_session (priv_key, session);
+ gp11_object_destroy (priv_key, NULL);
+ g_object_unref (priv_key);
+
+ return FALSE;
+ }
+
+ g_object_unref (pub_key);
+ g_object_unref (priv_key);
+
+ return TRUE;
+}
+
+static void
+destroy_replaced_keys (GP11Session *session, GList *keys)
+{
+ GError *error = NULL;
+ GList *l;
+
+ g_assert (GP11_IS_SESSION (session));
+
+ for (l = keys; l; l = g_list_next (l)) {
+ gp11_object_set_session (l->data, session);
+ if (!gp11_object_destroy (l->data, &error)) {
+ if (error->code != CKR_OBJECT_HANDLE_INVALID)
+ g_warning ("couldn't delete a SSH key we replaced: %s", error->message);
+ g_clear_error (&error);
+ }
+ }
+}
+
+static gboolean
+replace_key_pair (GP11Session *session, GP11Attributes *priv, GP11Attributes *pub)
+{
+ GList *priv_prev, *pub_prev;
+
+ g_assert (GP11_IS_SESSION (session));
+ g_assert (priv);
+ g_assert (pub);
+
+ gp11_attributes_add_boolean (priv, CKA_TOKEN, FALSE);
+ gp11_attributes_add_boolean (pub, CKA_TOKEN, FALSE);
+
+ /* Find the previous keys that match the same description */
+ priv_prev = find_keys_for_attributes (session, priv, CKO_PRIVATE_KEY);
+ pub_prev = find_keys_for_attributes (session, pub, CKO_PUBLIC_KEY);
+
+ /* Now try and create the new keys */
+ if (create_key_pair (session, priv, pub)) {
+
+ /* Delete the old keys */
+ destroy_replaced_keys (session, priv_prev);
+ destroy_replaced_keys (session, pub_prev);
+ }
+
+ gp11_list_unref_free (priv_prev);
+ gp11_list_unref_free (pub_prev);
+
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------------
+ * OPERATIONS
+ */
+
+static gboolean
+op_add_identity (GckSshAgentCall *call)
+{
+ GP11Attributes *pub;
+ GP11Attributes *priv;
+ GP11Session *session;
+ gchar *stype = NULL;
+ gchar *comment = NULL;
+ gboolean ret;
+ gulong algo;
+ gsize offset;
+
+ if (!gkr_buffer_get_string (call->req, 5, &offset, &stype, (GkrBufferAllocator)g_realloc))
+ return FALSE;
+
+ algo = gck_ssh_agent_proto_keytype_to_algo (stype);
+ if (algo == (gulong)-1) {
+ g_warning ("unsupported algorithm from SSH: %s", stype);
+ g_free (stype);
+ return FALSE;
+ }
+
+ g_free (stype);
+ priv = gp11_attributes_new_full ((GP11Allocator)gkr_secure_realloc);
+ pub = gp11_attributes_new_full (g_realloc);
+
+ switch (algo) {
+ case CKK_RSA:
+ ret = gck_ssh_agent_proto_read_pair_rsa (call->req, &offset, priv, pub);
+ break;
+ case CKK_DSA:
+ ret = gck_ssh_agent_proto_read_pair_dsa (call->req, &offset, priv, pub);
+ break;
+ default:
+ g_assert_not_reached ();
+ return FALSE;
+ }
+
+ if (!ret) {
+ g_warning ("couldn't read incoming SSH private key");
+ gp11_attributes_unref (pub);
+ gp11_attributes_unref (priv);
+ return FALSE;
+ }
+
+
+ /* TODO: Blinding? See ssh-agent.c */
+
+ /* Get the comment */
+ if (!gkr_buffer_get_string (call->req, offset, &offset, &comment, (GkrBufferAllocator)g_realloc)) {
+ gp11_attributes_unref (pub);
+ gp11_attributes_unref (priv);
+ return FALSE;
+ }
+
+ gp11_attributes_add_string (pub, CKA_LABEL, comment);
+ gp11_attributes_add_string (priv, CKA_LABEL, comment);
+ g_free (comment);
+
+ /*
+ * This is the session that owns these objects. Only
+ * one thread can use it at a time.
+ */
+
+ session = gck_ssh_agent_checkout_main_session ();
+ g_return_val_if_fail (session, FALSE);
+
+ ret = replace_key_pair (session, priv, pub);
+
+ gck_ssh_agent_checkin_main_session (session);
+
+ gp11_attributes_unref (priv);
+ gp11_attributes_unref (pub);
+
+ gkr_buffer_add_byte (call->resp, ret ? GCK_SSH_RES_SUCCESS : GCK_SSH_RES_FAILURE);
+ return TRUE;
+}
+
+static gboolean
+op_v1_add_identity (GckSshAgentCall *call)
+{
+ GP11Attributes *pub, *priv;
+ GP11Session *session;
+ gboolean ret;
+ gsize offset = 5;
+ guint32 unused;
+
+ if (!gkr_buffer_get_uint32 (call->req, offset, &offset, &unused))
+ return FALSE;
+
+ priv = gp11_attributes_new_full ((GP11Allocator)gkr_secure_realloc);
+ pub = gp11_attributes_new_full (g_realloc);
+
+ if (!gck_ssh_agent_proto_read_pair_v1 (call->req, &offset, priv, pub)) {
+ g_warning ("couldn't read incoming SSH private key");
+ gp11_attributes_unref (pub);
+ gp11_attributes_unref (priv);
+ return FALSE;
+ }
+
+ gp11_attributes_add_string (priv, CKA_LABEL, V1_LABEL);
+ gp11_attributes_add_string (pub, CKA_LABEL, V1_LABEL);
+
+ /*
+ * This is the session that owns these objects. Only
+ * one thread can use it at a time.
+ */
+
+ session = gck_ssh_agent_checkout_main_session ();
+ g_return_val_if_fail (session, FALSE);
+
+ ret = replace_key_pair (session, priv, pub);
+
+ gck_ssh_agent_checkin_main_session (session);
+
+ gp11_attributes_unref (priv);
+ gp11_attributes_unref (pub);
+
+ gkr_buffer_add_byte (call->resp, ret ? GCK_SSH_RES_SUCCESS : GCK_SSH_RES_FAILURE);
+ return TRUE;
+}
+
+static gboolean
+op_request_identities (GckSshAgentCall *call)
+{
+ GList *objects, *l;
+ GList *all_attrs;
+ GError *error = NULL;
+ GP11Attributes *attrs;
+ GP11Object *pub, *priv;
+ GP11Attribute *label;
+ gsize blobpos;
+ gchar *comment;
+
+ /* Find all the private keys*/
+ /* TODO: Check SSH purpose */
+ objects = gp11_session_find_objects (call->session, &error,
+ CKA_CLASS, GP11_ULONG, CKO_PUBLIC_KEY,
+ GP11_INVALID);
+ if (error) {
+ g_warning ("couldn't search for private keys: %s", error->message);
+ g_clear_error (&error);
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_FAILURE);
+ return TRUE;
+ }
+
+ /* Find the public key, and load attributes for each public */
+ for (l = objects; l; l = g_list_next (l)) {
+ pub = l->data;
+
+ /* For each public key we find a private key. */
+ priv = private_key_for_public (call->session, pub);
+ if (priv == NULL)
+ continue;
+ g_object_unref (priv);
+
+ /*
+ * Now get attributes for that public key. Any attributes not present on
+ * the key will be returned as invalid.
+ */
+ gp11_object_set_session (pub, call->session);
+ attrs = gp11_object_get (pub, &error, CKA_LABEL, CKA_KEY_TYPE, CKA_MODULUS,
+ CKA_PUBLIC_EXPONENT, CKA_PRIME, CKA_SUBPRIME, CKA_BASE,
+ CKA_VALUE, CKA_CLASS, CKA_MODULUS_BITS, -1);
+
+ if (error) {
+ g_warning ("error retrieving attributes for public key: %s", error->message);
+ g_clear_error (&error);
+ continue;
+ }
+
+ /* Dig out the label, and see if it's not v1, skip if so */
+ label = gp11_attributes_find (attrs, CKA_LABEL);
+ if (label != NULL) {
+ if (label->length == strlen (V1_LABEL) &&
+ strncmp ((gchar*)label->value, V1_LABEL, label->length) == 0) {
+ gp11_attributes_unref (attrs);
+ continue;
+ }
+ }
+
+ /* We have this one squared away and ready to send */
+ all_attrs = g_list_prepend (all_attrs, attrs);
+ }
+
+ gp11_list_unref_free (objects);
+
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_IDENTITIES_ANSWER);
+ gkr_buffer_add_uint32 (call->resp, g_list_length (all_attrs));
+
+ for (l = all_attrs; l; l = g_list_next (l)) {
+
+ attrs = l->data;
+
+ /* Dig out the label */
+ if (!gp11_attributes_find_string (attrs, CKA_LABEL, &comment))
+ comment = NULL;
+
+ /* Add a space for the key blob length */
+ blobpos = call->resp->len;
+ gkr_buffer_add_uint32 (call->resp, 0);
+
+ /* Write out the key */
+ gck_ssh_agent_proto_write_public (call->resp, attrs);
+
+ /* Write back the blob length */
+ gkr_buffer_set_uint32 (call->resp, blobpos, (call->resp->len - blobpos) - 4);
+
+ /* And now a per key comment */
+ gkr_buffer_add_string (call->resp, comment ? comment : "");
+
+ g_free (comment);
+ gp11_attributes_unref (attrs);
+ }
+
+ g_list_free (all_attrs);
+
+ return TRUE;
+}
+
+static gboolean
+op_v1_request_identities (GckSshAgentCall *call)
+{
+ GList *objects, *l;
+ GError *error = NULL;
+ GList *all_attrs;
+ GP11Attributes *attrs;
+ GP11Object *pub, *priv;
+
+ /* Find all the keys not on token, and are V1 */
+ /* TODO: Check SSH purpose */
+ objects = gp11_session_find_objects (call->session, &error,
+ CKA_CLASS, GP11_ULONG, CKO_PUBLIC_KEY,
+ CKA_TOKEN, GP11_BOOLEAN, FALSE,
+ CKA_LABEL, GP11_STRING, V1_LABEL,
+ GP11_INVALID);
+ if (error) {
+ g_warning ("couldn't search for public keys: %s", error->message);
+ g_clear_error (&error);
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_FAILURE);
+ return TRUE;
+ }
+
+ /* Find the public key, and load attributes for each public */
+ for (l = objects; l; l = g_list_next (l)) {
+ pub = l->data;
+
+ /* For each public key we find a private key. */
+ priv = private_key_for_public (call->session, pub);
+ if (priv == NULL)
+ continue;
+ g_object_unref (priv);
+
+ /*
+ * Now get attributes for that public key. Any attributes not present on
+ * the key will be returned as invalid.
+ */
+ gp11_object_set_session (pub, call->session);
+ attrs = gp11_object_get (pub, &error, CKA_KEY_TYPE, CKA_MODULUS,
+ CKA_PUBLIC_EXPONENT, CKA_MODULUS_BITS, CKA_CLASS, -1);
+
+ if (error) {
+ g_warning ("error retrieving attributes for public key: %s", error->message);
+ g_clear_error (&error);
+ continue;
+ }
+
+ /* We have this one squared away and ready to send */
+ all_attrs = g_list_prepend (all_attrs, attrs);
+ }
+
+ gp11_list_unref_free (objects);
+
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_RSA_IDENTITIES_ANSWER);
+ gkr_buffer_add_uint32 (call->resp, g_list_length (all_attrs));
+
+ for (l = all_attrs; l; l = g_list_next (l)) {
+
+ attrs = l->data;
+
+ /* Write out the key */
+ gck_ssh_agent_proto_write_public_v1 (call->resp, attrs);
+
+ /* And now a per key comment */
+ gkr_buffer_add_string (call->resp, "Public Key");
+
+ gp11_attributes_unref (attrs);
+ }
+
+ g_list_free (all_attrs);
+
+ return TRUE;
+}
+
+static const guchar SHA1_ASN[15] = /* Object ID is 1.3.14.3.2.26 */
+ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
+ 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
+
+static const guchar MD5_ASN[18] = /* Object ID is 1.2.840.113549.2.5 */
+ { 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86,0x48,
+ 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 };
+
+static guchar*
+make_pkcs1_sign_hash (GChecksumType algo, const guchar *data, gsize n_data,
+ gsize *n_result)
+{
+ gsize n_algo, n_asn, n_hash;
+ GChecksum *checksum;
+ const guchar *asn;
+ guchar *hash;
+
+ g_assert (data);
+ g_assert (n_result);
+
+ n_algo = g_checksum_type_get_length (algo);
+ g_return_val_if_fail (n_algo > 0, FALSE);
+
+ if (algo == G_CHECKSUM_SHA1) {
+ asn = SHA1_ASN;
+ n_asn = sizeof (SHA1_ASN);
+ } else if (algo == G_CHECKSUM_MD5) {
+ asn = MD5_ASN;
+ n_asn = sizeof (MD5_ASN);
+ }
+
+ n_hash = n_algo + n_asn;
+ hash = g_malloc0 (n_hash);
+ memcpy (hash, asn, n_asn);
+
+ checksum = g_checksum_new (algo);
+ g_checksum_update (checksum, data, n_data);
+ g_checksum_get_digest (checksum, hash + n_asn, &n_algo);
+ g_checksum_free (checksum);
+
+ *n_result = n_hash;
+ return hash;
+}
+
+static guchar*
+make_raw_sign_hash (GChecksumType algo, const guchar *data, gsize n_data,
+ gsize *n_result)
+{
+ gsize n_hash;
+ GChecksum *checksum;
+ guchar *hash;
+
+ g_assert (data);
+ g_assert (n_result);
+
+ n_hash = g_checksum_type_get_length (algo);
+ g_return_val_if_fail (n_hash > 0, FALSE);
+
+ hash = g_malloc0 (n_hash);
+
+ checksum = g_checksum_new (algo);
+ g_checksum_update (checksum, data, n_data);
+ g_checksum_get_digest (checksum, hash, &n_hash);
+ g_checksum_free (checksum);
+
+ *n_result = n_hash;
+ return hash;
+}
+
+static gboolean
+op_sign_request (GckSshAgentCall *call)
+{
+ GP11Attributes *attrs;
+ GError *error = NULL;
+ const guchar *data;
+ const gchar *salgo;
+ GP11Object *key;
+ guchar *result;
+ gsize n_data, n_result;
+ guint32 flags;
+ gsize offset;
+ gboolean ret;
+ guint blobpos, sz;
+ guint8 *hash;
+ gulong algo, mech;
+ GChecksumType halgo;
+ gsize n_hash;
+
+ offset = 5;
+
+ /* The key packet size */
+ if (!gkr_buffer_get_uint32 (call->req, offset, &offset, &sz))
+ return FALSE;
+
+ /* The key itself */
+ attrs = gp11_attributes_new ();
+ if (!gck_ssh_agent_proto_read_public (call->req, &offset, attrs, &algo))
+ return FALSE;
+
+ /* Validate the key type / mechanism */
+ if (algo == CKK_RSA)
+ mech = CKM_RSA_PKCS;
+ else if (algo == CKK_DSA)
+ mech = CKM_DSA;
+ else
+ g_return_val_if_reached (FALSE);
+
+ if (!gkr_buffer_get_byte_array (call->req, offset, &offset, &data, &n_data) ||
+ !gkr_buffer_get_uint32 (call->req, offset, &offset, &flags)) {
+ gp11_attributes_unref (attrs);
+ return FALSE;
+ }
+
+ /* Lookup the key */
+ key = private_key_for_attributes (call->session, attrs);
+ gp11_attributes_unref (attrs);
+
+ if (!key) {
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_FAILURE);
+ return TRUE;
+ }
+
+ /* Usually we hash the data with SHA1 */
+ if (flags & GCK_SSH_FLAG_OLD_SIGNATURE)
+ halgo = G_CHECKSUM_MD5;
+ else
+ halgo = G_CHECKSUM_SHA1;
+
+ /* Build the hash */
+ if (mech == CKM_RSA_PKCS)
+ hash = make_pkcs1_sign_hash (halgo, data, n_data, &n_hash);
+ else
+ hash = make_raw_sign_hash (halgo, data, n_data, &n_hash);
+
+ /* Do the magic */
+ result = gp11_session_sign (call->session, key, mech, hash, n_hash, &n_result, &error);
+ g_object_unref (key);
+ g_free (hash);
+
+ if (error) {
+ g_warning ("signing of the data failed: %s", error->message);
+ g_clear_error (&error);
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_FAILURE);
+ return TRUE;
+ }
+
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_SIGN_RESPONSE);
+
+ /* Add a space for the sig blob length */
+ blobpos = call->resp->len;
+ gkr_buffer_add_uint32 (call->resp, 0);
+
+ salgo = gck_ssh_agent_proto_algo_to_keytype (algo);
+ g_assert (salgo);
+ gkr_buffer_add_string (call->resp, salgo);
+
+ switch (algo) {
+ case CKK_RSA:
+ ret = gck_ssh_agent_proto_write_signature_rsa (call->resp, result, n_result);
+ break;
+
+ case CKK_DSA:
+ ret = gck_ssh_agent_proto_write_signature_dsa (call->resp, result, n_result);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ g_free (result);
+ g_return_val_if_fail (ret, FALSE);
+
+ /* Write back the blob length */
+ gkr_buffer_set_uint32 (call->resp, blobpos, (call->resp->len - blobpos) - 4);
+
+ return TRUE;
+}
+
+static gboolean
+op_v1_challenge (GckSshAgentCall *call)
+{
+ gsize offset, n_data, n_result, n_hash;
+ GP11Attributes *attrs;
+ guchar session_id[16];
+ guint8 hash[16];
+ const guchar *data;
+ guchar *result = NULL;
+ GChecksum *checksum;
+ GP11Object *key = NULL;
+ guint32 resp_type;
+ GError *error = NULL;
+ gboolean ret;
+ guint i;
+ guchar b;
+
+ ret = FALSE;
+ offset = 5;
+
+ attrs = gp11_attributes_new ();
+ if (!gck_ssh_agent_proto_read_public_v1 (call->req, &offset, attrs)) {
+ gp11_attributes_unref (attrs);
+ return FALSE;
+ }
+
+ /* Read the entire challenge */
+ data = gck_ssh_agent_proto_read_challenge_v1 (call->req, &offset, &n_data);
+
+ /* Only protocol 1.1 is supported */
+ if (call->req->len <= offset) {
+ gp11_attributes_unref (attrs);
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_FAILURE);
+ return TRUE;
+ }
+
+ /* Read out the session id, raw, unbounded */
+ for (i = 0; i < 16; ++i) {
+ gkr_buffer_get_byte (call->req, offset, &offset, &b);
+ session_id[i] = b;
+ }
+
+ /* And the response type */
+ gkr_buffer_get_uint32 (call->req, offset, &offset, &resp_type);
+
+ /* Did parsing fail? */
+ if (gkr_buffer_has_error (call->req) || data == NULL) {
+ gp11_attributes_unref (attrs);
+ return FALSE;
+ }
+
+ /* Not supported request type */
+ if (resp_type != 1) {
+ gp11_attributes_unref (attrs);
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_FAILURE);
+ return TRUE;
+ }
+
+ /* Lookup the key */
+ key = private_key_for_attributes (call->session, attrs);
+ gp11_attributes_unref (attrs);
+
+ /* Didn't find a key? */
+ if (key == NULL) {
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_FAILURE);
+ return TRUE;
+ }
+
+ result = gp11_session_decrypt (call->session, key, CKM_RSA_PKCS, data, n_data, &n_result, &error);
+ g_object_unref (key);
+
+ if (error) {
+ g_warning ("decryption of the data failed: %s", error->message);
+ g_clear_error (&error);
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_FAILURE);
+ return TRUE;
+ }
+
+ /* Now build up a hash of this and the session_id */
+ checksum = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (checksum, result, n_result);
+ g_checksum_update (checksum, session_id, sizeof (session_id));
+ n_hash = sizeof (hash);
+ g_checksum_get_digest (checksum, hash, &n_hash);
+
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_RSA_RESPONSE);
+ gkr_buffer_append (call->resp, hash, n_hash);
+
+ g_free (result);
+ return TRUE;
+}
+
+static gboolean
+op_remove_identity (GckSshAgentCall *call)
+{
+ GP11Attributes *attrs;
+ GP11Session *session;
+ GP11Object *key;
+ gsize offset;
+ guint sz;
+
+ offset = 5;
+
+ /* The key packet size */
+ if (!gkr_buffer_get_uint32 (call->req, offset, &offset, &sz))
+ return FALSE;
+
+ /* The public key itself */
+ attrs = gp11_attributes_new ();
+ if (!gck_ssh_agent_proto_read_public (call->req, &offset, attrs, NULL)) {
+ gp11_attributes_unref (attrs);
+ return FALSE;
+ }
+
+ key = public_key_for_attributes (call->session, attrs);
+ gp11_attributes_unref (attrs);
+
+ if (key != NULL) {
+
+ /*
+ * This is the session that owns these objects. Only
+ * one thread can use it at a time.
+ */
+
+ session = gck_ssh_agent_checkout_main_session ();
+ g_return_val_if_fail (session, FALSE);
+
+ remove_or_lock_by_public_key (session, key);
+
+ gck_ssh_agent_checkin_main_session (session);
+
+ g_object_unref (key);
+ }
+
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_SUCCESS);
+
+ return TRUE;
+}
+
+static gboolean
+op_v1_remove_identity (GckSshAgentCall *call)
+{
+ GP11Session *session;
+ GP11Attributes *attrs;
+ GP11Object *key;
+ gsize offset;
+
+ offset = 5;
+
+ attrs = gp11_attributes_new ();
+ if (!gck_ssh_agent_proto_read_public_v1 (call->req, &offset, attrs)) {
+ gp11_attributes_unref (attrs);
+ return FALSE;
+ }
+
+ key = public_key_for_attributes (call->session, attrs);
+ gp11_attributes_unref (attrs);
+
+ /*
+ * This is the session that owns these objects. Only
+ * one thread can use it at a time.
+ */
+
+ if (key != NULL) {
+
+ /*
+ * This is the session that owns these objects. Only
+ * one thread can use it at a time.
+ */
+
+ session = gck_ssh_agent_checkout_main_session ();
+ g_return_val_if_fail (session, FALSE);
+
+ remove_by_public_key (session, key);
+
+ gck_ssh_agent_checkin_main_session (session);
+
+ g_object_unref (key);
+ }
+
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_SUCCESS);
+ return TRUE;
+}
+
+static gboolean
+op_remove_all_identities (GckSshAgentCall *call)
+{
+ GP11Session *session;
+ GList *objects, *l;
+ GError *error = NULL;
+
+ /* Find all session SSH public keys */
+ objects = gp11_session_find_objects (call->session, &error,
+ CKA_CLASS, GP11_ULONG, CKO_PUBLIC_KEY,
+ GP11_INVALID);
+
+
+ /*
+ * This is the session that owns these objects. Only
+ * one thread can use it at a time.
+ */
+
+ session = gck_ssh_agent_checkout_main_session ();
+ g_return_val_if_fail (session, FALSE);
+
+ for (l = objects; l; l = g_list_next (l))
+ remove_or_lock_by_public_key (session, l->data);
+
+ gck_ssh_agent_checkin_main_session (session);
+
+ gp11_list_unref_free (objects);
+
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_SUCCESS);
+ return TRUE;
+}
+
+static gboolean
+op_v1_remove_all_identities (GckSshAgentCall *call)
+{
+ GP11Session *session;
+ GList *objects, *l;
+ GError *error = NULL;
+
+ /* Find all session SSH v1 public keys */
+ objects = gp11_session_find_objects (call->session, &error,
+ CKA_TOKEN, GP11_BOOLEAN, FALSE,
+ CKA_CLASS, GP11_ULONG, CKO_PUBLIC_KEY,
+ CKA_LABEL, GP11_STRING, V1_LABEL,
+ GP11_INVALID);
+
+ /*
+ * This is the session that owns these objects. Only
+ * one thread can use it at a time.
+ */
+
+ session = gck_ssh_agent_checkout_main_session ();
+ g_return_val_if_fail (session, FALSE);
+
+ for (l = objects; l; l = g_list_next (l))
+ remove_by_public_key (session, l->data);
+
+ gck_ssh_agent_checkin_main_session (session);
+
+ gp11_list_unref_free (objects);
+
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_SUCCESS);
+ return TRUE;
+}
+
+static gboolean
+op_not_implemented_success (GckSshAgentCall *call)
+{
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_SUCCESS);
+ return TRUE;
+}
+
+static gboolean
+op_not_implemented_failure (GckSshAgentCall *call)
+{
+ gkr_buffer_add_byte (call->resp, GCK_SSH_RES_FAILURE);
+ return TRUE;
+}
+
+static gboolean
+op_invalid (GckSshAgentCall *call)
+{
+ /* Invalid request, disconnect immediately */
+ return FALSE;
+}
+
+const GckSshAgentOperation gck_ssh_agent_operations[GCK_SSH_OP_MAX] = {
+ op_invalid, /* 0 */
+ op_v1_request_identities, /* GKR_SSH_OP_REQUEST_RSA_IDENTITIES */
+ op_invalid, /* 2 */
+ op_v1_challenge, /* GKR_SSH_OP_RSA_CHALLENGE */
+ op_invalid, /* 4 */
+ op_invalid, /* 5 */
+ op_invalid, /* 6 */
+ op_v1_add_identity, /* GKR_SSH_OP_ADD_RSA_IDENTITY */
+ op_v1_remove_identity, /* GKR_SSH_OP_REMOVE_RSA_IDENTITY */
+ op_v1_remove_all_identities, /* GKR_SSH_OP_REMOVE_ALL_RSA_IDENTITIES */
+ op_invalid, /* 10 */
+ op_request_identities, /* GKR_SSH_OP_REQUEST_IDENTITIES */
+ op_invalid, /* 12 */
+ op_sign_request, /* GKR_SSH_OP_SIGN_REQUEST */
+ op_invalid, /* 14 */
+ op_invalid, /* 15 */
+ op_invalid, /* 16 */
+ op_add_identity, /* GKR_SSH_OP_ADD_IDENTITY */
+ op_remove_identity, /* GKR_SSH_OP_REMOVE_IDENTITY */
+ op_remove_all_identities, /* GKR_SSH_OP_REMOVE_ALL_IDENTITIES */
+ op_not_implemented_failure, /* GKR_SSH_OP_ADD_SMARTCARD_KEY */
+ op_not_implemented_failure, /* GKR_SSH_OP_REMOVE_SMARTCARD_KEY */
+ op_not_implemented_success, /* GKR_SSH_OP_LOCK */
+ op_not_implemented_success, /* GKR_SSH_OP_UNLOCK */
+ op_v1_add_identity, /* GKR_SSH_OP_ADD_RSA_ID_CONSTRAINED */
+ op_not_implemented_failure, /* GKR_SSH_OP_ADD_ID_CONSTRAINED */
+ op_not_implemented_failure, /* GKR_SSH_OP_ADD_SMARTCARD_KEY_CONSTRAINED */
+};
Added: trunk/pkcs11/ssh-agent/gck-ssh-agent-private.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/ssh-agent/gck-ssh-agent-private.h Sat Jan 3 23:00:21 2009
@@ -0,0 +1,178 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gkr-ssh-agent-private.h - Private SSH agent declarations
+
+ Copyright (C) 2007 Stefan Walter
+
+ Gnome keyring is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ Gnome keyring is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Author: Stef Walter <stef memberwebs com>
+*/
+
+#ifndef GKRSSHPRIVATE_H_
+#define GKRSSHPRIVATE_H_
+
+#include "common/gkr-buffer.h"
+
+#include "pkcs11/pkcs11.h"
+
+#include <gp11/gp11.h>
+
+#include <glib.h>
+
+typedef struct _GckSshAgentCall {
+ int sock;
+ GP11Session *session;
+ GkrBuffer *req;
+ GkrBuffer *resp;
+} GckSshAgentCall;
+
+/* -----------------------------------------------------------------------------
+ * SSH OPERATIONS and CONSTANTS
+ */
+
+/* Requests from client to daemon */
+#define GCK_SSH_OP_REQUEST_RSA_IDENTITIES 1
+#define GCK_SSH_OP_RSA_CHALLENGE 3
+#define GCK_SSH_OP_ADD_RSA_IDENTITY 7
+#define GCK_SSH_OP_REMOVE_RSA_IDENTITY 8
+#define GCK_SSH_OP_REMOVE_ALL_RSA_IDENTITIES 9
+#define GCK_SSH_OP_REQUEST_IDENTITIES 11
+#define GCK_SSH_OP_SIGN_REQUEST 13
+#define GCK_SSH_OP_ADD_IDENTITY 17
+#define GCK_SSH_OP_REMOVE_IDENTITY 18
+#define GCK_SSH_OP_REMOVE_ALL_IDENTITIES 19
+#define GCK_SSH_OP_ADD_SMARTCARD_KEY 20
+#define GCK_SSH_OP_REMOVE_SMARTCARD_KEY 21
+#define GCK_SSH_OP_LOCK 22
+#define GCK_SSH_OP_UNLOCK 23
+#define GCK_SSH_OP_ADD_RSA_ID_CONSTRAINED 24
+#define GCK_SSH_OP_ADD_ID_CONSTRAINED 25
+#define GCK_SSH_OP_ADD_SMARTCARD_KEY_CONSTRAINED 26
+
+#define GCK_SSH_OP_MAX 27
+
+/* Responses from daemon to client */
+#define GCK_SSH_RES_RSA_IDENTITIES_ANSWER 2
+#define GCK_SSH_RES_RSA_RESPONSE 4
+#define GCK_SSH_RES_FAILURE 5
+#define GCK_SSH_RES_SUCCESS 6
+#define GCK_SSH_RES_IDENTITIES_ANSWER 12
+#define GCK_SSH_RES_SIGN_RESPONSE 14
+#define GCK_SSH_RES_EXTENDED_FAILURE 30
+#define GCK_SSH_RES_SSHCOM_FAILURE 102
+
+
+#define GCK_SSH_FLAG_CONSTRAIN_LIFETIME 1
+#define GCK_SSH_FLAG_CONSTRAIN_CONFIRM 2
+
+#define GCK_SSH_DSA_SIGNATURE_PADDING 20
+#define GCK_SSH_FLAG_OLD_SIGNATURE 0x01
+
+/* -----------------------------------------------------------------------------
+ * gck-ssh-agent-ops.c
+ */
+
+typedef gboolean (*GckSshAgentOperation) (GckSshAgentCall *call);
+extern const GckSshAgentOperation gck_ssh_agent_operations[GCK_SSH_OP_MAX];
+
+/* -----------------------------------------------------------------------------
+ * gck-ssh-agent.c
+ */
+
+GP11Session* gck_ssh_agent_checkout_main_session (void);
+
+void gck_ssh_agent_checkin_main_session (GP11Session* session);
+
+/* -----------------------------------------------------------------------------
+ * gkr-ssh-proto.c
+ */
+
+gulong gck_ssh_agent_proto_keytype_to_algo (const gchar *salgo);
+
+const gchar* gck_ssh_agent_proto_algo_to_keytype (gulong algo);
+
+gboolean gck_ssh_agent_proto_read_mpi (GkrBuffer *req,
+ gsize *offset,
+ GP11Attributes *attrs,
+ CK_ATTRIBUTE_TYPE type);
+
+gboolean gck_ssh_agent_proto_read_mpi_v1 (GkrBuffer *req,
+ gsize *offset,
+ GP11Attributes *attrs,
+ CK_ATTRIBUTE_TYPE type);
+
+const guchar* gck_ssh_agent_proto_read_challenge_v1 (GkrBuffer *req,
+ gsize *offset,
+ gsize *n_challenge);
+
+gboolean gck_ssh_agent_proto_write_mpi (GkrBuffer *resp,
+ GP11Attribute *attr);
+
+gboolean gck_ssh_agent_proto_write_mpi_v1 (GkrBuffer *resp,
+ GP11Attribute *attr);
+
+gboolean gck_ssh_agent_proto_read_public (GkrBuffer *req,
+ gsize *offset,
+ GP11Attributes *attrs,
+ gulong *algo);
+
+gboolean gck_ssh_agent_proto_read_public_rsa (GkrBuffer *req,
+ gsize *offset,
+ GP11Attributes *attrs);
+
+gboolean gck_ssh_agent_proto_read_public_dsa (GkrBuffer *req,
+ gsize *offset,
+ GP11Attributes *attrs);
+
+gboolean gck_ssh_agent_proto_read_public_v1 (GkrBuffer *req,
+ gsize *offset,
+ GP11Attributes *attrs);
+
+gboolean gck_ssh_agent_proto_read_pair_rsa (GkrBuffer *req,
+ gsize *offset,
+ GP11Attributes *priv_attrs,
+ GP11Attributes *pub_attrs);
+
+gboolean gck_ssh_agent_proto_read_pair_dsa (GkrBuffer *req,
+ gsize *offset,
+ GP11Attributes *priv_attrs,
+ GP11Attributes *pub_attrs);
+
+gboolean gck_ssh_agent_proto_read_pair_v1 (GkrBuffer *req,
+ gsize *offset,
+ GP11Attributes *priv_attrs,
+ GP11Attributes *pub_attrs);
+
+gboolean gck_ssh_agent_proto_write_public (GkrBuffer *resp,
+ GP11Attributes *attrs);
+
+gboolean gck_ssh_agent_proto_write_public_rsa (GkrBuffer *resp,
+ GP11Attributes *attrs);
+
+gboolean gck_ssh_agent_proto_write_public_dsa (GkrBuffer *resp,
+ GP11Attributes *attrs);
+
+gboolean gck_ssh_agent_proto_write_public_v1 (GkrBuffer *resp,
+ GP11Attributes *attrs);
+
+gboolean gck_ssh_agent_proto_write_signature_rsa (GkrBuffer *resp,
+ CK_BYTE_PTR signature,
+ CK_ULONG n_signature);
+
+gboolean gck_ssh_agent_proto_write_signature_dsa (GkrBuffer *resp,
+ CK_BYTE_PTR signature,
+ CK_ULONG n_signature);
+
+#endif /*GKRSSHPRIVATE_H_*/
Added: trunk/pkcs11/ssh-agent/gck-ssh-agent-proto.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/ssh-agent/gck-ssh-agent-proto.c Sat Jan 3 23:00:21 2009
@@ -0,0 +1,513 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-ssh-agent-proto.c - SSH agent protocol helpers
+
+ Copyright (C) 2007 Stefan Walter
+
+ Gnome keyring is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ Gnome keyring is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "config.h"
+
+#include "gck-ssh-agent-private.h"
+
+#include "common/gkr-buffer.h"
+
+#include <gp11/gp11.h>
+
+#include <glib.h>
+
+#include <string.h>
+
+gulong
+gck_ssh_agent_proto_keytype_to_algo (const gchar *salgo)
+{
+ g_return_val_if_fail (salgo, (gulong)-1);
+ if (strcmp (salgo, "ssh-rsa") == 0)
+ return CKK_RSA;
+ else if (strcmp (salgo, "ssh-dss") == 0)
+ return CKK_DSA;
+ return (gulong)-1;
+}
+
+const gchar*
+gck_ssh_agent_proto_algo_to_keytype (gulong algo)
+{
+ if (algo == CKK_RSA)
+ return "ssh-rsa";
+ else if (algo == CKK_DSA)
+ return "ssh-dss";
+ return NULL;
+}
+
+gboolean
+gck_ssh_agent_proto_read_mpi (GkrBuffer *req, gsize *offset, GP11Attributes *attrs,
+ CK_ATTRIBUTE_TYPE type)
+{
+ const guchar *data;
+ gsize len;
+
+ if (!gkr_buffer_get_byte_array (req, *offset, offset, &data, &len))
+ return FALSE;
+
+ /* Convert to unsigned format */
+ if (len >= 2 && data[0] == 0 && (data[1] & 0x80)) {
+ ++data;
+ --len;
+ }
+
+ gp11_attributes_add_data (attrs, type, data, len);
+ return TRUE;
+}
+
+gboolean
+gck_ssh_agent_proto_read_mpi_v1 (GkrBuffer *req, gsize *offset, GP11Attributes *attrs,
+ CK_ATTRIBUTE_TYPE type)
+{
+ const guchar *data;
+ gsize bytes;
+ guint16 bits;
+
+ /* Get the number of bits */
+ if (!gkr_buffer_get_uint16 (req, *offset, offset, &bits))
+ return FALSE;
+
+ /* Figure out the number of binary bytes following */
+ bytes = (bits + 7) / 8;
+ if (bytes > 8 * 1024)
+ return FALSE;
+
+ /* Pull these out directly */
+ if (req->len < *offset + bytes)
+ return FALSE;
+ data = req->buf + *offset;
+ *offset += bytes;
+
+ gp11_attributes_add_data (attrs, type, data, bytes);
+ return TRUE;
+}
+
+gboolean
+gck_ssh_agent_proto_write_mpi (GkrBuffer *resp, GP11Attribute *attr)
+{
+ guchar *data;
+ gsize n_extra;
+
+ g_assert (resp);
+ g_assert (attr);
+
+ /* Convert from unsigned format */
+ n_extra = 0;
+ if (attr->length && (attr->value[0] & 0x80))
+ ++n_extra;
+
+ data = gkr_buffer_add_byte_array_empty (resp, attr->length + n_extra);
+ if (data == NULL)
+ return FALSE;
+
+ memset (data, 0, n_extra);
+ memcpy (data + n_extra, attr->value, attr->length);
+ return TRUE;
+}
+
+gboolean
+gck_ssh_agent_proto_write_mpi_v1 (GkrBuffer *resp, GP11Attribute *attr)
+{
+ guchar *data;
+
+ g_return_val_if_fail (attr->length * 8 < G_MAXUSHORT, FALSE);
+
+ if (!gkr_buffer_add_uint16 (resp, attr->length * 8))
+ return FALSE;
+
+ data = gkr_buffer_add_empty (resp, attr->length);
+ if (data == NULL)
+ return FALSE;
+ memcpy (data, attr->value, attr->length);
+ return TRUE;
+}
+
+const guchar*
+gck_ssh_agent_proto_read_challenge_v1 (GkrBuffer *req, gsize *offset, gsize *n_challenge)
+{
+ const guchar *data;
+ gsize bytes;
+ guint16 bits;
+
+ /* Get the number of bits */
+ if (!gkr_buffer_get_uint16 (req, *offset, offset, &bits))
+ return FALSE;
+
+ /* Figure out the number of binary bytes following */
+ bytes = (bits + 7) / 8;
+ if (bytes > 8 * 1024)
+ return FALSE;
+
+ /* Pull these out directly */
+ if (req->len < *offset + bytes)
+ return FALSE;
+ data = req->buf + *offset;
+ *offset += bytes;
+ *n_challenge = bytes;
+ return data;
+}
+
+gboolean
+gck_ssh_agent_proto_read_public (GkrBuffer *req, gsize *offset, GP11Attributes* attrs, gulong *algo)
+{
+ gboolean ret;
+ gchar *stype;
+ gulong alg;
+
+ g_assert (req);
+ g_assert (offset);
+
+ /* The string algorithm */
+ if (!gkr_buffer_get_string (req, *offset, offset, &stype, (GkrBufferAllocator)g_realloc))
+ return FALSE;
+
+ alg = gck_ssh_agent_proto_keytype_to_algo (stype);
+ if (alg == (gulong)-1) {
+ g_warning ("unsupported algorithm from SSH: %s", stype);
+ g_free (stype);
+ return FALSE;
+ }
+
+ g_free (stype);
+ switch (alg) {
+ case CKK_RSA:
+ ret = gck_ssh_agent_proto_read_public_rsa (req, offset, attrs);
+ break;
+ case CKK_DSA:
+ ret = gck_ssh_agent_proto_read_public_dsa (req, offset, attrs);
+ break;
+ default:
+ g_assert_not_reached ();
+ return FALSE;
+ }
+
+ if (!ret) {
+ g_warning ("couldn't read incoming SSH private key");
+ return FALSE;
+ }
+
+ if (algo)
+ *algo = alg;
+ return ret;
+}
+
+gboolean
+gck_ssh_agent_proto_read_pair_rsa (GkrBuffer *req, gsize *offset,
+ GP11Attributes *priv_attrs, GP11Attributes *pub_attrs)
+{
+ GP11Attribute *attr;
+
+ g_assert (req);
+ g_assert (offset);
+ g_assert (priv_attrs);
+ g_assert (pub_attrs);
+
+ if (!gck_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_MODULUS) ||
+ !gck_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_PUBLIC_EXPONENT) ||
+ !gck_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_PRIVATE_EXPONENT) ||
+ !gck_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_COEFFICIENT) ||
+ !gck_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_PRIME_1) ||
+ !gck_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_PRIME_2))
+ return FALSE;
+
+ /* Copy attributes to the public key */
+ attr = gp11_attributes_find (priv_attrs, CKA_MODULUS);
+ gp11_attributes_add (pub_attrs, attr);
+ attr = gp11_attributes_find (priv_attrs, CKA_PUBLIC_EXPONENT);
+ gp11_attributes_add (pub_attrs, attr);
+
+ /* Add in your basic other required attributes */
+ gp11_attributes_add_ulong (priv_attrs, CKA_CLASS, CKO_PRIVATE_KEY);
+ gp11_attributes_add_ulong (priv_attrs, CKA_KEY_TYPE, CKK_RSA);
+ gp11_attributes_add_ulong (pub_attrs, CKA_CLASS, CKO_PUBLIC_KEY);
+ gp11_attributes_add_ulong (pub_attrs, CKA_KEY_TYPE, CKK_RSA);
+
+ return TRUE;
+}
+
+gboolean
+gck_ssh_agent_proto_read_pair_v1 (GkrBuffer *req, gsize *offset,
+ GP11Attributes *priv_attrs, GP11Attributes *pub_attrs)
+{
+ GP11Attribute *attr;
+
+ g_assert (req);
+ g_assert (offset);
+ g_assert (priv_attrs);
+ g_assert (pub_attrs);
+
+ if (!gck_ssh_agent_proto_read_mpi_v1 (req, offset, priv_attrs, CKA_MODULUS) ||
+ !gck_ssh_agent_proto_read_mpi_v1 (req, offset, priv_attrs, CKA_PUBLIC_EXPONENT) ||
+ !gck_ssh_agent_proto_read_mpi_v1 (req, offset, priv_attrs, CKA_PRIVATE_EXPONENT) ||
+ !gck_ssh_agent_proto_read_mpi_v1 (req, offset, priv_attrs, CKA_COEFFICIENT) ||
+ !gck_ssh_agent_proto_read_mpi_v1 (req, offset, priv_attrs, CKA_PRIME_1) ||
+ !gck_ssh_agent_proto_read_mpi_v1 (req, offset, priv_attrs, CKA_PRIME_2))
+ return FALSE;
+
+ /* Copy attributes to the public key */
+ attr = gp11_attributes_find (priv_attrs, CKA_MODULUS);
+ gp11_attributes_add (pub_attrs, attr);
+ attr = gp11_attributes_find (priv_attrs, CKA_PUBLIC_EXPONENT);
+ gp11_attributes_add (pub_attrs, attr);
+
+ /* Add in your basic other required attributes */
+ gp11_attributes_add_ulong (priv_attrs, CKA_CLASS, CKO_PRIVATE_KEY);
+ gp11_attributes_add_ulong (priv_attrs, CKA_KEY_TYPE, CKK_RSA);
+ gp11_attributes_add_ulong (pub_attrs, CKA_CLASS, CKO_PUBLIC_KEY);
+ gp11_attributes_add_ulong (pub_attrs, CKA_KEY_TYPE, CKK_RSA);
+
+ return TRUE;
+}
+
+gboolean
+gck_ssh_agent_proto_read_public_rsa (GkrBuffer *req, gsize *offset, GP11Attributes *attrs)
+{
+ g_assert (req);
+ g_assert (offset);
+ g_assert (attrs);
+
+ if (!gck_ssh_agent_proto_read_mpi (req, offset, attrs, CKA_PUBLIC_EXPONENT) ||
+ !gck_ssh_agent_proto_read_mpi (req, offset, attrs, CKA_MODULUS))
+ return FALSE;
+
+ /* Add in your basic other required attributes */
+ gp11_attributes_add_ulong (attrs, CKA_CLASS, CKO_PUBLIC_KEY);
+ gp11_attributes_add_ulong (attrs, CKA_KEY_TYPE, CKK_RSA);
+
+ return TRUE;
+}
+
+gboolean
+gck_ssh_agent_proto_read_public_v1 (GkrBuffer *req, gsize *offset, GP11Attributes *attrs)
+{
+ guint32 bits;
+
+ g_assert (req);
+ g_assert (offset);
+ g_assert (attrs);
+
+ if (!gkr_buffer_get_uint32 (req, *offset, offset, &bits))
+ return FALSE;
+
+ if (!gck_ssh_agent_proto_read_mpi_v1 (req, offset, attrs, CKA_PUBLIC_EXPONENT) ||
+ !gck_ssh_agent_proto_read_mpi_v1 (req, offset, attrs, CKA_MODULUS))
+ return FALSE;
+
+ /* Add in your basic other required attributes */
+ gp11_attributes_add_ulong (attrs, CKA_CLASS, CKO_PUBLIC_KEY);
+ gp11_attributes_add_ulong (attrs, CKA_KEY_TYPE, CKK_RSA);
+
+ return TRUE;
+}
+
+gboolean
+gck_ssh_agent_proto_read_pair_dsa (GkrBuffer *req, gsize *offset,
+ GP11Attributes *priv_attrs, GP11Attributes *pub_attrs)
+{
+ GP11Attribute *attr;
+
+ g_assert (req);
+ g_assert (offset);
+ g_assert (priv_attrs);
+ g_assert (pub_attrs);
+
+ if (!gck_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_PRIME) ||
+ !gck_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_SUBPRIME) ||
+ !gck_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_BASE) ||
+ !gck_ssh_agent_proto_read_mpi (req, offset, pub_attrs, CKA_VALUE) ||
+ !gck_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_VALUE))
+ return FALSE;
+
+ /* Copy attributes to the public key */
+ attr = gp11_attributes_find (priv_attrs, CKA_PRIME);
+ gp11_attributes_add (pub_attrs, attr);
+ attr = gp11_attributes_find (priv_attrs, CKA_SUBPRIME);
+ gp11_attributes_add (pub_attrs, attr);
+ attr = gp11_attributes_find (priv_attrs, CKA_BASE);
+ gp11_attributes_add (pub_attrs, attr);
+
+ /* Add in your basic other required attributes */
+ gp11_attributes_add_ulong (priv_attrs, CKA_CLASS, CKO_PRIVATE_KEY);
+ gp11_attributes_add_ulong (priv_attrs, CKA_KEY_TYPE, CKK_DSA);
+ gp11_attributes_add_ulong (pub_attrs, CKA_CLASS, CKO_PUBLIC_KEY);
+ gp11_attributes_add_ulong (pub_attrs, CKA_KEY_TYPE, CKK_DSA);
+
+ return TRUE;
+}
+
+gboolean
+gck_ssh_agent_proto_read_public_dsa (GkrBuffer *req, gsize *offset, GP11Attributes *attrs)
+{
+ g_assert (req);
+ g_assert (offset);
+ g_assert (attrs);
+
+ if (!gck_ssh_agent_proto_read_mpi (req, offset, attrs, CKA_PRIME) ||
+ !gck_ssh_agent_proto_read_mpi (req, offset, attrs, CKA_SUBPRIME) ||
+ !gck_ssh_agent_proto_read_mpi (req, offset, attrs, CKA_BASE) ||
+ !gck_ssh_agent_proto_read_mpi (req, offset, attrs, CKA_VALUE))
+ return FALSE;
+
+ /* Add in your basic other required attributes */
+ gp11_attributes_add_ulong (attrs, CKA_CLASS, CKO_PUBLIC_KEY);
+ gp11_attributes_add_ulong (attrs, CKA_KEY_TYPE, CKK_DSA);
+
+ return TRUE;
+}
+
+gboolean
+gck_ssh_agent_proto_write_public (GkrBuffer *resp, GP11Attributes *attrs)
+{
+ gboolean ret = FALSE;
+ const gchar *salgo;
+ gulong algo;
+
+ g_assert (resp);
+ g_assert (attrs);
+
+ if (!gp11_attributes_find_ulong (attrs, CKA_KEY_TYPE, &algo))
+ g_return_val_if_reached (FALSE);
+
+ salgo = gck_ssh_agent_proto_algo_to_keytype (algo);
+ g_assert (salgo);
+ gkr_buffer_add_string (resp, salgo);
+
+ switch (algo) {
+ case CKK_RSA:
+ ret = gck_ssh_agent_proto_write_public_rsa (resp, attrs);
+ break;
+
+ case CKK_DSA:
+ ret = gck_ssh_agent_proto_write_public_dsa (resp, attrs);
+ break;
+
+ default:
+ g_return_val_if_reached (FALSE);
+ break;
+ }
+
+ return ret;
+}
+
+gboolean
+gck_ssh_agent_proto_write_public_rsa (GkrBuffer *resp, GP11Attributes *attrs)
+{
+ GP11Attribute *attr;
+
+ g_assert (resp);
+ g_assert (attrs);
+
+ attr = gp11_attributes_find (attrs, CKA_PUBLIC_EXPONENT);
+ g_return_val_if_fail (attr, FALSE);
+
+ if (!gck_ssh_agent_proto_write_mpi (resp, attr))
+ return FALSE;
+
+ attr = gp11_attributes_find (attrs, CKA_MODULUS);
+ g_return_val_if_fail (attr, FALSE);
+
+ if (!gck_ssh_agent_proto_write_mpi (resp, attr))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gck_ssh_agent_proto_write_public_dsa (GkrBuffer *resp, GP11Attributes *attrs)
+{
+ GP11Attribute *attr;
+
+ g_assert (resp);
+ g_assert (attrs);
+
+ attr = gp11_attributes_find (attrs, CKA_PRIME);
+ g_return_val_if_fail (attr, FALSE);
+
+ if (!gck_ssh_agent_proto_write_mpi (resp, attr))
+ return FALSE;
+
+ attr = gp11_attributes_find (attrs, CKA_SUBPRIME);
+ g_return_val_if_fail (attr, FALSE);
+
+ if (!gck_ssh_agent_proto_write_mpi (resp, attr))
+ return FALSE;
+
+ attr = gp11_attributes_find (attrs, CKA_BASE);
+ g_return_val_if_fail (attr, FALSE);
+
+ if (!gck_ssh_agent_proto_write_mpi (resp, attr))
+ return FALSE;
+
+ attr = gp11_attributes_find (attrs, CKA_VALUE);
+ g_return_val_if_fail (attr, FALSE);
+
+ if (!gck_ssh_agent_proto_write_mpi (resp, attr))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gck_ssh_agent_proto_write_public_v1 (GkrBuffer *resp, GP11Attributes *attrs)
+{
+ GP11Attribute *attr;
+ gulong bits;
+
+ g_assert (resp);
+ g_assert (attrs);
+
+ /* This is always an RSA key. */
+
+ /* Write out the number of bits of the key */
+ if (!gp11_attributes_find_ulong (attrs, CKA_MODULUS_BITS, &bits))
+ g_return_val_if_reached (FALSE);
+ gkr_buffer_add_uint32 (resp, bits);
+
+ /* Write out the exponent */
+ attr = gp11_attributes_find (attrs, CKA_PUBLIC_EXPONENT);
+ g_return_val_if_fail (attr, FALSE);
+
+ if (!gck_ssh_agent_proto_write_mpi_v1 (resp, attr))
+ return FALSE;
+
+ /* Write out the modulus */
+ attr = gp11_attributes_find (attrs, CKA_MODULUS);
+ g_return_val_if_fail (attr, FALSE);
+
+ if (!gck_ssh_agent_proto_write_mpi_v1 (resp, attr))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gck_ssh_agent_proto_write_signature_rsa (GkrBuffer *resp, CK_BYTE_PTR signature, CK_ULONG n_signature)
+{
+ return gkr_buffer_add_byte_array (resp, signature, n_signature);
+}
+
+gboolean
+gck_ssh_agent_proto_write_signature_dsa (GkrBuffer *resp, CK_BYTE_PTR signature, CK_ULONG n_signature)
+{
+ g_return_val_if_fail (n_signature == 40, FALSE);
+ return gkr_buffer_add_byte_array (resp, signature, n_signature);
+}
+
Added: trunk/pkcs11/ssh-agent/gck-ssh-agent-standalone.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/ssh-agent/gck-ssh-agent-standalone.c Sat Jan 3 23:00:21 2009
@@ -0,0 +1,125 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-ssh-agent-standalone.c - Test standalone SSH agent
+
+ Copyright (C) 2007 Stefan Walter
+
+ Gnome keyring is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ Gnome keyring is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "config.h"
+
+#include <glib.h>
+
+#include "gck-ssh-agent.h"
+
+#include "common/gkr-secure-memory.h"
+
+G_LOCK_DEFINE_STATIC (memory_mutex);
+
+void gkr_memory_lock (void)
+ { G_LOCK (memory_mutex); }
+
+void gkr_memory_unlock (void)
+ { G_UNLOCK (memory_mutex); }
+
+void* gkr_memory_fallback (void *p, unsigned long sz)
+ { return g_realloc (p, sz); }
+
+static gboolean
+accept_client (GIOChannel *channel, GIOCondition cond, gpointer unused)
+{
+ gck_ssh_agent_accept ();
+ return TRUE;
+}
+
+static gboolean
+authenticate_token (GP11Slot *self, gchar *label, gchar **password, gpointer unused)
+{
+ gchar *prompt = g_strdup_printf ("Enter token password (%s): ", label);
+ char *result = getpass (prompt);
+ g_free (prompt);
+ *password = g_strdup (result);
+ memset (result, 0, strlen (result));
+ return TRUE;
+}
+
+static gboolean
+authenticate_object (GP11Slot *self, GP11Object *object, gchar *label, gchar **password)
+{
+ gchar *prompt = g_strdup_printf ("Enter object password (%s): ", label);
+ char *result = getpass (prompt);
+ g_free (prompt);
+ *password = g_strdup (result);
+ memset (result, 0, strlen (result));
+ return TRUE;
+}
+
+int
+main(int argc, char *argv[])
+{
+ GP11Module *module;
+ GList *slots;
+ GP11Slot *slot;
+ GError *error = NULL;
+ GIOChannel *channel;
+ GMainLoop *loop;
+
+ g_type_init ();
+
+ if (!g_thread_supported ())
+ g_thread_init (NULL);
+
+ module = gp11_module_initialize (argv[1], argc > 2 ? argv[2] : NULL, &error);
+ if (!module) {
+ g_message ("couldn't load pkcs11 module: %s", error->message);
+ g_clear_error (&error);
+ return 1;
+ }
+
+ /* This is currently brittle because it's just used for development */
+ slots = gp11_module_get_slots (module, TRUE);
+ if (!slots) {
+ g_message ("no slots present in pkcs11 module");
+ return 1;
+ }
+
+ slot = g_object_ref (slots->data);
+ gp11_list_unref_free (slots);
+
+ g_signal_connect (slot, "authenticate-token", G_CALLBACK (authenticate_token), NULL);
+ g_signal_connect (slot, "authenticate-object", G_CALLBACK (authenticate_object), NULL);
+ gp11_slot_set_auto_login (slot, TRUE);
+
+ if (!gck_ssh_agent_initialize ("/tmp/test-gck-ssh-agent", slot))
+ return 1;
+
+ channel = g_io_channel_unix_new (gck_ssh_agent_get_socket_fd ());
+ g_io_add_watch (channel, G_IO_IN | G_IO_HUP, accept_client, NULL);
+ g_io_channel_unref (channel);
+
+ g_print ("SSH_AUTH_SOCK=%s\n", gck_ssh_agent_get_socket_path ());
+
+ /* Run a main loop */
+ loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+
+ gck_ssh_agent_uninitialize ();
+ g_object_unref (slot);
+
+ return 0;
+}
Added: trunk/pkcs11/ssh-agent/gck-ssh-agent.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/ssh-agent/gck-ssh-agent.c Sat Jan 3 23:00:21 2009
@@ -0,0 +1,432 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-ssh-agent.c - handles SSH i/o from the clients
+
+ Copyright (C) 2007 Stefan Walter
+
+ Gnome keyring is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ Gnome keyring is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "gck-ssh-agent.h"
+#include "gck-ssh-agent-private.h"
+
+#include "common/gkr-buffer.h"
+#include "common/gkr-secure-memory.h"
+
+#ifndef HAVE_SOCKLEN_T
+#define socklen_t int
+#endif
+
+/* The PKCS#11 slot we call into */
+static GP11Slot *pkcs11_slot = NULL;
+
+static gboolean
+read_all (int fd, guchar *buf, int len)
+{
+ int all = len;
+ int res;
+
+ while (len > 0) {
+
+ res = read (fd, buf, len);
+
+ if (res <= 0) {
+ if (errno == EAGAIN && errno == EINTR)
+ continue;
+ if (res < 0)
+ g_warning ("couldn't read %u bytes from client: %s", all,
+ g_strerror (errno));
+ return FALSE;
+ } else {
+ len -= res;
+ buf += res;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+write_all (int fd, const guchar *buf, int len)
+{
+ int all = len;
+ int res;
+
+ while (len > 0) {
+
+ res = write (fd, buf, len);
+
+ if (res <= 0) {
+ if (errno == EAGAIN && errno == EINTR)
+ continue;
+ g_warning ("couldn't write %u bytes to client: %s", all,
+ res < 0 ? g_strerror (errno) : "");
+ return FALSE;
+ } else {
+ len -= res;
+ buf += res;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+read_packet_with_size (GckSshAgentCall *call)
+{
+ int fd;
+ guint32 packet_size;
+
+ fd = call->sock;
+
+ gkr_buffer_resize (call->req, 4);
+ if (!read_all (fd, call->req->buf, 4))
+ return FALSE;
+
+ if (!gkr_buffer_get_uint32 (call->req, 0, NULL, &packet_size) ||
+ packet_size < 1) {
+ g_warning ("invalid packet size from client");
+ return FALSE;
+ }
+
+ gkr_buffer_resize (call->req, packet_size + 4);
+ if (!read_all (fd, call->req->buf + 4, packet_size))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gpointer
+run_client_thread (gpointer data)
+{
+ gboolean running = TRUE;
+ GError *error = NULL;
+ gint *socket = data;
+ GckSshAgentCall call;
+ GkrBuffer req;
+ GkrBuffer resp;
+ guchar op;
+
+ g_assert (pkcs11_slot);
+
+ memset (&call, 0, sizeof (call));
+ call.sock = g_atomic_int_get (socket);
+ g_assert (call.sock != -1);
+
+ gkr_buffer_init_full (&req, 128, gkr_secure_realloc);
+ gkr_buffer_init_full (&resp, 128, (GkrBufferAllocator)g_realloc);
+ call.req = &req;
+ call.resp = &resp;
+
+ /* Try to open a session for this thread */
+ call.session = gp11_slot_open_session (pkcs11_slot, CKF_SERIAL_SESSION, &error);
+ if (!call.session) {
+ g_warning ("couldn't open pkcs#11 session for agent thread: %s", error->message);
+ g_clear_error (&error);
+ running = FALSE;
+ }
+
+ while (running) {
+
+ gkr_buffer_reset (call.req);
+
+ /* 1. Read in the request */
+ if (!read_packet_with_size (&call))
+ break;
+
+ /* 2. Now decode the operation */
+ if (!gkr_buffer_get_byte (call.req, 4, NULL, &op))
+ break;
+ if (op >= GCK_SSH_OP_MAX)
+ break;
+ g_assert (gck_ssh_agent_operations[op]);
+
+ /* 3. Execute the right operation */
+ gkr_buffer_reset (call.resp);
+ gkr_buffer_add_uint32 (call.resp, 0);
+ if (!(gck_ssh_agent_operations[op]) (&call))
+ break;
+ if (!gkr_buffer_set_uint32 (call.resp, 0, call.resp->len - 4))
+ break;
+
+ /* 4. Write the reply back out */
+ if (!write_all (call.sock, call.resp->buf, call.resp->len))
+ break;
+ }
+
+ gkr_buffer_uninit (&req);
+ gkr_buffer_uninit (&resp);
+
+ close (call.sock);
+ g_atomic_int_set (socket, -1);
+
+ return NULL;
+}
+
+/* --------------------------------------------------------------------------------------
+ * MAIN SESSION
+ */
+
+/* The main PKCS#11 session that owns objects, and the mutex/cond for waiting on it */
+static GP11Session *pkcs11_session = NULL;
+static gboolean pkcs11_session_checked = FALSE;
+static GMutex *pkcs11_session_mutex = NULL;
+static GCond *pkcs11_session_cond = NULL;
+
+static gboolean
+init_main_session (GP11Slot *slot)
+{
+ GP11Session *session;
+ GError *error = NULL;
+
+ g_assert (GP11_IS_SLOT (slot));
+
+ /* Load our main session */
+ session = gp11_slot_open_session (slot, CKF_SERIAL_SESSION, &error);
+ if (!session) {
+ g_warning ("couldn't create pkcs#11 session: %s", error->message);
+ g_clear_error (&error);
+ return FALSE;
+ }
+
+ pkcs11_session_mutex = g_mutex_new ();
+ pkcs11_session_cond = g_cond_new ();
+ pkcs11_session_checked = FALSE;
+ pkcs11_session = session;
+
+ return TRUE;
+}
+
+GP11Session*
+gck_ssh_agent_checkout_main_session (void)
+{
+ GP11Session *result;
+
+ g_mutex_lock (pkcs11_session_mutex);
+
+ g_assert (GP11_IS_SESSION (pkcs11_session));
+ while (pkcs11_session_checked)
+ g_cond_wait (pkcs11_session_cond, pkcs11_session_mutex);
+ pkcs11_session_checked = TRUE;
+ result = g_object_ref (pkcs11_session);
+
+ g_mutex_unlock (pkcs11_session_mutex);
+
+ return result;
+}
+
+void
+gck_ssh_agent_checkin_main_session (GP11Session *session)
+{
+ g_assert (GP11_IS_SESSION (session));
+
+ g_mutex_lock (pkcs11_session_mutex);
+
+ g_assert (session == pkcs11_session);
+ g_assert (pkcs11_session_checked);
+
+ g_object_unref (session);
+ pkcs11_session_checked = FALSE;
+ g_cond_signal (pkcs11_session_cond);
+
+ g_mutex_unlock (pkcs11_session_mutex);
+}
+
+static void
+uninit_main_session (void)
+{
+ gboolean ret;
+
+ g_assert (pkcs11_session_mutex);
+ ret = g_mutex_trylock (pkcs11_session_mutex);
+ g_assert (ret);
+
+ g_assert (GP11_IS_SESSION (pkcs11_session));
+ g_assert (!pkcs11_session_checked);
+ g_object_unref (pkcs11_session);
+ pkcs11_session = NULL;
+
+ g_mutex_unlock (pkcs11_session_mutex);
+ g_mutex_free (pkcs11_session_mutex);
+ g_cond_free (pkcs11_session_cond);
+}
+
+/* --------------------------------------------------------------------------------------
+ * MAIN THREAD
+ */
+
+typedef struct _Client {
+ GThread *thread;
+ gint sock;
+} Client;
+
+/* Each client thread in this list */
+static GList *socket_clients = NULL;
+
+/* The main socket we listen on */
+static int socket_fd = -1;
+
+/* The path of the socket listening on */
+static char socket_path[1024] = { 0, };
+
+int
+gck_ssh_agent_get_socket_fd (void)
+{
+ return socket_fd;
+}
+
+const gchar*
+gck_ssh_agent_get_socket_path (void)
+{
+ return socket_path;
+}
+
+void
+gck_ssh_agent_accept (void)
+{
+ Client *client;
+ struct sockaddr_un addr;
+ socklen_t addrlen;
+ GError *error = NULL;
+ GList *l;
+ int new_fd;
+
+ g_return_if_fail (socket_fd != -1);
+
+ /* Cleanup any completed dispatch threads */
+ for (l = socket_clients; l; l = g_list_next (l)) {
+ client = l->data;
+ if (g_atomic_int_get (&client->sock) == -1) {
+ g_thread_join (client->thread);
+ g_slice_free (Client, client);
+ l->data = NULL;
+ }
+ }
+ socket_clients = g_list_remove_all (socket_clients, NULL);
+
+ addrlen = sizeof (addr);
+ new_fd = accept (socket_fd, (struct sockaddr*) &addr, &addrlen);
+ if (socket_fd < 0) {
+ g_warning ("cannot accept SSH agent connection: %s", strerror (errno));
+ return;
+ }
+
+ client = g_slice_new0 (Client);
+ client->sock = new_fd;
+
+ /* And create a new thread/process */
+ client->thread = g_thread_create (run_client_thread, &client->sock, TRUE, &error);
+ if (!client->thread) {
+ g_warning ("couldn't create thread SSH agent connection: %s",
+ error && error->message ? error->message : "");
+ g_slice_free (Client, client);
+ return;
+ }
+
+ socket_clients = g_list_append (socket_clients, client);
+}
+
+void
+gck_ssh_agent_uninitialize (void)
+{
+ Client *client;
+ GList *l;
+
+ if (socket_fd != -1)
+ close (socket_fd);
+
+ if (*socket_path)
+ unlink (socket_path);
+
+ /* Stop all of the dispatch threads */
+ for (l = socket_clients; l; l = g_list_next (l)) {
+ client = l->data;
+
+ /* Forcibly shutdown the connection */
+ if (client->sock != -1)
+ shutdown (client->sock, SHUT_RDWR);
+ g_thread_join (client->thread);
+
+ /* This is always closed by client thread */
+ g_assert (client->sock == -1);
+ g_slice_free (Client, client);
+ }
+
+ g_list_free (socket_clients);
+ socket_clients = NULL;
+
+ uninit_main_session ();
+
+ g_object_unref (pkcs11_slot);
+ pkcs11_slot = NULL;
+}
+
+gboolean
+gck_ssh_agent_initialize (const gchar *prefix, GP11Slot *slot)
+{
+ struct sockaddr_un addr;
+ int sock;
+
+ g_return_val_if_fail (GP11_IS_SLOT (slot), FALSE);
+ g_return_val_if_fail (prefix, FALSE);
+
+ snprintf (socket_path, sizeof (socket_path), "%s.ssh", prefix);
+ unlink (socket_path);
+
+ sock = socket (AF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0) {
+ g_warning ("couldn't create socket: %s", g_strerror (errno));
+ return FALSE;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy (addr.sun_path, socket_path, sizeof (addr.sun_path));
+ if (bind (sock, (struct sockaddr *) & addr, sizeof (addr)) < 0) {
+ g_warning ("couldn't bind to socket: %s: %s", socket_path, g_strerror (errno));
+ close (sock);
+ return FALSE;
+ }
+
+ if (listen (sock, 128) < 0) {
+ g_warning ("couldn't listen on socket: %s", g_strerror (errno));
+ close (sock);
+ return FALSE;
+ }
+
+ /* Load our main session */
+ if (!init_main_session (slot)) {
+ close (sock);
+ return FALSE;
+ }
+
+ pkcs11_slot = g_object_ref (slot);
+ socket_fd = sock;
+ return TRUE;
+}
Added: trunk/pkcs11/ssh-agent/gck-ssh-agent.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/ssh-agent/gck-ssh-agent.h Sat Jan 3 23:00:21 2009
@@ -0,0 +1,18 @@
+#ifndef GCKSSHAGENT_H_
+#define GCKSSHAGENT_H_
+
+#include <glib.h>
+
+#include "gp11/gp11.h"
+
+gboolean gck_ssh_agent_initialize (const gchar *prefix, GP11Slot *slot);
+
+int gck_ssh_agent_get_socket_fd (void);
+
+const gchar* gck_ssh_agent_get_socket_path (void);
+
+void gck_ssh_agent_accept (void);
+
+void gck_ssh_agent_uninitialize (void);
+
+#endif /* GCKSSHAGENT_H_ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]