[gnome-keyring/wip/dueno/ssh-agent: 98/105] More work on loading
- From: Daiki Ueno <dueno src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-keyring/wip/dueno/ssh-agent: 98/105] More work on loading
- Date: Mon, 11 Dec 2017 16:33:41 +0000 (UTC)
commit f88eb5da59471f92c315ef4dce7d1a6bdd315745
Author: Stef Walter <stefw redhat com>
Date: Sat Aug 30 07:11:49 2014 +0200
More work on loading
daemon/ssh-agent/Makefile.am | 2 +
daemon/ssh-agent/gkd-ssh-agent-client.c | 179 ++++++--------
daemon/ssh-agent/gkd-ssh-agent-client.h | 37 +---
daemon/ssh-agent/gkd-ssh-agent-ops.c | 26 +-
daemon/ssh-agent/gkd-ssh-agent-preload.c | 383 ++++++++++++++++++++++++++++
daemon/ssh-agent/gkd-ssh-agent-preload.h | 42 +++
daemon/ssh-agent/gkd-ssh-agent-private.h | 2 +-
daemon/ssh-agent/gkd-ssh-agent.c | 7 +-
daemon/ssh-agent/gkd-ssh-openssh.h | 29 ++
daemon/ssh-agent/gkm-ssh-openssh.c | 409 ++++++++++++++++++++++++++++++
daemon/ssh-agent/test-ssh-openssh.c | 138 ++++++++++
11 files changed, 1098 insertions(+), 156 deletions(-)
---
diff --git a/daemon/ssh-agent/Makefile.am b/daemon/ssh-agent/Makefile.am
index 86b47ba..687db32 100644
--- a/daemon/ssh-agent/Makefile.am
+++ b/daemon/ssh-agent/Makefile.am
@@ -10,6 +10,8 @@ libgkd_ssh_agent_la_SOURCES = \
daemon/ssh-agent/gkd-ssh-agent.h \
daemon/ssh-agent/gkd-ssh-agent-client.h \
daemon/ssh-agent/gkd-ssh-agent-client.c \
+ daemon/ssh-agent/gkd-ssh-agent-preload.h \
+ daemon/ssh-agent/gkd-ssh-agent-preload.c \
daemon/ssh-agent/gkd-ssh-agent-private.h \
daemon/ssh-agent/gkd-ssh-agent-ops.c
libgkd_ssh_agent_la_CFLAGS = \
diff --git a/daemon/ssh-agent/gkd-ssh-agent-client.c b/daemon/ssh-agent/gkd-ssh-agent-client.c
index 3fa6d4d..9d5e8d5 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-client.c
+++ b/daemon/ssh-agent/gkd-ssh-agent-client.c
@@ -22,141 +22,116 @@
#include "gkd-ssh-agent-client.h"
-static gint
-agent_start (const char *socket)
+#include "daemon/gkd-util.h"
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <unistd.h>
+
+static gchar *ssh_agent_path = NULL;
+static GPid ssh_agent_pid;
+static GMutex ssh_agent_mutex;
+
+static void
+on_child_watch (GPid pid,
+ gint status,
+ gpointer user_data)
{
- const gchar *argv[] = { SSH_AGENT, "-d", "-a", socket, NULL };
- gchar *standard_error = NULL;
- gchar *standard_output = NULL;
GError *error = NULL;
- gint exit_status = 0;
- gint ret = 0;
- gchar *cmd;
-
- if (!g_spawn_async ("/", argv, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL,
- NULL, NULL,
- NULL, NULL, &standard_output,
- &standard_error, &exit_status, &error) ||
- !g_spawn_check_exit_status (exit_status, NULL)) {
- cmd = g_strjoinv (" ", argv);
- if (error != NULL) {
- g_warning ("couldn't run: %s: %s", cmd, error->message);
- g_error_free (error);
- } else {
- g_warning ("failed to run: %s", cmd);
- }
- g_free (cmd);
-
- /* Sucessfully running, pull out the PID */
- } else {
- lines = g_strsplit (standard_output, "\n", -1);
- for (i = 0; lines[i] != NULL; i++) {
- g_strstrip (lines[i]);
- if (g_str_has_prefix (lines[i], "SSH_AGENT_PID=")) {
- pid = lines[i] + 16;
- pos = strchr (pid, ';');
- if (pos != NULL)
- pos[0] = '\0';
- ret = (int)strtol (pid, 10, &endptr);
- if (!endptr || endptr != '\0') {
- g_warning ("invalid pid received from ssh-agent: %s", pid);
- ret = 0;
- }
- break;
- }
- }
- g_strfreev (lines);
+
+ if (pid != ssh_agent_pid)
+ return;
+
+ g_mutex_lock (&ssh_agent_mutex);
+
+ ssh_agent_pid = 0;
+
+ if (!g_spawn_check_exit_status (status, &error)) {
+ g_message ("ssh-agent: %s", error->message);
+ g_error_free (error);
}
- if (standard_error) {
- lines = g_strsplit (standard_error, "\n", -1);
- for (i = 0; lines[i] != NULL; i++)
- g_warning ("%s", g_strchomp (lines[0]));
- g_strfreev (lines);
+ g_mutex_unlock (&ssh_agent_mutex);
+}
+
+static gboolean
+agent_start_inlock (const char *socket)
+{
+ const gchar *argv[] = { SSH_AGENT, "-d", "-a", socket, NULL };
+ GError *error = NULL;
+ GPid pid;
+
+ if (!g_spawn_async ("/", (gchar **)argv, NULL,
+ G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL,
+ NULL, NULL, &pid, &error)) {
+ g_warning ("couldn't run %s: %s", SSH_AGENT, error->message);
+ g_error_free (error);
+ return FALSE;
}
- g_free (standard_error);
- g_free (standard_output);
- return ret;
+ ssh_agent_pid = pid;
+ g_child_watch_add (ssh_agent_pid, on_child_watch, NULL);
+ return TRUE;
}
-gboolean
-agent_check (gint pid)
+static gboolean
+agent_check (GPid pid)
{
return pid && (kill (pid, 0) == 0);
}
-void
+static void
agent_terminate (gint pid)
{
kill (pid, SIGTERM);
}
-static gchar *
-agent_make_path (void)
-{
- const char *directory;
-
- directory = gkd_util_master_directory ();
-}
-
-G_LOCK (ssh_agent_process);
-static gchar *ssh_agent_path = NULL;
-static gint ssh_agent_pid;
-
-GkdSshAgentClient *
+gint
gkd_ssh_agent_client_connect (void)
{
- GSocketConnection *connection;
- GSocketAddress *address;
+ struct sockaddr_un addr;
const gchar *directory;
- GError *error = NULL;
- GSocket *sock;
gboolean ready;
+ gint sock;
- G_LOCK (ssh_agent_process);
+ g_mutex_lock (&ssh_agent_mutex);
- if (ssh_agent_path) {
- directory = gkd_util_master_directory ();
- ssh_agent_path = g_build_filename (directory, "ssh-actual", NULL);
+ if (!ssh_agent_path) {
+ directory = gkd_util_get_master_directory ();
+ ssh_agent_path = g_build_filename (directory, "ssh-agent-real", NULL);
}
ready = agent_check (ssh_agent_pid);
- if (!ready) {
- ssh_agent_pid = agent_start (ssh_agent_path);
- ready = (ssh_agent_pid != 0);
- }
-
- G_UNLOCK (ssh_agent_pid);
-
if (!ready)
- return NULL;
+ ready = agent_start_inlock (ssh_agent_path);
- sock = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM,
- G_SOCKET_PROTOCOL_DEFAULT);
- g_return_val_if_fail (sock != NULL, NULL);
+ addr.sun_family = AF_UNIX;
+ g_strlcpy (addr.sun_path, ssh_agent_path, sizeof (addr.sun_path));
- connection = g_socket_connection_factory_create_connection (sock);
- g_return_val_if_fail (connection != NULL, NULL);
- g_object_unref (sock);
+ g_mutex_unlock (&ssh_agent_mutex);
+
+ if (!ready)
+ return -1;
- address = g_unix_socket_address_new (ssh_agent_path);
- g_return_val_if_fail (address != NULL, NULL);
+ sock = socket (AF_UNIX, SOCK_STREAM, 0);
+ g_return_val_if_fail (sock >= 0, -1);
- if (!g_socket_connection_connect (connection, address, NULL, &error)) {
- g_warning ("couldn't connect to ssh-agent: %s", error->message);
- g_object_unref (connection);
- connection = NULL;
+ if (connect (sock, (struct sockaddr*) &addr, sizeof (addr)) < 0) {
+ g_message ("couldn't connect to ssh-agent socket at: %s: %s",
+ addr.sun_path, g_strerror (errno));
+ close (sock);
+ sock = -1;
}
- g_object_unref (address);
- return connection;
+ return sock;
}
void
gkd_ssh_agent_client_cleanup (void)
{
- G_LOCK (ssh_agent_process);
+ g_mutex_lock (&ssh_agent_mutex);
if (ssh_agent_pid)
agent_terminate (ssh_agent_pid);
@@ -165,13 +140,5 @@ gkd_ssh_agent_client_cleanup (void)
g_free (ssh_agent_path);
ssh_agent_path = NULL;
- G_UNLOCK (ssh_agent_process);
-}
-
-gboolean
-gkd_ssh_agent_client_transact (GkdSshAgentClient *self,
- EggBuffer *req,
- EggBuffer *resp)
-{
-
+ g_mutex_unlock (&ssh_agent_mutex);
}
diff --git a/daemon/ssh-agent/gkd-ssh-agent-client.h b/daemon/ssh-agent/gkd-ssh-agent-client.h
index cd5919d..c999073 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-client.h
+++ b/daemon/ssh-agent/gkd-ssh-agent-client.h
@@ -24,41 +24,10 @@
#ifndef __GKD_SSH_AGENT_CLIENT_H__
#define __GKD_SSH_AGENT_CLIENT_H__
-#include <glib-object.h>
+#include <glib.h>
-#include "egg/egg-buffer.h"
+gint gkd_ssh_agent_client_connect (void);
-#define GKD_TYPE_SSH_AGENT_CLIENT (gkm_ssh_agent_client_get_type ())
-#define GKD_SSH_AGENT_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GKD_TYPE_SSH_AGENT_CLIENT, GkdSshAgentClient))
-#define GKD_SSH_AGENT_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
GKD_TYPE_SSH_AGENT_CLIENT, GkdSshAgentClientClass))
-#define GKD_IS_SSH_AGENT_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
GKD_TYPE_SSH_AGENT_CLIENT))
-#define GKD_IS_SSH_AGENT_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
GKD_TYPE_SSH_AGENT_CLIENT))
-#define GKD_SSH_AGENT_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
GKD_TYPE_SSH_AGENT_CLIENT, GkdSshAgentClientClass))
-
-typedef struct _GkdSshAgentClient GkdSshAgentClient;
-typedef struct _GkdSshAgentClientClass GkdSshAgentClientClass;
-
-GType gkd_ssh_agent_client_get_type (void);
-
-GkdSshAgentClient * gkd_ssh_agent_client_connect (void);
-
-gboolean gkd_ssh_agent_client_transact (GkdSshAgentClient *self,
- EggBuffer *req,
- EggBuffer *resp);
-
-GList * gkd_ssh_agent_preload_keys (GkdSshAgentClient *self);
-
-gchar * gkd_ssh_agent_preload_comment (GkdSshAgentClient *self,
- GBytes *key);
-
-GBytes * gkd_ssh_agent_preload_unlock (GkdSshAgentClient *self,
- GBytes *key);
-
-void gkd_ssh_agent_preload_clear (GkdSshAgentClient *self,
- GBytes *key);
-
-void gkd_ssh_agent_preload_clear_all (GkdSshAgentClient *self);
-
-void gkd_ssh_agent_preload_cleanup (void);
+void gkd_ssh_agent_client_cleanup (void);
#endif /* __GKD_SSH_AGENT_CLIENT_H__ */
diff --git a/daemon/ssh-agent/gkd-ssh-agent-ops.c b/daemon/ssh-agent/gkd-ssh-agent-ops.c
index 3d9db37..73bf2ca 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-ops.c
+++ b/daemon/ssh-agent/gkd-ssh-agent-ops.c
@@ -24,6 +24,7 @@
#include "config.h"
#include "gkd-ssh-agent-client.h"
+#include "gkd-ssh-agent-preload.h"
#include "gkd-ssh-agent-private.h"
#include "egg/egg-error.h"
@@ -59,14 +60,14 @@ op_add_identity (GkdSshAgentCall *call)
* initial bytes as the public key we've loaded.
*/
- keys = gkd_ssh_agent_client_preload_keys (call->agent);
+ keys = gkd_ssh_agent_preload_keys ();
offset = 5;
for (l = keys; l != NULL; l = g_list_next (l)) {
blob = g_bytes_get_data (l->data, &length);
if (call->req->len >= length + offset &&
memcmp (call->req->buf + offset, blob, length) == 0) {
- gkd_ssh_agent_client_preload_clear (call->agent, l->data);
+ gkd_ssh_agent_preload_clear (l->data);
break;
}
}
@@ -133,13 +134,13 @@ op_request_identities (GkdSshAgentCall *call)
added = 0;
/* Add any preloaded keys not already in answer */
- keys = gkd_ssh_agent_client_preload_keys (call->agent);
+ keys = gkd_ssh_agent_preload_keys ();
for (l = keys; l != NULL; l = g_list_next (l)) {
if (g_hash_table_lookup (answer, l->data))
continue;
blob = g_bytes_get_data (l->data, &length);
egg_buffer_add_byte_array (call->resp, blob, length);
- comment = gkd_ssh_agent_client_preload_comment (call->agent, l->data);
+ comment = gkd_ssh_agent_preload_comment (l->data);
egg_buffer_add_string (call->resp, comment ? comment : "");
g_free (comment);
added++;
@@ -155,7 +156,7 @@ op_request_identities (GkdSshAgentCall *call)
}
static void
-preload_key_if_necessary (GkdSshAgentClient *agent,
+preload_key_if_necessary (gint ssh_agent,
GBytes *key)
{
EggBuffer buf;
@@ -165,7 +166,7 @@ preload_key_if_necessary (GkdSshAgentClient *agent,
GBytes *priv;
guchar code;
- priv = gkd_ssh_agent_client_preload_unlock (agent, key);
+ priv = gkd_ssh_agent_preload_private (key);
if (!priv)
return;
@@ -175,15 +176,16 @@ preload_key_if_necessary (GkdSshAgentClient *agent,
blob = g_bytes_get_data (priv, &length);
egg_buffer_add_byte_array (&buf, blob, length);
- if (gkd_ssh_agent_client_transact (agent, &buf, &buf)) {
+ if (gkd_ssh_agent_write_packet (ssh_agent, &buf) &&
+ gkd_ssh_agent_read_packet (ssh_agent, &buf)) {
if (!egg_buffer_get_byte (&buf, 4, NULL, &code) || code != GKD_SSH_RES_SUCCESS) {
- comment = gkd_ssh_agent_client_preload_comment (agent, key);
+ comment = gkd_ssh_agent_preload_comment (key);
g_warning ("couldn't add private key '%s' to ssh-agent", comment);
g_free (comment);
}
}
- gkd_ssh_agent_client_preload_clear (agent, key);
+ gkd_ssh_agent_preload_clear (key);
}
static gboolean
@@ -197,7 +199,7 @@ op_sign_request (GkdSshAgentCall *call)
/* If parsing the request fails, just pass through */
if (egg_buffer_get_byte_array (call->resp, offset, &offset, &blob, &length)) {
key = g_bytes_new (blob, length);
- preload_key_if_necessary (call->agent, key);
+ preload_key_if_necessary (call->ssh_agent, key);
g_bytes_unref (key);
} else {
g_warning ("got unparseable sign request for ssh-agent");
@@ -217,7 +219,7 @@ op_remove_identity (GkdSshAgentCall *call)
/* If parsing the request fails, just pass through */
if (egg_buffer_get_byte_array (call->resp, offset, &offset, &blob, &length)) {
key = g_bytes_new (blob, length);
- gkd_ssh_agent_client_preload_clear (call->agent, key);
+ gkd_ssh_agent_preload_clear (key);
g_bytes_unref (key);
} else {
g_warning ("got unparseable remove request for ssh-agent");
@@ -230,7 +232,7 @@ op_remove_identity (GkdSshAgentCall *call)
static gboolean
op_remove_all_identities (GkdSshAgentCall *call)
{
- gkd_ssh_agent_client_preload_clear_all (call->agent);
+ gkd_ssh_agent_preload_clear_all ();
return gkd_ssh_agent_relay (call);
}
diff --git a/daemon/ssh-agent/gkd-ssh-agent-preload.c b/daemon/ssh-agent/gkd-ssh-agent-preload.c
new file mode 100644
index 0000000..1eb63fa
--- /dev/null
+++ b/daemon/ssh-agent/gkd-ssh-agent-preload.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2014 Stef 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 thewalter net>
+ */
+
+#include "config.h"
+
+#include "gkd-ssh-agent-preload.h"
+#include "gkd-ssh-openssh.h"
+
+#include "egg/egg-file-tracker.h"
+
+#include <string.h>
+
+typedef struct {
+ gchar *filename;
+ GBytes *public_key;
+ GBytes *private_file;
+ gchar *comment;
+} Preload;
+
+static GMutex preload_mutex;
+static GHashTable *preloads_by_filename;
+static GHashTable *preloads_by_key;
+static EggFileTracker *file_tracker;
+
+static void
+preload_free (gpointer data)
+{
+ Preload *preload = data;
+ if (preload->private_file)
+ g_bytes_unref (preload->private_file);
+ g_bytes_unref (preload->public_key);
+ g_free (preload->comment);
+ g_free (preload->filename);
+ g_free (preload);
+}
+
+static gchar *
+private_path_for_public (const gchar *public_path)
+{
+ gsize length;
+
+ length = strlen (public_path);
+ if (length > 4 && g_str_equal (public_path + (length - 4), ".pub"))
+ return g_strndup (public_path, length - 4);
+
+ return NULL;
+}
+
+static GBytes *
+file_get_contents (const gchar *path,
+ gboolean must_be_present)
+{
+ GError *error = NULL;
+ gchar *contents;
+ gsize length;
+
+ if (!g_file_get_contents (path, &contents, &length, &error)) {
+ if (must_be_present || error->code != G_FILE_ERROR_NOENT)
+ g_message ("couldn't read file: %s: %s", path, error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ return g_bytes_new_take (contents, length);
+}
+
+static void
+file_remove_inlock (EggFileTracker *tracker,
+ const gchar *path,
+ gpointer user_data)
+{
+ Preload *preload;
+
+ preload = g_hash_table_lookup (preloads_by_filename, path);
+ if (preload) {
+ g_hash_table_remove (preloads_by_filename, path);
+ g_hash_table_remove (preloads_by_key, preload->public_key);
+ }
+}
+
+static void
+file_load_inlock (EggFileTracker *tracker,
+ const gchar *path,
+ gpointer user_data)
+{
+ gchar *private_path;
+ GBytes *private_bytes;
+ GBytes *public_bytes;
+ GBytes *public_key;
+ Preload *preload;
+ gchar *comment;
+
+ file_remove_inlock (tracker, path, user_data);
+
+ private_path = private_path_for_public (path);
+
+ private_bytes = file_get_contents (private_path, FALSE);
+ if (!private_bytes) {
+ g_debug ("no private key present for public key: %s", path);
+ g_free (private_path);
+ return;
+ }
+
+ public_bytes = file_get_contents (path, TRUE);
+ if (public_bytes) {
+ public_key = gkd_ssh_openssh_parse_public_key (public_bytes, &comment);
+ if (public_key) {
+ preload = g_new0 (Preload, 1);
+ preload->filename = g_strdup (path);
+ preload->public_key = public_key;
+ preload->comment = comment;
+ g_hash_table_replace (preloads_by_filename, preload->filename, preload);
+ g_hash_table_replace (preloads_by_key, preload->public_key, preload);
+ } else {
+ g_message ("failed to parse ssh public key: %s", path);
+ }
+
+ g_bytes_unref (public_bytes);
+ }
+
+ g_bytes_unref (private_bytes);
+ g_free (private_path);
+}
+
+static gboolean
+preload_lock_and_update (void)
+{
+ g_mutex_lock (&preload_mutex);
+
+ if (!preloads_by_filename)
+ preloads_by_filename = g_hash_table_new (g_str_hash, g_str_equal);
+
+ if (!preloads_by_key)
+ preloads_by_key = g_hash_table_new_full (g_bytes_hash, g_bytes_equal, NULL, preload_free);
+
+ if (!file_tracker) {
+ file_tracker = egg_file_tracker_new ("~/.ssh", "*.pub", NULL);
+ g_signal_connect (file_tracker, "file-added", G_CALLBACK (file_load_inlock), NULL);
+ g_signal_connect (file_tracker, "file-removed", G_CALLBACK (file_remove_inlock), NULL);
+ }
+
+ egg_file_tracker_refresh (file_tracker, FALSE);
+
+ return TRUE;
+}
+
+static void
+preload_unlock (void)
+{
+ g_mutex_unlock (&preload_mutex);
+}
+
+GList *
+gkd_ssh_agent_preload_keys (void)
+{
+ GList *keys = NULL;
+ GHashTableIter iter;
+ Preload *preload;
+
+ preload_lock_and_update ();
+
+ g_hash_table_iter_init (&iter, preloads_by_key);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *)preload)) {
+ if (preload->private_file)
+ keys = g_list_prepend (keys, g_bytes_ref (preload->public_key));
+ }
+
+ preload_unlock ();
+
+ return keys;
+}
+
+gchar *
+gkd_ssh_agent_preload_comment (GBytes *key)
+{
+ gchar *comment = NULL;
+ Preload *preload;
+
+ preload_lock_and_update ();
+
+ preload = g_hash_table_lookup (preloads_by_key, key);
+ if (preload)
+ comment = g_strdup (preload->comment);
+
+ preload_unlock ();
+
+ return comment;
+}
+
+gboolean
+gkd_ssh_agent_proto_write_pair_rsa (EggBuffer *req,
+ gsize *offset,
+ GckAttributes *priv_attrs,
+ GckAttributes *pub_attrs)
+{
+ const GckAttribute *attr;
+
+ g_assert (req);
+ g_assert (offset);
+ g_assert (priv_attrs);
+ g_assert (pub_attrs);
+
+ if (!gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_MODULUS) ||
+ !gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_PUBLIC_EXPONENT) ||
+ !gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_PRIVATE_EXPONENT) ||
+ !gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_COEFFICIENT) ||
+ !gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_PRIME_1) ||
+ !gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_PRIME_2))
+ return FALSE;
+
+ /* Copy attributes to the public key */
+ attr = gck_builder_find (priv_attrs, CKA_MODULUS);
+ gck_builder_add_attribute (pub_attrs, attr);
+ attr = gck_builder_find (priv_attrs, CKA_PUBLIC_EXPONENT);
+ gck_builder_add_attribute (pub_attrs, attr);
+
+ /* Add in your basic other required attributes */
+ gck_builder_add_ulong (priv_attrs, CKA_CLASS, CKO_PRIVATE_KEY);
+ gck_builder_add_ulong (priv_attrs, CKA_KEY_TYPE, CKK_RSA);
+ gck_builder_add_ulong (pub_attrs, CKA_CLASS, CKO_PUBLIC_KEY);
+ gck_builder_add_ulong (pub_attrs, CKA_KEY_TYPE, CKK_RSA);
+
+ return TRUE;
+}
+
+gboolean
+gkd_ssh_agent_proto_read_pair_dsa (EggBuffer *req,
+ gsize *offset,
+ GckBuilder *priv_attrs,
+ GckBuilder *pub_attrs)
+{
+ const GckAttribute *attr;
+
+ g_assert (req);
+ g_assert (offset);
+ g_assert (priv_attrs);
+ g_assert (pub_attrs);
+
+ if (!gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_PRIME) ||
+ !gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_SUBPRIME) ||
+ !gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_BASE) ||
+ !gkd_ssh_agent_proto_read_mpi (req, offset, pub_attrs, CKA_VALUE) ||
+ !gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_VALUE))
+ return FALSE;
+
+ /* Copy attributes to the public key */
+ attr = gck_builder_find (priv_attrs, CKA_PRIME);
+ gck_builder_add_attribute (pub_attrs, attr);
+ attr = gck_builder_find (priv_attrs, CKA_SUBPRIME);
+ gck_builder_add_attribute (pub_attrs, attr);
+ attr = gck_builder_find (priv_attrs, CKA_BASE);
+ gck_builder_add_attribute (pub_attrs, attr);
+
+ /* Add in your basic other required attributes */
+ gck_builder_add_ulong (priv_attrs, CKA_CLASS, CKO_PRIVATE_KEY);
+ gck_builder_add_ulong (priv_attrs, CKA_KEY_TYPE, CKK_DSA);
+ gck_builder_add_ulong (pub_attrs, CKA_CLASS, CKO_PUBLIC_KEY);
+ gck_builder_add_ulong (pub_attrs, CKA_KEY_TYPE, CKK_DSA);
+
+ return TRUE;
+}
+
+static GBytes *
+encode_key_pair ()
+{
+
+}
+
+GBytes *
+gkd_ssh_agent_preload_private (GBytes *key)
+{
+ GcrParsed *parsed = NULL;
+ gchar *comment = NULL;
+ GBytes *contents = NULL;
+ GcrParser *parser;
+ Preload *preload;
+
+ preload_lock_and_update ();
+
+ preload = g_hash_table_lookup (preloads_by_key, key);
+ if (preload) {
+ if (preload->private_file)
+ contents = g_bytes_ref (preload->private_file);
+ comments = g_strdup (preload->comment);
+ }
+
+ preload_unlock ();
+
+ if (!contents)
+ return NULL;
+
+ parser = gcr_parser_new ();
+ gcr_parser_format_disable (parser, GCR_FORMAT_ALL);
+ gcr_parser_format_enable (parser, GCR_FORMAT_PEM);
+ g_signal_connect (parser, "authenticate", G_CALLBACK (on_parser_authenticate), comment);
+ g_signal_connect (parser, "parsed", G_CALLBACK (on_parser_parsed), &parsed);
+
+ if (!gcr_parser_parse_bytes (parser, contents, &error))
+ g_message ("couldn't parse private key: %s: %s", comment, error->message);
+
+ g_free (comment);
+ g_bytes_unref (contents);
+
+ if (parsed) {
+ gcr_parsed_get_attributes ();
+ }
+
+
+ g_object_unref (parser);
+ /* TODO */
+}
+
+void
+gkd_ssh_agent_preload_clear (GBytes *key)
+{
+ Preload *preload;
+
+ preload_lock_and_update ();
+
+ preload = g_hash_table_lookup (preloads_by_key, key);
+ if (preload) {
+ g_bytes_unref (preload->private_file);
+ preload->private_file = NULL;
+ }
+
+ preload_unlock ();
+}
+
+void
+gkd_ssh_agent_preload_clear_all (void)
+{
+ GHashTableIter iter;
+ Preload *preload;
+
+ preload_lock_and_update ();
+
+ g_hash_table_iter_init (&iter, preloads_key_key);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&preload)) {
+ if (preload->private_file) {
+ g_bytes_unref (preload->private_file);
+ preload->private_file = NULL;
+ }
+ }
+
+ preload_unlock ();
+}
+
+void
+gkd_ssh_agent_preload_cleanup (void)
+{
+ g_mutex_lock (&preload_mutex);
+
+ if (preloads_by_key)
+ g_hash_table_destroy (prelodas_by_key);
+ preloads_by_key = NULL;
+
+ if (preloads_by_filename)
+ g_hash_table_destroy (preloads_by_filename);
+ preloads_by_filename = NULL;
+
+ if (file_tracker)
+ g_object_unref (file_tracker);
+ file_tracker = NULL;
+
+ g_mutex_unlock (&preload_mutex);
+}
diff --git a/daemon/ssh-agent/gkd-ssh-agent-preload.h b/daemon/ssh-agent/gkd-ssh-agent-preload.h
new file mode 100644
index 0000000..503b25e
--- /dev/null
+++ b/daemon/ssh-agent/gkd-ssh-agent-preload.h
@@ -0,0 +1,42 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2014 Stef Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stef thewalter net>
+ */
+
+#ifndef __GKD_SSH_AGENT_PRELOAD_H__
+#define __GKD_SSH_AGENT_PRELOAD_H__
+
+#include <glib.h>
+
+GList * gkd_ssh_agent_preload_keys (void);
+
+gchar * gkd_ssh_agent_preload_comment (GBytes *key);
+
+GBytes * gkd_ssh_agent_preload_private (GBytes *key);
+
+void gkd_ssh_agent_preload_clear (GBytes *key);
+
+void gkd_ssh_agent_preload_clear_all (void);
+
+void gkd_ssh_agent_preload_cleanup (void);
+
+#endif /* __GKD_SSH_AGENT_PRELOAD_H__ */
+
diff --git a/daemon/ssh-agent/gkd-ssh-agent-private.h b/daemon/ssh-agent/gkd-ssh-agent-private.h
index d6c79cd..d477bf6 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-private.h
+++ b/daemon/ssh-agent/gkd-ssh-agent-private.h
@@ -33,7 +33,7 @@ typedef struct _GkdSshAgentCall {
int sock;
EggBuffer *req;
EggBuffer *resp;
- GkdSshAgentClient *agent;
+ gint ssh_agent;
} GkdSshAgentCall;
/* -----------------------------------------------------------------------------
diff --git a/daemon/ssh-agent/gkd-ssh-agent.c b/daemon/ssh-agent/gkd-ssh-agent.c
index ad84756..385cf97 100644
--- a/daemon/ssh-agent/gkd-ssh-agent.c
+++ b/daemon/ssh-agent/gkd-ssh-agent.c
@@ -137,7 +137,8 @@ gkd_ssh_agent_write_packet (gint fd,
gboolean
gkd_ssh_agent_relay (GkdSshAgentCall *call)
{
- return gkd_ssh_agent_client_transact (call->agent, call->req, call->resp);
+ return gkd_ssh_agent_write_packet (call->ssh_agent, call->req) &&
+ gkd_ssh_agent_read_packet (call->ssh_agent, call->resp);
}
static gpointer
@@ -159,8 +160,8 @@ run_client_thread (gpointer data)
call.req = &req;
call.resp = &resp;
- call.agent = gkd_ssh_agent_client_connect ();
- if (!call.agent)
+ call.ssh_agent = gkd_ssh_agent_client_connect ();
+ if (call.ssh_agent < 0)
goto out;
for (;;) {
diff --git a/daemon/ssh-agent/gkd-ssh-openssh.h b/daemon/ssh-agent/gkd-ssh-openssh.h
new file mode 100644
index 0000000..382e3c6
--- /dev/null
+++ b/daemon/ssh-agent/gkd-ssh-openssh.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 Stef 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 thewalter net>
+ */
+
+#ifndef GKD_SSH_OPENSSH_H_
+#define GKD_SSH_OPENSSH_H_
+
+#include <glib.h>
+
+GBytes * gkd_ssh_openssh_parse_public_key (GBytes *data,
+ gchar **comment);
+
+#endif /* GKD_SSH_OPENSSH_H_ */
diff --git a/daemon/ssh-agent/gkm-ssh-openssh.c b/daemon/ssh-agent/gkm-ssh-openssh.c
new file mode 100644
index 0000000..c3a59a8
--- /dev/null
+++ b/daemon/ssh-agent/gkm-ssh-openssh.c
@@ -0,0 +1,409 @@
+
+#include "gkm-ssh-openssh.h"
+
+#include "gkm/gkm-data-asn1.h"
+#include "gkm/gkm-data-der.h"
+#include "gkm/gkm-data-types.h"
+
+#include "egg/egg-armor.h"
+#include "egg/egg-asn1x.h"
+#include "egg/egg-buffer.h"
+#include "egg/egg-openssl.h"
+#include "egg/egg-secure-memory.h"
+
+typedef struct _ParsePrivate {
+ gcry_sexp_t sexp;
+ gboolean seen;
+ GkmDataResult result;
+ const gchar *password;
+ gssize n_password;
+} ParsePrivate;
+
+/* ------------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static int
+keytype_to_algo (const gchar *salgo)
+{
+ g_return_val_if_fail (salgo, 0);
+ if (strcmp (salgo, "ssh-rsa") == 0)
+ return GCRY_PK_RSA;
+ else if (strcmp (salgo, "ssh-dss") == 0)
+ return GCRY_PK_DSA;
+ return 0;
+}
+
+static gboolean
+read_mpi (EggBuffer *req, gsize *offset, gcry_mpi_t *mpi)
+{
+ const guchar *data;
+ gsize len;
+ gcry_error_t gcry;
+
+ if (!egg_buffer_get_byte_array (req, *offset, offset, &data, &len))
+ return FALSE;
+
+ gcry = gcry_mpi_scan (mpi, GCRYMPI_FMT_USG, data, len, NULL);
+ if (gcry)
+ return FALSE;
+
+ return TRUE;
+}
+
+#define SEXP_PUBLIC_DSA \
+ "(public-key" \
+ " (dsa" \
+ " (p %m)" \
+ " (q %m)" \
+ " (g %m)" \
+ " (y %m)))"
+
+static gboolean
+read_public_dsa (EggBuffer *req, gsize *offset, gcry_sexp_t *sexp)
+{
+ gcry_mpi_t p, q, g, y;
+ int gcry;
+
+ if (!read_mpi (req, offset, &p) ||
+ !read_mpi (req, offset, &q) ||
+ !read_mpi (req, offset, &g) ||
+ !read_mpi (req, offset, &y))
+ return FALSE;
+
+ gcry = gcry_sexp_build (sexp, NULL, SEXP_PUBLIC_DSA, p, q, g, y);
+ if (gcry) {
+ g_warning ("couldn't parse incoming public DSA key: %s", gcry_strerror (gcry));
+ return FALSE;
+ }
+
+ gcry_mpi_release (p);
+ gcry_mpi_release (q);
+ gcry_mpi_release (g);
+ gcry_mpi_release (y);
+
+ return TRUE;
+}
+
+#define SEXP_PUBLIC_RSA \
+ "(public-key" \
+ " (rsa" \
+ " (n %m)" \
+ " (e %m)))"
+
+static gboolean
+read_public_rsa (EggBuffer *req, gsize *offset, gcry_sexp_t *sexp)
+{
+ gcry_mpi_t n, e;
+ int gcry;
+
+ if (!read_mpi (req, offset, &e) ||
+ !read_mpi (req, offset, &n))
+ return FALSE;
+
+ gcry = gcry_sexp_build (sexp, NULL, SEXP_PUBLIC_RSA, n, e);
+ if (gcry) {
+ g_warning ("couldn't parse incoming public RSA key: %s", gcry_strerror (gcry));
+ return FALSE;
+ }
+
+ gcry_mpi_release (n);
+ gcry_mpi_release (e);
+
+ return TRUE;
+}
+
+static gboolean
+read_public (EggBuffer *req, gsize *offset, gcry_sexp_t *key, int *algo)
+{
+ gboolean ret;
+ gchar *stype;
+ int alg;
+
+ /* The string algorithm */
+ if (!egg_buffer_get_string (req, *offset, offset, &stype, (EggBufferAllocator)g_realloc))
+ return FALSE;
+
+ alg = keytype_to_algo (stype);
+ g_free (stype);
+
+ if (!alg) {
+ g_warning ("unsupported algorithm from SSH: %s", stype);
+ return FALSE;
+ }
+
+ switch (alg) {
+ case GCRY_PK_RSA:
+ ret = read_public_rsa (req, offset, key);
+ break;
+ case GCRY_PK_DSA:
+ ret = read_public_dsa (req, offset, key);
+ 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 TRUE;
+}
+
+static GkmDataResult
+load_encrypted_key (GBytes *data,
+ const gchar *dekinfo,
+ const gchar *password,
+ gssize n_password,
+ gcry_sexp_t *skey)
+{
+ guchar *decrypted = NULL;
+ gsize n_decrypted = 0;
+ GBytes *bytes;
+ GkmDataResult ret;
+ gint length;
+
+ /* Decrypt, this will result in garble if invalid password */
+ decrypted = egg_openssl_decrypt_block (dekinfo, password, n_password,
+ data, &n_decrypted);
+ if (!decrypted)
+ return FALSE;
+
+ /* Unpad the DER data */
+ length = egg_asn1x_element_length (decrypted, n_decrypted);
+ if (length > 0)
+ n_decrypted = length;
+
+ bytes = g_bytes_new_with_free_func (decrypted, n_decrypted, egg_secure_free, decrypted);
+
+ /* Try to parse */
+ ret = gkm_data_der_read_private_key (bytes, skey);
+ g_bytes_unref (bytes);
+
+ if (ret != GKM_DATA_UNRECOGNIZED)
+ return ret;
+
+ return GKM_DATA_LOCKED;
+}
+
+static gboolean
+is_private_key_type (GQuark type)
+{
+ static GQuark PEM_RSA_PRIVATE_KEY;
+ static GQuark PEM_DSA_PRIVATE_KEY;
+ static gsize quarks_inited = 0;
+
+ /* Initialize the first time through */
+ if (g_once_init_enter (&quarks_inited)) {
+ PEM_RSA_PRIVATE_KEY = g_quark_from_static_string ("RSA PRIVATE KEY");
+ PEM_DSA_PRIVATE_KEY = g_quark_from_static_string ("DSA PRIVATE KEY");
+ g_once_init_leave (&quarks_inited, 1);
+ }
+
+ /* Only handle SSHv2 private keys */
+ return (type == PEM_RSA_PRIVATE_KEY || type == PEM_DSA_PRIVATE_KEY);
+}
+
+static void
+parsed_pem_block (GQuark type,
+ GBytes *data,
+ GBytes *outer,
+ GHashTable *headers,
+ gpointer user_data)
+{
+ ParsePrivate *ctx = (ParsePrivate*)user_data;
+ const gchar *dekinfo;
+
+ if (!is_private_key_type (type))
+ return;
+
+ ctx->seen = TRUE;
+
+ /* Only parse first key in the file */
+ if (ctx->sexp)
+ return;
+
+ /* If it's encrypted ... */
+ dekinfo = egg_openssl_get_dekinfo (headers);
+ if (dekinfo) {
+ ctx->result = load_encrypted_key (data, dekinfo, ctx->password,
+ ctx->n_password, &ctx->sexp);
+
+ /* not encryted, just load the data */
+ } else {
+ ctx->result = gkm_data_der_read_private_key (data, &ctx->sexp);
+ }
+}
+
+static void
+digest_pem_block (GQuark type,
+ GBytes *data,
+ GBytes *outer,
+ GHashTable *headers,
+ gpointer user_data)
+{
+ gchar **result = (gchar**)user_data;
+
+ g_assert (result);
+
+ if (!is_private_key_type (type))
+ return;
+
+ /* Only digest the first key in the file */
+ if (*result != NULL)
+ return;
+
+ *result = g_compute_checksum_for_data (G_CHECKSUM_SHA1,
+ g_bytes_get_data (data, NULL),
+ g_bytes_get_size (data));
+}
+
+/* ------------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+GkmDataResult
+gkm_ssh_openssh_parse_public_key (gconstpointer input, gsize n_data,
+ gcry_sexp_t *sexp, gchar **comment)
+{
+ EggBuffer buf;
+ const guchar *at;
+ guchar *decoded;
+ gsize n_decoded;
+ gsize offset;
+ gchar *val;
+ gboolean ret;
+ gint state, algo;
+ guint save;
+ const guchar *data = input;
+
+ g_return_val_if_fail (data, FALSE);
+ g_return_val_if_fail (sexp, FALSE);
+
+ /* Look for a key line */
+ for (;;) {
+ /* Eat space at the front */
+ while (n_data > 0 && g_ascii_isspace (data[0])) {
+ ++data;
+ --n_data;
+ }
+
+ /* Not a comment or blank line? Then parse... */
+ if (data[0] != '#')
+ break;
+
+ /* Skip to the next line */
+ at = memchr (data, '\n', n_data);
+ if (!at)
+ return GKM_DATA_UNRECOGNIZED;
+ at += 1;
+ n_data -= (at - data);
+ data = at;
+ }
+
+ /* Limit to use only the first line */
+ at = memchr (data, '\n', n_data);
+ if (at != NULL)
+ n_data = at - data;
+
+ /* Find the first space */
+ at = memchr (data, ' ', n_data);
+ if (!at) {
+ g_message ("SSH public key missing space");
+ return GKM_DATA_UNRECOGNIZED;
+ }
+
+ /* Parse the key type */
+ val = g_strndup ((gchar*)data, at - data);
+ algo = keytype_to_algo (val);
+ if (!algo) {
+ /* A number usually means an SSH1 key, just quietly ignore */
+ if (atoi (val) == 0)
+ g_message ("Unsupported or unknown SSH key algorithm: %s", val);
+ }
+ g_free (val);
+ if (!algo)
+ return GKM_DATA_UNRECOGNIZED;
+
+ /* Skip more whitespace */
+ n_data -= (at - data);
+ data = at;
+ while (n_data > 0 && (data[0] == ' ' || data[0] == '\t')) {
+ ++data;
+ --n_data;
+ }
+
+ /* Find the next whitespace, or the end */
+ at = memchr (data, ' ', n_data);
+ if (at == NULL)
+ at = data + n_data;
+
+ /* Decode the base64 key */
+ save = state = 0;
+ decoded = g_malloc (n_data * 3 / 4);
+ n_decoded = g_base64_decode_step ((gchar*)data, n_data, decoded, &state, &save);
+
+ /* Parse the actual key */
+ egg_buffer_init_static (&buf, decoded, n_decoded);
+ offset = 0;
+ ret = read_public (&buf, &offset, sexp, NULL);
+ g_free (decoded);
+ if (!ret) {
+ g_message ("failed to parse base64 part of SSH key");
+ return GKM_DATA_FAILURE;
+ }
+
+ /* Skip more whitespace */
+ n_data -= (at - data);
+ data = at;
+ while (n_data > 0 && (data[0] == ' ' || data[0] == '\t')) {
+ ++data;
+ --n_data;
+ }
+
+ /* If there's data left, its the comment */
+ if (comment)
+ *comment = n_data ? g_strndup ((gchar*)data, n_data) : NULL;
+
+ return GKM_DATA_SUCCESS;
+}
+
+GkmDataResult
+gkm_ssh_openssh_parse_private_key (GBytes *data,
+ const gchar *password,
+ gssize n_password,
+ gcry_sexp_t *sexp)
+{
+ ParsePrivate ctx;
+ guint num;
+
+ memset (&ctx, 0, sizeof (ctx));
+ ctx.result = FALSE;
+ ctx.seen = FALSE;
+ ctx.sexp = NULL;
+ ctx.password = password;
+ ctx.n_password = n_password;
+
+ num = egg_armor_parse (data, parsed_pem_block, &ctx);
+
+ /* Didn't find any private key there */
+ if (num == 0 || !ctx.seen) {
+ g_message ("no private keys found in file");
+ return GKM_DATA_UNRECOGNIZED;
+ }
+
+ *sexp = ctx.sexp;
+ return ctx.result;
+}
+
+gchar *
+gkm_ssh_openssh_digest_private_key (GBytes *data)
+{
+ gchar *result = NULL;
+ egg_armor_parse (data, digest_pem_block, &result);
+ return result;
+}
diff --git a/daemon/ssh-agent/test-ssh-openssh.c b/daemon/ssh-agent/test-ssh-openssh.c
new file mode 100644
index 0000000..e1c4fb7
--- /dev/null
+++ b/daemon/ssh-agent/test-ssh-openssh.c
@@ -0,0 +1,138 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ Copyright (C) 2008 Stefan Walter
+
+ The Gnome Keyring Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Keyring Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ <http://www.gnu.org/licenses/>.
+
+ Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "config.h"
+
+#include "egg/egg-secure-memory.h"
+
+#include "ssh-store/gkm-ssh-openssh.h"
+
+#include "gkm/gkm-sexp.h"
+
+#include <glib.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+EGG_SECURE_DEFINE_GLIB_GLOBALS ();
+
+static const gchar *PRIVATE_FILES[] = {
+ SRCDIR "/pkcs11/ssh-store/fixtures/id_rsa_encrypted",
+ SRCDIR "/pkcs11/ssh-store/fixtures/id_rsa_plain",
+ SRCDIR "/pkcs11/ssh-store/fixtures/id_dsa_encrypted",
+ SRCDIR "/pkcs11/ssh-store/fixtures/id_dsa_plain"
+};
+
+static const gchar *PUBLIC_FILES[] = {
+ SRCDIR "/pkcs11/ssh-store/fixtures/id_rsa_test.pub",
+ SRCDIR "/pkcs11/ssh-store/fixtures/id_dsa_test.pub"
+};
+
+#define COMMENT "A public key comment"
+
+static void
+test_parse_public (void)
+{
+ gcry_sexp_t sexp;
+ gchar *comment;
+ gchar *data;
+ gsize n_data;
+ int algorithm;
+ gboolean is_private;
+ guint i;
+ GkmDataResult res;
+
+
+ for (i = 0; i < G_N_ELEMENTS (PUBLIC_FILES); ++i) {
+
+ if (!g_file_get_contents (PUBLIC_FILES[i], &data, &n_data, NULL))
+ g_assert_not_reached ();
+
+ res = gkm_ssh_openssh_parse_public_key (data, n_data, &sexp, &comment);
+ if (res != GKM_DATA_SUCCESS) {
+ g_warning ("couldn't parse public key: %s", PUBLIC_FILES[i]);
+ g_assert_cmpint (res, ==, GKM_DATA_SUCCESS);
+ }
+
+ if (!gkm_sexp_parse_key (sexp, &algorithm, &is_private, NULL))
+ g_assert_not_reached ();
+
+ g_assert_cmpstr (comment, ==, COMMENT);
+ g_assert_cmpint (algorithm, !=, 0);
+ g_assert (!is_private);
+
+ g_free (data);
+ g_free (comment);
+ gcry_sexp_release (sexp);
+ }
+}
+
+static void
+test_parse_private (void)
+{
+ gcry_sexp_t sexp;
+ gchar *data;
+ gsize n_data;
+ int algorithm;
+ gboolean is_private;
+ GBytes *bytes;
+ guint i;
+ GkmDataResult res;
+
+
+ for (i = 0; i < G_N_ELEMENTS (PRIVATE_FILES); ++i) {
+
+ if (!g_file_get_contents (PRIVATE_FILES[i], &data, &n_data, NULL))
+ g_assert_not_reached ();
+
+ bytes = g_bytes_new_take (data, n_data);
+ res = gkm_ssh_openssh_parse_private_key (bytes, "password", 8, &sexp);
+ g_bytes_unref (bytes);
+
+ if (res != GKM_DATA_SUCCESS) {
+ g_warning ("couldn't parse private key: %s", PRIVATE_FILES[i]);
+ g_assert_cmpint (res, ==, GKM_DATA_SUCCESS);
+ }
+
+ if (!gkm_sexp_parse_key (sexp, &algorithm, &is_private, NULL))
+ g_assert_not_reached ();
+
+ g_assert_cmpint (algorithm, !=, 0);
+ g_assert (is_private);
+
+ gcry_sexp_release (sexp);
+ }
+}
+
+int
+main (int argc, char **argv)
+{
+#if !GLIB_CHECK_VERSION(2,35,0)
+ g_type_init ();
+#endif
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/ssh-store/openssh/parse_private", test_parse_private);
+ g_test_add_func ("/ssh-store/openssh/parse_public", test_parse_public);
+
+ return g_test_run ();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]