aboutsummaryrefslogtreecommitdiffstats
path: root/agent/command-ssh.c
diff options
context:
space:
mode:
Diffstat (limited to 'agent/command-ssh.c')
-rw-r--r--agent/command-ssh.c2402
1 files changed, 1483 insertions, 919 deletions
diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index 2535b58b1..18bd56685 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -1,4 +1,4 @@
-/* command-ssh.c - gpg-agent's ssh-agent emulation
+/* command-ssh.c - gpg-agent's ssh-agent emulation layer
* Copyright (C) 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
@@ -25,6 +25,7 @@
#include <string.h>
#include <errno.h>
#include <sys/types.h>
+#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
@@ -32,7 +33,7 @@
#include <gcrypt.h>
-#include "gpg-stream.h"
+#include "estream.h"
@@ -63,180 +64,178 @@
/* A "byte". */
typedef unsigned char byte_t;
-/* A "mpint". */
-typedef gcry_mpi_t mpint_t;
+typedef int (*ssh_request_handler_t) (ctrl_t ctrl,
+ estream_t request, estream_t response);
-
-
-/* SSH specific types. */
-
-typedef byte_t ssh_request_type_t;
-typedef byte_t ssh_response_type_t;
-
-/* A "Packet header"; part of a Request/Response. */
-typedef struct ssh_packet_header
+typedef struct ssh_request_spec
{
- uint32_t length;
byte_t type;
-} ssh_packet_header_t;
+ ssh_request_handler_t handler;
+} ssh_request_spec_t;
-/* A "Key type". */
-typedef enum ssh_key_type
- {
- SSH_KEY_TYPE_NONE,
- SSH_KEY_TYPE_RSA,
- } ssh_key_type_t;
+typedef gpg_error_t (*ssh_key_modifier_t) (const char *elems, gcry_mpi_t *mpis);
+typedef gpg_error_t (*ssh_signature_encoder_t) (estream_t signature_blob,
+ gcry_mpi_t *mpis);
-/* Type used for associating Key types with their string
- representation. */
typedef struct ssh_key_type_spec
{
- ssh_key_type_t type;
- const char *name;
+ const char *ssh_identifier;
+ const char *identifier;
+ const char *elems_key_secret;
+ const char *elems_key_public;
+ const char *elems_secret;
+ const char *elems_signature;
+ const char *elems_sexp_order;
+ ssh_key_modifier_t key_modifier;
+ ssh_signature_encoder_t signature_encoder;
+ unsigned int flags;
} ssh_key_type_spec_t;
-/* Secret RSA key material. */
-typedef struct ssh_key_secret_rsa
-{
- mpint_t n;
- mpint_t e;
- mpint_t d;
- mpint_t p;
- mpint_t q;
- mpint_t u;
-} ssh_key_secret_rsa_t;
-
-/* Public RSA key material. */
-typedef struct ssh_key_public_rsa
-{
- mpint_t e;
- mpint_t n;
-} ssh_key_public_rsa_t;
-
-/* A secret key. */
-typedef struct ssh_key_secret
-{
- ssh_key_type_t type;
- union
- {
- ssh_key_secret_rsa_t rsa;
- } material;
-} ssh_key_secret_t;
+
-/* A public key. */
-typedef struct ssh_key_public
-{
- ssh_key_type_t type;
- union
- {
- ssh_key_public_rsa_t rsa;
- } material;
-} ssh_key_public_t;
+static uint32_t lifetime_default;
-typedef void (*ssh_request_handler_t) (ctrl_t ctrl,
- gpg_stream_t request,
- gpg_stream_t response);
+/* Primitive I/O functions. */
-typedef struct ssh_request_spec
+static gpg_error_t
+es_read_byte (estream_t stream, byte_t *b)
{
- ssh_request_type_t type;
- ssh_request_handler_t handler;
-} ssh_request_spec_t;
+ gpg_error_t err;
+ int ret;
-
+ ret = es_fgetc (stream);
+ if (ret == EOF)
+ {
+ if (es_ferror (stream))
+ err = gpg_error_from_errno (errno);
+ else
+ err = gpg_error (GPG_ERR_EOF);
+ }
+ else
+ {
+ *b = ret & 0xFF;
+ err = 0;
+ }
-/* Table associating numeric key types with their string
- representation. */
-static ssh_key_type_spec_t ssh_key_types[] =
- {
- { SSH_KEY_TYPE_RSA, "ssh-rsa" }
- };
+ return err;
+}
-static uint32_t lifetime_default;
+static gpg_error_t
+es_write_byte (estream_t stream, byte_t b)
+{
+ gpg_error_t err;
+ int ret;
-
+ ret = es_fputc (b, stream);
+ if (ret == EOF)
+ err = gpg_error_from_errno (errno);
+ else
+ err = 0;
-/* Primitive I/O functions. */
+ return err;
+}
-static gpg_err_code_t
-gpg_stream_read_byte (gpg_stream_t stream, byte_t *b)
+static gpg_error_t
+es_read_uint32 (estream_t stream, uint32_t *uint32)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- unsigned char buffer[1];
- size_t bytes_read = 0;
-
- err = gpg_stream_read (stream, buffer, sizeof (buffer), &bytes_read);
- if ((! err) && (bytes_read != sizeof (buffer)))
- err = GPG_ERR_EOF;
-
- if (! err)
- *b = buffer[0];
+ unsigned char buffer[4];
+ size_t bytes_read;
+ gpg_error_t err;
+ int ret;
+
+ ret = es_read (stream, buffer, sizeof (buffer), &bytes_read);
+ if (ret)
+ err = gpg_error_from_errno (errno);
+ else
+ {
+ if (bytes_read != sizeof (buffer))
+ err = gpg_error (GPG_ERR_EOF);
+ else
+ {
+ uint32_t n;
+
+ n = (0
+ | ((uint32_t) (buffer[0] << 24))
+ | ((uint32_t) (buffer[1] << 16))
+ | ((uint32_t) (buffer[2] << 8))
+ | ((uint32_t) (buffer[3] << 0)));
+ *uint32 = n;
+ err = 0;
+ }
+ }
return err;
}
-static gpg_err_code_t
-gpg_stream_write_byte (gpg_stream_t stream, byte_t b)
+static gpg_error_t
+es_write_uint32 (estream_t stream, uint32_t uint32)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
-
- err = gpg_stream_write (stream, &b, sizeof (b), NULL);
+ unsigned char buffer[4];
+ gpg_error_t err;
+ int ret;
+
+ buffer[0] = (uint32 >> 24) & 0xFF;
+ buffer[1] = (uint32 >> 16) & 0xFF;
+ buffer[2] = (uint32 >> 8) & 0xFF;
+ buffer[3] = (uint32 >> 0) & 0xFF;
+
+ ret = es_write (stream, buffer, sizeof (buffer), NULL);
+ if (ret)
+ err = gpg_error_from_errno (errno);
+ else
+ err = 0;
return err;
}
-static gpg_err_code_t
-gpg_stream_read_uint32 (gpg_stream_t stream, uint32_t *uint32)
+static gpg_error_t
+es_read_data (estream_t stream, unsigned char *buffer, size_t size)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- unsigned char buffer[4] = { 0 };
- size_t bytes_read = 0;
- uint32_t n = 0;
+ gpg_error_t err;
+ size_t bytes_read;
+ int ret;
- err = gpg_stream_read (stream, buffer, sizeof (buffer), &bytes_read);
- if ((! err) && (bytes_read != sizeof (buffer)))
- err = GPG_ERR_EOF;
-
- if (! err)
+ ret = es_read (stream, buffer, size, &bytes_read);
+ if (ret)
+ err = gpg_error_from_errno (errno);
+ else
{
- n = (0
- | ((uint32_t) (buffer[0] << 24))
- | ((uint32_t) (buffer[1] << 16))
- | ((uint32_t) (buffer[2] << 8))
- | ((uint32_t) (buffer[3] << 0)));
- *uint32 = n;
+ if (bytes_read != size)
+ err = gpg_error (GPG_ERR_EOF);
+ else
+ err = 0;
}
return err;
}
-static gpg_err_code_t
-gpg_stream_write_uint32 (gpg_stream_t stream, uint32_t uint32)
+static gpg_error_t
+es_write_data (estream_t stream, const unsigned char *buffer, size_t size)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- unsigned char buffer[4] = { 0 };
-
- buffer[0] = uint32 >> 24;
- buffer[1] = uint32 >> 16;
- buffer[2] = uint32 >> 8;
- buffer[3] = uint32 >> 0;
+ gpg_error_t err;
+ int ret;
- err = gpg_stream_write (stream, buffer, sizeof (buffer), NULL);
+ ret = es_write (stream, buffer, size, NULL);
+ if (ret)
+ err = gpg_error_from_errno (errno);
+ else
+ err = 0;
return err;
}
-static gpg_err_code_t
-gpg_stream_read_string (gpg_stream_t stream, unsigned int secure,
- unsigned char **string, uint32_t *string_size)
+static gpg_error_t
+es_read_string (estream_t stream, unsigned int secure,
+ unsigned char **string, uint32_t *string_size)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- unsigned char *buffer = NULL;
- size_t bytes_read = 0;
- uint32_t length = 0;
+ gpg_error_t err;
+ unsigned char *buffer;
+ uint32_t length;
+
+ buffer = NULL;
/* Read string length. */
- err = gpg_stream_read_uint32 (stream, &length);
+ err = es_read_uint32 (stream, &length);
if (err)
goto out;
@@ -247,113 +246,117 @@ gpg_stream_read_string (gpg_stream_t stream, unsigned int secure,
buffer = gcry_malloc (length + 1);
if (! buffer)
{
- err = gpg_err_code_from_errno (errno);
+ /* FIXME: gcry_malloc_secure does not set errno, does it? */
+ err = gpg_error_from_errno (errno);
+ abort ();
goto out;
}
/* Read data. */
- err = gpg_stream_read (stream, buffer, length, &bytes_read);
- if ((! err) && (bytes_read != length))
- err = GPG_ERR_EOF;
+ err = es_read_data (stream, buffer, length);
if (err)
goto out;
/* Finalize string object. */
buffer[length] = 0;
+ *string = buffer;
+ if (string_size)
+ *string_size = length;
out:
- if (! err)
- {
- *string = buffer;
- if (string_size)
- *string_size = length;
- }
- else
- if (buffer)
- gcry_free (buffer);
+ if (err)
+ gcry_free (buffer);
return err;
}
-static gpg_err_code_t
-gpg_stream_write_string (gpg_stream_t stream,
- const unsigned char *string, uint32_t string_n)
+static gpg_error_t
+es_read_cstring (estream_t stream, char **string)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
+ unsigned char *buffer;
+ gpg_error_t err;
- err = gpg_stream_write_uint32 (stream, string_n);
+ err = es_read_string (stream, 0, &buffer, NULL);
if (err)
goto out;
+
+ *string = (char *) buffer;
- err = gpg_stream_write (stream, string, string_n, NULL);
+ out:
+
+ return err;
+}
+
+static gpg_error_t
+es_write_string (estream_t stream,
+ const unsigned char *string, uint32_t string_n)
+{
+ gpg_error_t err;
+
+ err = es_write_uint32 (stream, string_n);
if (err)
goto out;
+ err = es_write_data (stream, string, string_n);
+
out:
return err;
}
-static gpg_err_code_t
-gpg_stream_write_cstring (gpg_stream_t stream, char *string)
+static gpg_error_t
+es_write_cstring (estream_t stream, const char *string)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
+ gpg_error_t err;
- err = gpg_stream_write_string (stream, (char *) string, strlen (string));
+ err = es_write_string (stream,
+ (const unsigned char *) string, strlen (string));
return err;
}
-static gpg_err_code_t
-gpg_stream_read_mpint (gpg_stream_t stream, unsigned int secure,
- mpint_t *mpint, unsigned int mpi_type)
+static gpg_error_t
+es_read_mpi (estream_t stream, unsigned int secure, gcry_mpi_t *mpint)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- unsigned char *mpi_data = NULL;
- uint32_t mpi_data_size = 0;
- gcry_mpi_t mpi = NULL;
+ unsigned char *mpi_data;
+ uint32_t mpi_data_size;
+ gpg_error_t err;
+ gcry_mpi_t mpi;
- if (! mpi_type)
- mpi_type = GCRYMPI_FMT_STD;
+ mpi_data = NULL;
- err = gpg_stream_read_string (stream, secure,
- &mpi_data, &mpi_data_size);
+ err = es_read_string (stream, secure, &mpi_data, &mpi_data_size);
if (err)
goto out;
- err = gcry_mpi_scan (&mpi, mpi_type, mpi_data, mpi_data_size, NULL);
+ err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_STD, mpi_data, mpi_data_size, NULL);
if (err)
goto out;
+ *mpint = mpi;
+
out:
gcry_free (mpi_data);
- if (! err)
- *mpint = mpi;
-
return err;
}
-static gpg_err_code_t
-gpg_stream_write_mpint (gpg_stream_t stream,
- mpint_t mpint, unsigned int mpi_type)
+static gpg_error_t
+es_write_mpi (estream_t stream, gcry_mpi_t mpint)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- unsigned char *mpi_buffer = NULL;
- size_t mpi_buffer_n = 0;
+ unsigned char *mpi_buffer;
+ size_t mpi_buffer_n;
+ gpg_error_t err;
- if (! mpi_type)
- mpi_type = GCRYMPI_FMT_STD;
+ mpi_buffer = NULL;
- err = gcry_mpi_aprint (mpi_type, &mpi_buffer, &mpi_buffer_n, mpint);
+ err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &mpi_buffer, &mpi_buffer_n, mpint);
if (err)
goto out;
- err = gpg_stream_write_string (stream, mpi_buffer, mpi_buffer_n);
- if (err)
- goto out;
+ err = es_write_string (stream, mpi_buffer, mpi_buffer_n);
out:
@@ -362,508 +365,946 @@ gpg_stream_write_mpint (gpg_stream_t stream,
return err;
}
-static gpg_err_code_t
-gpg_stream_read_file (const char *filename,
- unsigned char **buffer, size_t *buffer_n)
+static gpg_error_t
+es_read_file (const char *filename, unsigned char **buffer, size_t *buffer_n)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- unsigned char *buffer_new = NULL;
- size_t buffer_new_n = 0;
- gpg_stream_t stream = NULL;
- size_t bytes_read = 0;
-
- err = gpg_stream_create_file (&stream, filename, GPG_STREAM_FLAG_READ);
- if (err)
- goto out;
+ unsigned char *buffer_new;
+ struct stat statbuf;
+ estream_t stream;
+ gpg_error_t err;
+ int ret;
+
+ buffer_new = NULL;
+ err = 0;
+
+ stream = es_fopen (filename, "r");
+ if (! stream)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
- err = gpg_stream_stat (stream, &buffer_new_n);
- if (err)
- goto out;
+ ret = fstat (es_fileno (stream), &statbuf);
+ if (ret)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
- buffer_new = gcry_malloc (buffer_new_n);
+ buffer_new = gcry_malloc (statbuf.st_size);
if (! buffer_new)
{
- err = gpg_err_code_from_errno (errno);
+ err = gpg_error_from_errno (errno);
goto out;
}
- err = gpg_stream_read (stream, buffer_new, buffer_new_n, &bytes_read);
- if ((! err) && (bytes_read != buffer_new_n))
- err = GPG_ERR_INTERNAL; /* FIXME? */
+ err = es_read_data (stream, buffer_new, statbuf.st_size);
if (err)
goto out;
+ *buffer = buffer_new;
+ *buffer_n = statbuf.st_size;
+
out:
- gpg_stream_destroy (stream);
+ if (stream)
+ es_fclose (stream);
- if (! err)
+ if (err)
+ gcry_free (buffer_new);
+
+ return err;
+}
+
+static gpg_error_t
+es_copy (estream_t dst, estream_t src)
+{
+ char buffer[BUFSIZ];
+ size_t bytes_read;
+ gpg_error_t err;
+ int ret;
+
+ err = 0;
+ while (1)
{
- *buffer = buffer_new;
- *buffer_n = buffer_new_n;
+ ret = es_read (src, buffer, sizeof (buffer), &bytes_read);
+ if (ret || (! bytes_read))
+ {
+ if (ret)
+ err = gpg_error_from_errno (errno);
+ break;
+ }
+ ret = es_write (dst, buffer, bytes_read, NULL);
+ if (ret)
+ {
+ err = gpg_error_from_errno (errno);
+ break;
+ }
}
- else
- gcry_free (buffer_new);
return err;
}
-/* Key I/O. */
+/* MPI lists. */
-static gpg_err_code_t
-ssh_key_type_lookup (const char *key_type_identifier, ssh_key_type_t *key_type)
+static void
+mpint_list_free (gcry_mpi_t *mpi_list)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- unsigned int i = 0;
+ if (mpi_list)
+ {
+ unsigned int i;
- for (i = 0; i < DIM (ssh_key_types); i++)
- if (! strcmp (key_type_identifier, ssh_key_types[i].name))
- break;
+ for (i = 0; mpi_list[i]; i++)
+ gcry_mpi_release (mpi_list[i]);
+ gcry_free (mpi_list);
+ }
+}
+
+static gpg_error_t
+ssh_receive_mpint_list (estream_t stream, int secret,
+ ssh_key_type_spec_t key_spec, gcry_mpi_t **mpi_list)
+{
+ const char *elems_secret;
+ const char *elems;
+ unsigned int elems_n;
+ gcry_mpi_t *mpis;
+ unsigned int i;
+ gpg_error_t err;
+ int elem_is_secret;
+
+ mpis = NULL;
+ err = 0;
- if (i == DIM (ssh_key_types))
- err = GPG_ERR_NOT_FOUND;
+ if (secret)
+ {
+ elems = key_spec.elems_key_secret;
+ elems_secret = key_spec.elems_secret;
+ }
else
- *key_type = ssh_key_types[i].type;
+ {
+ elems = key_spec.elems_key_public;
+ elems_secret = "";
+ }
+ elems_n = strlen (elems);
+
+ mpis = gcry_malloc (sizeof (*mpis) * (elems_n + 1));
+ if (! mpis)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
+
+ memset (mpis, 0, sizeof (*mpis) * (elems_n + 1));
+
+ for (i = 0; i < elems_n; i++)
+ {
+ elem_is_secret = strchr (elems_secret, elems[i]) ? 1 : 0;
+ err = es_read_mpi (stream, elem_is_secret, &mpis[i]);
+ if (err)
+ break;
+ }
+ if (err)
+ goto out;
+
+ *mpi_list = mpis;
+
+ out:
+
+ if (err)
+ mpint_list_free (mpis);
return err;
}
-static gpg_err_code_t
-ssh_receive_key_secret (gpg_stream_t stream, ssh_key_secret_t *key_secret)
+
+
+static gpg_error_t
+ssh_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- ssh_key_secret_t key = { 0 };
- unsigned char *key_type = NULL;
- gcry_mpi_t mpi_iqmp = NULL;
-
- err = gpg_stream_read_string (stream, 0, &key_type, NULL);
- if (err)
+ gcry_mpi_t p;
+ gcry_mpi_t q;
+ gcry_mpi_t u;
+
+ if (strcmp (elems, "nedupq"))
+ /* Modifying only necessary for secret keys. */
goto out;
- err = ssh_key_type_lookup (key_type, &key.type);
+ p = mpis[4];
+ q = mpis[5];
+ u = mpis[3];
+
+ if (gcry_mpi_cmp (p, q))
+ {
+ /* P shall be smaller then Q! Swap primes. iqmp becomes u. */
+ gcry_mpi_t tmp = NULL;
+
+ tmp = mpis[4];
+ mpis[4] = mpis[5];
+ mpis[5] = tmp;
+ }
+ else
+ /* U needs to be recomputed. */
+ gcry_mpi_invm (u, p, q);
+
+ out:
+
+ return 0;
+}
+
+static gpg_error_t
+ssh_signature_encoder_rsa (estream_t signature_blob, gcry_mpi_t *mpis)
+{
+ unsigned char *data;
+ size_t data_n;
+ gpg_error_t err;
+ gcry_mpi_t s;
+
+ s = mpis[0];
+
+ err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &data, &data_n, s);
if (err)
goto out;
- switch (key.type)
+ err = es_write_string (signature_blob, data, data_n);
+ gcry_free (data);
+
+ out:
+
+ return err;
+}
+
+#define SSH_DSA_SIGNATURE_PADDING 20
+#define SSH_DSA_SIGNATURE_ELEMS 2
+
+static gpg_error_t
+ssh_signature_encoder_dsa (estream_t signature_blob, gcry_mpi_t *mpis)
+{
+ unsigned char buffer[SSH_DSA_SIGNATURE_PADDING * SSH_DSA_SIGNATURE_ELEMS];
+ unsigned char *data;
+ size_t data_n;
+ gpg_error_t err;
+ int i;
+
+ data = NULL;
+
+ for (i = 0; i < 2; i++)
{
- case SSH_KEY_TYPE_RSA:
- {
- err = gpg_stream_read_mpint (stream, 0, &key.material.rsa.n, 0);
- if (err)
- break;
- err = gpg_stream_read_mpint (stream, 0, &key.material.rsa.e, 0);
- if (err)
- break;
- err = gpg_stream_read_mpint (stream, 1, &key.material.rsa.d, 0);
- if (err)
- break;
- err = gpg_stream_read_mpint (stream, 1, &mpi_iqmp, 0);
- if (err)
- break;
- err = gpg_stream_read_mpint (stream, 1, &key.material.rsa.p, 0);
- if (err)
- break;
- err = gpg_stream_read_mpint (stream, 1, &key.material.rsa.q, 0);
- if (err)
+ err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &data, &data_n, mpis[i]);
+ if (err)
+ break;
+
+ if (data_n > SSH_DSA_SIGNATURE_PADDING)
+ {
+ err = gpg_error (GPG_ERR_INTERNAL); /* FIXME? */
break;
+ }
+
+ memset (buffer + (i * SSH_DSA_SIGNATURE_PADDING), 0,
+ SSH_DSA_SIGNATURE_PADDING - data_n);
+ memcpy (buffer + (i * SSH_DSA_SIGNATURE_PADDING)
+ + (SSH_DSA_SIGNATURE_PADDING - data_n), data, data_n);
- if (gcry_mpi_cmp (key.material.rsa.p, key.material.rsa.q))
- {
- /* P shall be smaller then Q! Swap primes. iqmp becomes
- u. */
- gcry_mpi_t mpi_tmp = NULL;
-
- mpi_tmp = key.material.rsa.p;
- key.material.rsa.p = key.material.rsa.q;
- key.material.rsa.q = mpi_tmp;
- key.material.rsa.u = mpi_iqmp;
- mpi_iqmp = NULL;
- }
- else
- {
- /* u has to be recomputed. */
+ gcry_free (data);
+ data = NULL;
+ }
+ if (err)
+ goto out;
- key.material.rsa.u = gcry_mpi_snew (0);
- gcry_mpi_invm (key.material.rsa.u,
- key.material.rsa.p, key.material.rsa.q);
- }
-
- break;
- }
+ err = es_write_string (signature_blob, buffer, sizeof (buffer));
- case SSH_KEY_TYPE_NONE:
- default:
- err = GPG_ERR_INTERNAL; /* fixme: key type unsupported. */
- break;
+ out:
+
+ gcry_free (data);
+
+ return err;
+}
+
+#define SPEC_FLAG_USE_PKCS1V2 (1 << 0)
+
+
+/* Table holding key type specifications. */
+static ssh_key_type_spec_t ssh_key_types[] =
+ {
+ {
+ "ssh-rsa", "rsa", "nedupq", "en", "dupq", "s", "nedpqu",
+ ssh_key_modifier_rsa, ssh_signature_encoder_rsa,
+ SPEC_FLAG_USE_PKCS1V2
+ },
+ {
+ "ssh-dss", "dsa", "pqgyx", "pqgy", "x", "rs", "pqgyx",
+ NULL, ssh_signature_encoder_dsa,
+ 0
+ },
+ };
+
+
+
+/* S-Expressions. */
+
+static gpg_error_t
+ssh_sexp_construct (gcry_sexp_t *sexp,
+ ssh_key_type_spec_t key_spec, int secret,
+ gcry_mpi_t *mpis, const char *comment)
+{
+ gcry_sexp_t sexp_new;
+ char *sexp_template;
+ size_t sexp_template_n;
+ gpg_error_t err;
+ const char *elems;
+ size_t elems_n;
+ unsigned int i;
+ void **arg_list;
+
+ err = 0;
+ sexp_new = NULL;
+ arg_list = NULL;
+ if (secret)
+ elems = key_spec.elems_sexp_order;
+ else
+ elems = key_spec.elems_key_public;
+ elems_n = strlen (elems);
+
+ sexp_template_n = 33 + strlen (key_spec.identifier) + (elems_n * 6) - (! secret);
+ sexp_template = gcry_malloc (sexp_template_n);
+ if (! sexp_template)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
+
+ arg_list = gcry_malloc (sizeof (*arg_list) * (elems_n + 1));
+ if (! arg_list)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
}
+
+ sprintf (sexp_template, "(%s-key (%s ",
+ secret ? "private" : "public", key_spec.identifier);
+ for (i = 0; i < elems_n; i++)
+ {
+ sprintf (strchr (sexp_template, 0), "(%c %%m)", elems[i]);
+ arg_list[i] = &mpis[i];
+ }
+ arg_list[i] = &comment;
+ sprintf (strchr (sexp_template, 0), ") (comment %%s))");
+
+ err = gcry_sexp_build_array (&sexp_new, NULL, sexp_template, arg_list);
if (err)
goto out;
+ *sexp = sexp_new;
+
out:
- gcry_free (key_type);
- gcry_mpi_release (mpi_iqmp);
+ gcry_free (arg_list);
+ gcry_free (sexp_template);
+ if (err)
+ gcry_sexp_release (sexp_new);
+
+ return err;
+}
- if (! err)
- *key_secret = key;
+static gpg_error_t
+ssh_sexp_extract (gcry_sexp_t sexp,
+ ssh_key_type_spec_t key_spec, int *secret,
+ gcry_mpi_t **mpis, const char **comment)
+{
+ gpg_error_t err;
+ gcry_sexp_t value_list;
+ gcry_sexp_t value_pair;
+ gcry_sexp_t comment_list;
+ unsigned int i;
+ char *comment_new;
+ const char *data;
+ size_t data_n;
+ int is_secret;
+ size_t elems_n;
+ const char *elems;
+ gcry_mpi_t *mpis_new;
+ gcry_mpi_t mpi;
+
+ err = 0;
+ value_list = NULL;
+ value_pair = NULL;
+ comment_list = NULL;
+ comment_new = NULL;
+ mpis_new = NULL;
+
+ data = gcry_sexp_nth_data (sexp, 0, &data_n);
+ if (! data)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto out;
+ }
+
+ if ((data_n == 10) && (! strncmp (data, "public-key", 10)))
+ {
+ is_secret = 0;
+ elems = key_spec.elems_key_public;
+ }
+ else if (((data_n == 11) && (! strncmp (data, "private-key", 11)))
+ || ((data_n == 21) && (! strncmp (data, "protected-private-key", 21))))
+ {
+ is_secret = 1;
+ elems = key_spec.elems_key_secret;
+ }
else
{
- switch (key.type)
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto out;
+ }
+
+ elems_n = strlen (elems);
+ mpis_new = gcry_malloc (sizeof (*mpis_new) * (elems_n + 1));
+ if (! mpis_new)
+ {
+ err = gpg_error_from_errno (errno); /* FIXME, gcry_malloc+errno. */
+ goto out;
+ }
+ memset (mpis_new, 0, sizeof (*mpis_new) * (elems_n + 1));
+
+ value_list = gcry_sexp_find_token (sexp, key_spec.identifier, 0);
+ if (! value_list)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto out;
+ }
+
+ for (i = 0; i < elems_n; i++)
+ {
+ value_pair = gcry_sexp_find_token (value_list, elems + i, 1);
+ if (! value_pair)
{
- case SSH_KEY_TYPE_RSA:
- gcry_mpi_release (key.material.rsa.n);
- gcry_mpi_release (key.material.rsa.e);
- gcry_mpi_release (key.material.rsa.d);
- gcry_mpi_release (key.material.rsa.p);
- gcry_mpi_release (key.material.rsa.q);
- gcry_mpi_release (key.material.rsa.u);
+ err = gpg_error (GPG_ERR_INV_SEXP);
break;
+ }
- case SSH_KEY_TYPE_NONE:
+ mpi = gcry_sexp_nth_mpi (value_pair, 1, GCRYMPI_FMT_USG);
+ if (! mpi)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
break;
}
+ mpis_new[i] = mpi;
+ gcry_sexp_release (value_pair);
+ value_pair = NULL;
+ }
+ if (err)
+ goto out;
+
+ /* We do not require a comment sublist to be present here. */
+ data = NULL;
+ data_n = 0;
+ comment_list = gcry_sexp_find_token (sexp, "comment", 0);
+ if (comment_list)
+ data = gcry_sexp_nth_data (comment_list, 1, &data_n);
+ if (! data)
+ {
+ data = "(none)";
+ data_n = 6;
+ }
+
+ comment_new = gcry_malloc (data_n + 1);
+ if (! comment_new)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
+ strncpy (comment_new, data, data_n);
+ comment_new[data_n] = 0;
+
+ if (secret)
+ *secret = is_secret;
+ *mpis = mpis_new;
+ if (comment)
+ *comment = comment_new;
+
+ out:
+
+ gcry_sexp_release (value_list);
+ gcry_sexp_release (value_pair);
+ gcry_sexp_release (comment_list);
+
+ if (err)
+ {
+ gcry_free (comment_new);
+ mpint_list_free (mpis_new);
}
return err;
}
-static gpg_err_code_t
-ssh_send_key_public (gpg_stream_t stream, ssh_key_public_t *key_public)
+static gpg_error_t
+ssh_sexp_extract_key_type (gcry_sexp_t sexp, const char **key_type)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
-
- switch (key_public->type)
- {
- case SSH_KEY_TYPE_RSA:
- {
- err = gpg_stream_write_cstring (stream, "ssh-rsa");
- if (err)
- goto out;
- err = gpg_stream_write_mpint (stream, key_public->material.rsa.e, 0);
- if (err)
- goto out;
- err = gpg_stream_write_mpint (stream, key_public->material.rsa.n, 0);
- if (err)
- goto out;
+ gcry_sexp_t sublist;
+ char *key_type_new;
+ const char *data;
+ size_t data_n;
+ gpg_error_t err;
+
+ err = 0;
+ key_type_new = NULL;
+
+ sublist = gcry_sexp_nth (sexp, 1);
+ if (! sublist)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto out;
+ }
- break;
- }
+ data = gcry_sexp_nth_data (sublist, 0, &data_n);
+ if (! data)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto out;
+ }
- case SSH_KEY_TYPE_NONE:
- default:
- err = GPG_ERR_INTERNAL; /* FIXME */
+ key_type_new = gcry_malloc (data_n + 1);
+ if (! key_type_new)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
}
+ strncpy (key_type_new, data, data_n);
+ key_type_new[data_n] = 0;
+ *key_type = key_type_new;
+
out:
+ gcry_sexp_release (sublist);
+
return err;
}
-static gpg_err_code_t
-ssh_receive_key_public (gpg_stream_t stream, ssh_key_public_t *key_public)
+
+
+/* Key I/O. */
+
+static gpg_error_t
+ssh_key_type_lookup (const char *ssh_name, const char *name,
+ ssh_key_type_spec_t *spec)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- ssh_key_public_t key = { 0 };
- unsigned char *key_type = NULL;
+ gpg_error_t err;
+ unsigned int i;
- err = gpg_stream_read_string (stream, 0, &key_type, NULL);
+ for (i = 0; i < DIM (ssh_key_types); i++)
+ if ((ssh_name && (! strcmp (ssh_name, ssh_key_types[i].ssh_identifier)))
+ || (name && (! strcmp (name, ssh_key_types[i].identifier))))
+ break;
+
+ if (i == DIM (ssh_key_types))
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ else
+ {
+ *spec = ssh_key_types[i];
+ err = 0;
+ }
+
+ return err;
+}
+
+static gpg_error_t
+ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret, int read_comment,
+ ssh_key_type_spec_t *key_spec)
+{
+ gpg_error_t err;
+ char *key_type;
+ char *comment;
+ gcry_sexp_t key;
+ ssh_key_type_spec_t spec;
+ gcry_mpi_t *mpi_list;
+ const char *elems;
+
+ mpi_list = NULL;
+ key_type = NULL;
+ comment = "";
+ key = NULL;
+
+ err = es_read_cstring (stream, &key_type);
if (err)
goto out;
- err = ssh_key_type_lookup (key_type, &key.type);
+ err = ssh_key_type_lookup (key_type, NULL, &spec);
if (err)
goto out;
- switch (key.type)
- {
- case SSH_KEY_TYPE_RSA:
- {
- err = gpg_stream_read_mpint (stream, 0, &key.material.rsa.e, 0);
- if (err)
- break;
- err = gpg_stream_read_mpint (stream, 0, &key.material.rsa.n, 0);
- if (err)
- break;
- break;
- }
+ err = ssh_receive_mpint_list (stream, secret, spec, &mpi_list);
+ if (err)
+ goto out;
- case SSH_KEY_TYPE_NONE:
- err = GPG_ERR_INTERNAL; /* fixme: key type unsupported. */
- break;
+ if (read_comment)
+ {
+ err = es_read_cstring (stream, &comment);
+ if (err)
+ goto out;
}
+ if (secret)
+ elems = spec.elems_key_secret;
+ else
+ elems = spec.elems_key_public;
+
+ if (spec.key_modifier)
+ {
+ err = (*spec.key_modifier) (elems, mpi_list);
+ if (err)
+ goto out;
+ }
+
+ err = ssh_sexp_construct (&key, spec, secret, mpi_list, comment);
if (err)
goto out;
+ if (key_spec)
+ *key_spec = spec;
+
out:
+ gcry_free (mpi_list);
gcry_free (key_type);
+ if (read_comment)
+ gcry_free (comment);
if (! err)
- *key_public = key;
- else
- {
- switch (key.type)
- {
- case SSH_KEY_TYPE_RSA:
- gcry_mpi_release (key.material.rsa.e);
- gcry_mpi_release (key.material.rsa.n);
- break;
-
- case SSH_KEY_TYPE_NONE:
- break;
- }
- }
+ *key_new = key;
return err;
}
-static gpg_err_code_t
-ssh_extract_key_public_from_blob (unsigned char *blob, size_t blob_size,
- ssh_key_public_t *key_public)
+static gpg_error_t
+ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
+ const char *type, gcry_mpi_t *mpis)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- gpg_stream_t blob_stream = NULL;
+ unsigned char *blob_new;
+ long int blob_size_new;
+ estream_t stream;
+ gpg_error_t err;
+ unsigned int i;
+
+ blob_new = NULL;
+ stream = NULL;
+ err = 0;
+
+ stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
+ if (! stream)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
- err = gpg_stream_create (&blob_stream, NULL, NULL,
- GPG_STREAM_FLAG_READ | GPG_STREAM_FLAG_WRITE,
- gpg_stream_functions_mem);
+ err = es_write_cstring (stream, type);
if (err)
goto out;
- err = gpg_stream_write (blob_stream, blob, blob_size, NULL);
+ for (i = 0; mpis[i] && (! err); i++)
+ err = es_write_mpi (stream, mpis[i]);
if (err)
goto out;
- err = gpg_stream_seek (blob_stream, 0, SEEK_SET);
+ blob_size_new = es_ftell (stream);
+ if (blob_size_new == -1)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
+
+ err = es_fseek (stream, 0, SEEK_SET);
if (err)
goto out;
- err = ssh_receive_key_public (blob_stream, key_public);
+ blob_new = gcry_malloc (blob_size_new);
+ if (! blob_new)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
+
+ err = es_read_data (stream, blob_new, blob_size_new);
if (err)
goto out;
+ *blob = blob_new;
+ *blob_size = blob_size_new;
+
out:
- gpg_stream_destroy (blob_stream);
+ if (stream)
+ es_fclose (stream);
+ if (err)
+ gcry_free (blob_new);
return err;
}
+
-static gpg_err_code_t
-ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
- ssh_key_public_t *key_public)
+static gpg_error_t
+ssh_send_key_public (estream_t stream, gcry_sexp_t key_public)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- gpg_stream_t blob_stream = NULL;
- unsigned char *blob_new = NULL;
- size_t blob_new_size = 0;
- size_t bytes_read = 0;
-
- err = gpg_stream_create (&blob_stream, NULL, NULL,
- GPG_STREAM_FLAG_READ | GPG_STREAM_FLAG_WRITE,
- gpg_stream_functions_mem);
+ ssh_key_type_spec_t spec;
+ gcry_mpi_t *mpi_list;
+ const char *key_type;
+ const char *comment;
+ unsigned char *blob;
+ size_t blob_n;
+ gpg_error_t err;
+
+ key_type = NULL;
+ mpi_list = NULL;
+ comment = NULL;
+ blob = NULL;
+
+ err = ssh_sexp_extract_key_type (key_public, &key_type);
if (err)
goto out;
- err = ssh_send_key_public (blob_stream, key_public);
+ err = ssh_key_type_lookup (NULL, key_type, &spec);
if (err)
goto out;
- err = gpg_stream_seek (blob_stream, 0, SEEK_SET);
+ err = ssh_sexp_extract (key_public, spec, NULL, &mpi_list, &comment);
if (err)
goto out;
- err = gpg_stream_stat (blob_stream, &blob_new_size);
+ err = ssh_convert_key_to_blob (&blob, &blob_n, spec.ssh_identifier, mpi_list);
if (err)
goto out;
-
- blob_new = gcry_malloc (blob_new_size);
- if (! blob_new)
- {
- err = gpg_err_code_from_errno (errno);
- goto out;
- }
-
- err = gpg_stream_read (blob_stream, blob_new, blob_new_size, &bytes_read);
- if ((! err) && (bytes_read != blob_new_size))
- err = GPG_ERR_INTERNAL; /* FIXME? */
+
+ err = es_write_string (stream, blob, blob_n);
if (err)
goto out;
+ err = es_write_cstring (stream, comment);
+
out:
- gpg_stream_destroy (blob_stream);
-
- if (! err)
- {
- *blob = blob_new;
- *blob_size = blob_new_size;
- }
- else
- gcry_free (blob_new);
+ mpint_list_free (mpi_list);
+ gcry_free ((void *) key_type);
+ gcry_free ((void *) comment);
+ gcry_free (blob);
return err;
}
-
-
-static gpg_err_code_t
-ssh_key_grip (ssh_key_public_t *public, ssh_key_secret_t *secret,
- unsigned char *buffer)
+static gpg_error_t
+ssh_read_key_public_from_blob (unsigned char *blob, size_t blob_size,
+ gcry_sexp_t *key_public,
+ ssh_key_type_spec_t *key_spec)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- unsigned char *ret = NULL;
- gcry_sexp_t sexp = NULL;
-
- switch (public ? public->type : secret->type)
- {
- case SSH_KEY_TYPE_RSA:
- err = gcry_sexp_build (&sexp, NULL,
- "(public-key (rsa (n %m) (e %m)))",
- public
- ? public->material.rsa.n
- : secret->material.rsa.n,
- public
- ? public->material.rsa.e
- : secret->material.rsa.e);
- break;
+ estream_t blob_stream;
+ gpg_error_t err;
- case SSH_KEY_TYPE_NONE:
- abort ();
- break;
+ err = 0;
+
+ blob_stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
+ if (! blob_stream)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
}
+
+ err = es_write_data (blob_stream, blob, blob_size);
if (err)
goto out;
- ret = gcry_pk_get_keygrip (sexp, buffer);
- if (! ret)
- {
- err = GPG_ERR_INTERNAL; /* FIXME? */
- goto out;
- }
+ err = es_fseek (blob_stream, 0, SEEK_SET);
+ if (err)
+ goto out;
+
+ err = ssh_receive_key (blob_stream, key_public, 0, 0, key_spec);
out:
- gcry_sexp_release (sexp);
+ if (blob_stream)
+ es_fclose (blob_stream);
return err;
}
-static gpg_err_code_t
-ssh_key_public_from_stored_key (unsigned char *buffer, size_t buffer_n,
- ssh_key_public_t *key)
+
+
+static gpg_error_t
+key_secret_to_public (gcry_sexp_t *key_public,
+ ssh_key_type_spec_t spec, gcry_sexp_t key_secret)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- gcry_sexp_t key_stored = NULL;
- gcry_sexp_t key_data = NULL;
- gcry_sexp_t value = NULL;
- const char *identifier = NULL;
- size_t identifier_n = 0;
+ gpg_error_t err;
+ gcry_sexp_t value_pair;
+ unsigned int i;
+ gcry_mpi_t *mpis;
+ gcry_mpi_t mpi;
+ void **arglist;
+ size_t elems_n;
+ char *template;
+ size_t template_n;
+ const char *elems;
+ char *comment;
+ const char *data;
+ size_t data_n;
+
+ err = 0;
+ mpis = NULL;
+ arglist = NULL;
+ comment = NULL;
+ template = NULL;
+ value_pair = NULL;
+
+ elems = spec.elems_key_public;
+ elems_n = strlen (elems);
+
+ data = NULL;
+ value_pair = gcry_sexp_find_token (key_secret, "comment", 0);
+ if (value_pair)
+ data = gcry_sexp_nth_data (value_pair, 1, &data_n);
+ if (! data)
+ {
+ data = "";
+ data_n = 0;
+ }
+
+ comment = gcry_malloc (data_n + 1);
+ if (! comment)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
+ strncpy (comment, data, data_n);
+ comment[data_n] = 0;
+
+ gcry_sexp_release (value_pair);
+ value_pair = NULL;
- err = gcry_sexp_new (&key_stored, buffer, buffer_n, 1);
- if (err)
- goto out;
+ template_n = 29 + strlen (spec.identifier) + (elems_n * 7) + 1;
+ template = gcry_malloc (template_n);
+ if (! template)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
- identifier = gcry_sexp_nth_data (key_stored, 0, &identifier_n);
- if (! identifier)
+ mpis = gcry_malloc (sizeof (*mpis) * (elems_n + 1));
+ if (! mpis)
+ {
+ err = gpg_error_from_errno (errno); /* FIXME: errno. */
+ goto out;
+ }
+ memset (mpis, 0, sizeof (*mpis) * (elems_n + 1));
+
+ arglist = gcry_malloc (sizeof (*arglist) * (elems_n + 1));
+ if (! arglist)
{
- err = GPG_ERR_INTERNAL;
+ err = gpg_error_from_errno (errno);
goto out;
}
- if ((identifier_n == 21)
- && (! strncmp (identifier, "protected-private-key", identifier_n)))
+ for (i = 0; i < elems_n; i++)
{
- key_data = gcry_sexp_cadr (key_stored);
- if (! key_data)
+ value_pair = gcry_sexp_find_token (key_secret, elems + i, 1);
+ if (! value_pair)
{
- err = GPG_ERR_INTERNAL;
- goto out;
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ break;
}
- identifier = gcry_sexp_nth_data (key_data, 0, &identifier_n);
- if (! identifier)
+ mpi = gcry_sexp_nth_mpi (value_pair, 1, GCRYMPI_FMT_USG);
+ if (! mpi)
{
- err = GPG_ERR_INTERNAL;
- goto out;
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ break;
}
+ gcry_sexp_release (value_pair);
+ value_pair = NULL;
- if ((identifier_n == 3)
- && (! (strncmp (identifier, "rsa", identifier_n))))
- {
- gcry_mpi_t mpi_n = NULL;
- gcry_mpi_t mpi_e = NULL;
-
- value = gcry_sexp_find_token (key_data, "n", 0);
- if (! value)
- err = GPG_ERR_INTERNAL;
- else
- mpi_n = gcry_sexp_nth_mpi (value, 1, GCRYMPI_FMT_STD);
-
- if (! err)
- {
- value = gcry_sexp_find_token (key_data, "e", 0);
- if (! value)
- err = GPG_ERR_INTERNAL;
- else
- mpi_e = gcry_sexp_nth_mpi (value, 1, GCRYMPI_FMT_STD);
- }
-
- if (! err)
- {
- key->type = SSH_KEY_TYPE_RSA;
- key->material.rsa.e = mpi_e;
- key->material.rsa.n = mpi_n;
- }
- else
- {
- gcry_mpi_release (mpi_n);
- gcry_mpi_release (mpi_e);
- }
- }
+ mpis[i] = mpi;
+ arglist[i] = &mpis[i];
+ mpi = NULL;
}
+ if (err)
+ goto out;
+
+ sprintf (template, "(public-key (%s", spec.identifier);
+ for (i = 0; i < elems_n; i++)
+ sprintf (strchr (template, 0)," (%c %%m)", elems[i]);
+ sprintf (strchr (template, 0), ") (comment %%s))");
+ arglist[i] = &comment;
+ err = gcry_sexp_build_array (key_public, NULL, template, arglist);
+
out:
- gcry_sexp_release (key_stored);
- gcry_sexp_release (key_data);
- gcry_sexp_release (value);
+ gcry_sexp_release (value_pair);
+ gcry_free (template);
+ mpint_list_free (mpis);
+ gcry_free (arglist);
+ gcry_free (comment);
return err;
}
+static char *
+make_cstring (const char *data, size_t data_n)
+{
+ char *s;
+
+ s = gcry_malloc (data_n + 1);
+ if (s)
+ {
+ strncpy (s, data, data_n);
+ s[data_n] = 0;
+ }
+
+ return s;
+}
+
+
+
/* Request handler. */
-static void
-ssh_handler_request_identities (ctrl_t ctrl,
- gpg_stream_t request, gpg_stream_t response)
+static int
+ssh_handler_request_identities (ctrl_t ctrl, estream_t request, estream_t response)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- gpg_err_code_t ret = GPG_ERR_NO_ERROR;
- struct dirent *dir_entry = NULL;
- char *key_directory = NULL;
- size_t key_directory_n = 0;
- char *key_path = NULL;
- unsigned char *key_blob = NULL;
- size_t key_blob_n = 0;
- unsigned char *buffer = NULL;
- size_t buffer_n = 0;
- uint32_t key_counter = 0;
- gpg_stream_t key_blobs = NULL;
- ssh_key_public_t key = { SSH_KEY_TYPE_NONE };
- DIR *dir = NULL;
+ const char *key_type;
+ ssh_key_type_spec_t spec;
+ struct dirent *dir_entry;
+ char *key_directory;
+ size_t key_directory_n;
+ char *key_path;
+ unsigned char *buffer;
+ size_t buffer_n;
+ uint32_t key_counter;
+ estream_t key_blobs;
+ gcry_sexp_t key_secret;
+ gcry_sexp_t key_public;
+ DIR *dir;
+ gpg_error_t err;
+ int ret;
+ int bad;
+
+ if (DBG_COMMAND)
+ log_debug ("[ssh-agent] request identities\n");
/* Prepare buffer stream. */
- err = gpg_stream_create (&key_blobs, NULL, NULL,
- GPG_STREAM_FLAG_READ | GPG_STREAM_FLAG_WRITE,
- gpg_stream_functions_mem);
- if (err)
- goto out;
+ key_directory = NULL;
+ key_secret = NULL;
+ key_public = NULL;
+ key_type = NULL;
+ key_path = NULL;
+ key_counter = 0;
+ dir = NULL;
+ bad = 0;
+ err = 0;
+
+ key_blobs = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
+ if (! key_blobs)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
/* Open key directory. */
key_directory = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, NULL);
@@ -893,337 +1334,383 @@ ssh_handler_request_identities (ctrl_t ctrl,
/* Iterate over key files. */
+ /* FIXME: make sure that buffer gets deallocated properly. */
+
while (1)
{
dir_entry = readdir (dir);
if (dir_entry)
{
- if ((dir_entry->d_namlen == 44)
+ if ((strlen (dir_entry->d_name) == 44)
&& (! strncmp (dir_entry->d_name + 40, ".key", 4)))
{
strncpy (key_path + key_directory_n + 1, dir_entry->d_name, 40);
/* Read file content. */
- err = gpg_stream_read_file (key_path, &buffer, &buffer_n);
+ err = es_read_file (key_path, &buffer, &buffer_n);
+ if (err)
+ break;
+
+ err = gcry_sexp_sscan (&key_secret, NULL, buffer, buffer_n);
if (err)
- goto out;
+ break;
- /* Convert it into a public key. */
- err = ssh_key_public_from_stored_key (buffer, buffer_n, &key);
gcry_free (buffer);
buffer = NULL;
+
+ err = ssh_sexp_extract_key_type (key_secret, &key_type);
if (err)
- goto out;
+ break;
- /* Convert public key to key blob. */
- err = ssh_convert_key_to_blob (&key_blob, &key_blob_n, &key);
+ err = ssh_key_type_lookup (NULL, key_type, &spec);
if (err)
- goto out;
+ break;
+
+ gcry_free ((void *) key_type);
+ key_type = NULL;
- /* Add key blob to buffer stream. */
- err = gpg_stream_write_string (key_blobs, key_blob, key_blob_n);
- gcry_free (key_blob);
- key_blob = NULL;
+ err = key_secret_to_public (&key_public, spec, key_secret);
if (err)
- goto out;
- err = gpg_stream_write_cstring (key_blobs, "");
+ break;
+
+ gcry_sexp_release (key_secret);
+ key_secret = NULL;
+
+ err = ssh_send_key_public (key_blobs, key_public);
if (err)
- goto out;
-
+ break;
+
+ gcry_sexp_release (key_public);
+ key_public = NULL;
+
key_counter++;
}
}
else
break;
}
-
- err = gpg_stream_seek (key_blobs, 0, SEEK_SET);
if (err)
goto out;
+
+ ret = es_fseek (key_blobs, 0, SEEK_SET);
+ if (ret)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
out:
/* Send response. */
- ret = gpg_stream_write_byte (response, SSH_RESPONSE_IDENTITIES_ANSWER);
-
- if (! ret)
- ret = gpg_stream_write_uint32 (response, err ? 0 : key_counter);
+ gcry_sexp_release (key_secret);
+ gcry_sexp_release (key_public);
- if ((! ret) && (! err))
- gpg_stream_copy (response, key_blobs);
+ es_write_byte (response, SSH_RESPONSE_IDENTITIES_ANSWER);
+ if (! es_ferror (response))
+ es_write_uint32 (response, err ? 0 : key_counter);
+ if (! (err || es_ferror (response)))
+ es_copy (response, key_blobs);
- gpg_stream_destroy (key_blobs);
- closedir (dir);
+ if (key_blobs)
+ es_fclose (key_blobs);
+ if (dir)
+ closedir (dir);
+
free (key_directory);
gcry_free (key_path);
- gcry_free (key_blob);
+ gcry_free ((void *) key_type); /* FIXME? */
+
+ return bad;
}
-static gpg_err_code_t
+static gpg_error_t
data_hash (unsigned char *data, size_t data_n,
int md_algorithm, unsigned char *hash)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
-
gcry_md_hash_buffer (md_algorithm, hash, data, data_n);
- return err;
+ return 0;
}
-static gpg_err_code_t
-data_sign (CTRL ctrl, unsigned char **sig, size_t *sig_n)
+static gpg_error_t
+data_sign (CTRL ctrl, ssh_signature_encoder_t sig_encoder,
+ unsigned char **sig, size_t *sig_n)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- gcry_sexp_t signature_sexp = NULL;
- gpg_stream_t stream = NULL;
- gcry_sexp_t sublist = NULL;
- unsigned char *signature = NULL;
- size_t signature_n = 0;
- gcry_mpi_t sig_value = NULL;
- unsigned char *sig_blob = NULL;
- size_t sig_blob_n = 0;
- size_t bytes_read = 0;
- char description[] =
- "Please provide the passphrase for key `%c':";
+ char description[] = "Please provide the passphrase for key `%c':";
+ gpg_error_t err;
+ gcry_sexp_t signature_sexp;
+ estream_t stream;
+ gcry_sexp_t valuelist;
+ gcry_sexp_t sublist;
+ gcry_mpi_t sig_value;
+ unsigned char *sig_blob;
+ size_t sig_blob_n;
+ const char *identifier;
+ const char *identifier_raw;
+ size_t identifier_n;
+ ssh_key_type_spec_t spec;
+ int ret;
+ unsigned int i;
+ const char *elems;
+ size_t elems_n;
+ gcry_mpi_t *mpis;
+
+ signature_sexp = NULL;
+ identifier = NULL;
+ valuelist = NULL;
+ sublist = NULL;
+ sig_blob = NULL;
+ sig_blob_n = 0;
+ stream = NULL;
+ sig_value = NULL;
+ mpis = NULL;
err = agent_pksign_do (ctrl, description, &signature_sexp, 0);
if (err)
goto out;
- err = gpg_stream_create (&stream, NULL, NULL,
- GPG_STREAM_FLAG_READ | GPG_STREAM_FLAG_WRITE,
- gpg_stream_functions_mem);
+ valuelist = gcry_sexp_nth (signature_sexp, 1);
+ if (! valuelist)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto out;
+ }
+
+ stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
+ if (! stream)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
+
+ identifier_raw = gcry_sexp_nth_data (valuelist, 0, &identifier_n);
+ if (! identifier_raw)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto out;
+ }
+
+ identifier = make_cstring (identifier_raw, identifier_n);
+ if (! identifier)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
+
+ err = ssh_key_type_lookup (NULL, identifier, &spec);
if (err)
goto out;
- /* FIXME */
- switch (1 /* rsa */)
+ err = es_write_cstring (stream, spec.ssh_identifier);
+ if (err)
+ goto out;
+
+ elems = spec.elems_signature;
+ elems_n = strlen (elems);
+
+ mpis = gcry_malloc (sizeof (*mpis) * (elems_n + 1));
+ if (! mpis)
{
- case 1:
- sublist = gcry_sexp_find_token (signature_sexp, "s", 0);
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
+ memset (mpis, 0, sizeof (*mpis) * (elems_n + 1));
+
+ for (i = 0; i < elems_n; i++)
+ {
+ sublist = gcry_sexp_find_token (valuelist, spec.elems_signature + i, 1);
if (! sublist)
{
- err = GPG_ERR_INTERNAL;
+ err = gpg_error (GPG_ERR_INV_SEXP);
break;
}
sig_value = gcry_sexp_nth_mpi (sublist, 1, GCRYMPI_FMT_USG);
if (! sig_value)
{
- err = GPG_ERR_INTERNAL;
+ err = gpg_error (GPG_ERR_INTERNAL); /* FIXME? */
break;
}
+ gcry_sexp_release (sublist);
+ sublist = NULL;
- err = gcry_mpi_aprint (GCRYMPI_FMT_USG,
- &signature, &signature_n, sig_value);
- if (err)
- break;
-
- err = gpg_stream_write_cstring (stream, "ssh-rsa");
- if (err)
- break;
-
- err = gpg_stream_write_string (stream, signature, signature_n);
- if (err)
- break;
+ mpis[i] = sig_value;
+ sig_value = NULL;
}
if (err)
goto out;
- err = gpg_stream_seek (stream, 0, SEEK_SET);
+ err = (*sig_encoder) (stream, mpis);
if (err)
goto out;
- err = gpg_stream_stat (stream, &sig_blob_n);
- if (err)
- goto out;
+ sig_blob_n = es_ftell (stream);
+ if (sig_blob_n == -1)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
sig_blob = gcry_malloc (sig_blob_n);
if (! sig_blob)
{
- err = gpg_err_code_from_errno (errno);
+ err = gpg_error_from_errno (errno);
goto out;
}
- err = gpg_stream_read (stream, sig_blob, sig_blob_n, &bytes_read);
- if ((! err) && (sig_blob_n != bytes_read))
- err = GPG_ERR_EOF;
+ ret = es_fseek (stream, 0, SEEK_SET);
+ if (ret)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
+
+ err = es_read_data (stream, sig_blob, sig_blob_n);
if (err)
goto out;
-
+
+ *sig = (char *) sig_blob;
+ *sig_n = sig_blob_n;
+
out:
- gpg_stream_destroy (stream);
- gcry_mpi_release (sig_value);
- free (signature);
+ if (stream)
+ es_fclose (stream);
- if (! err)
- {
- *sig = sig_blob;
- *sig_n = sig_blob_n;
- }
- else
- {
- gcry_sexp_release (signature_sexp);
- gcry_sexp_release (sublist);
- gcry_free (sig_blob);
- }
+ if (err)
+ gcry_free (sig_blob);
+
+ gcry_sexp_release (valuelist);
+ gcry_sexp_release (signature_sexp);
+ gcry_sexp_release (sublist);
+ gcry_mpi_release (sig_value);
+ mpint_list_free (mpis);
+ gcry_free ((void *) identifier);
return err;
}
-static void
-ssh_handler_sign_request (ctrl_t ctrl,
- gpg_stream_t request, gpg_stream_t response)
+static int
+ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
{
- ssh_key_public_t key = { SSH_KEY_TYPE_NONE };
- unsigned char hash[MAX_DIGEST_LEN] = { 0 };
- unsigned int hash_n = 0;
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- unsigned char key_grip[20] = { 0 };
- unsigned char *key_blob = NULL;
- uint32_t key_blob_size = 0;
- unsigned char *sig = NULL;
- unsigned char *data = NULL;
- uint32_t data_size = 0;
- size_t sig_n = 0;
- uint32_t flags = 0;
+ gcry_sexp_t key;
+ ssh_key_type_spec_t spec;
+ unsigned char hash[MAX_DIGEST_LEN];
+ unsigned int hash_n;
+ unsigned char key_grip[20];
+ unsigned char *key_blob;
+ uint32_t key_blob_size;
+ unsigned char *data;
+ unsigned char *sig;
+ size_t sig_n;
+ uint32_t data_size;
+ uint32_t flags;
+ const void *p;
+ gpg_error_t err;
+ int bad;
+
+ key_blob = NULL;
+ data = NULL;
+ sig = NULL;
+ key = NULL;
+ bad = 0;
if (DBG_COMMAND)
log_debug ("[ssh-agent] sign request\n");
/* Receive key. */
- err = gpg_stream_read_string (request, 0, &key_blob, &key_blob_size);
+ err = es_read_string (request, 0, &key_blob, &key_blob_size);
if (err)
- goto out;
-
- err = ssh_extract_key_public_from_blob (key_blob, key_blob_size, &key);
+ {
+ bad = 1;
+ goto out;
+ }
+
+ err = ssh_read_key_public_from_blob (key_blob, key_blob_size, &key, &spec);
if (err)
- goto out;
+ {
+ bad = 1;
+ goto out;
+ }
/* Receive data to sign. */
-
- err = gpg_stream_read_string (request, 0, &data, &data_size);
+ err = es_read_string (request, 0, &data, &data_size);
if (err)
- goto out;
-
- /* Read flags, FIXME? */
+ {
+ bad = 1;
+ goto out;
+ }
- err = gpg_stream_read_uint32 (request, &flags);
+ /* FIXME? */
+ err = es_read_uint32 (request, &flags);
if (err)
- goto out;
+ {
+ bad = 1;
+ goto out;
+ }
/* Hash data. */
-
hash_n = gcry_md_get_algo_dlen (GCRY_MD_SHA1);
if (! hash_n)
{
- err = GPG_ERR_INTERNAL; /* FIXME? */
+ err = gpg_error (GPG_ERR_INTERNAL);
goto out;
}
-
err = data_hash (data, data_size, GCRY_MD_SHA1, hash);
if (err)
goto out;
/* Calculate key grip. */
-
- err = ssh_key_grip (&key, NULL, key_grip);
- if (err)
- goto out;
+ p = gcry_pk_get_keygrip (key, key_grip);
+ if (! p)
+ {
+ err = gpg_error (GPG_ERR_INTERNAL); /* FIXME? */
+ goto out;
+ }
- /* Fill control structure. */
+ /* Sign data. */
ctrl->digest.algo = GCRY_MD_SHA1;
memcpy (ctrl->digest.value, hash, hash_n);
ctrl->digest.valuelen = hash_n;
+ ctrl->digest.raw_value = ! (spec.flags & SPEC_FLAG_USE_PKCS1V2);
ctrl->have_keygrip = 1;
memcpy (ctrl->keygrip, key_grip, 20);
- /* Sign data. */
-
- err = data_sign (ctrl, &sig, &sig_n);
- if (err)
- goto out;
+ err = data_sign (ctrl, spec.signature_encoder, &sig, &sig_n);
out:
- /* Done. */
-
- if (! err)
+ if (! bad)
{
- err = gpg_stream_write_byte (response, SSH_RESPONSE_SIGN_RESPONSE);
- if (! err)
- err = gpg_stream_write_string (response, sig, sig_n);
+ /* Done. */
+ es_write_byte (response, SSH_RESPONSE_SIGN_RESPONSE);
+ if (! es_ferror (response))
+ {
+ if (! err)
+ es_write_string (response, sig, sig_n);
+ else
+ es_write_byte (response, SSH_RESPONSE_FAILURE);
+ }
}
- else
- gpg_stream_write_byte (response, SSH_RESPONSE_FAILURE);
-
+
+ gcry_sexp_release (key);
gcry_free (key_blob);
gcry_free (data);
gcry_free (sig);
-}
-static gpg_err_code_t
-ssh_key_to_sexp_buffer (ssh_key_secret_t *key,
- const char *comment, const char *passphrase,
- unsigned char **buffer, size_t *buffer_n)
-{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- unsigned char *buffer_new = NULL;
- unsigned int buffer_new_n = 0;
- gcry_sexp_t key_sexp = NULL;
-
- err = gcry_sexp_build (&key_sexp, NULL,
- "(private-key"
- " (rsa"
- " (n %m)"
- " (e %m)"
- " (d %m)"
- " (p %m)"
- " (q %m)"
- " (u %m))"
- " (comment %s))",
- key->material.rsa.n,
- key->material.rsa.e,
- key->material.rsa.d,
- key->material.rsa.p,
- key->material.rsa.q,
- key->material.rsa.u,
- comment ? comment : "");
- if (err)
- goto out;
-
- buffer_new_n = gcry_sexp_sprint (key_sexp, GCRYSEXP_FMT_CANON, NULL, 0);
- buffer_new = gcry_malloc (buffer_new_n);
- if (! buffer_new)
- {
- err = gpg_err_code_from_errno (errno);
- goto out;
- }
-
- gcry_sexp_sprint (key_sexp, GCRYSEXP_FMT_CANON, buffer_new, buffer_new_n);
-
- err = agent_protect (buffer_new, passphrase, buffer, buffer_n);
- if (err)
- goto out;
-
- out:
-
- gcry_sexp_release (key_sexp);
- gcry_free (buffer_new);
-
- return err;
+ return bad;
}
-static gpg_err_code_t
-get_passphrase (char *description, size_t passphrase_n, char *passphrase)
+static gpg_error_t
+get_passphrase (const char *description, size_t passphrase_n, char *passphrase)
{
- gpg_error_t err = GPG_ERR_NO_ERROR;
- struct pin_entry_info_s *pi = NULL;
+ struct pin_entry_info_s *pi;
+ gpg_error_t err;
+ err = 0;
pi = gcry_calloc_secure (1, sizeof (*pi) + passphrase_n + 1);
if (! pi)
{
@@ -1245,36 +1732,138 @@ get_passphrase (char *description, size_t passphrase_n, char *passphrase)
goto out;
memcpy (passphrase, pi->pin, passphrase_n);
+ passphrase[passphrase_n] = 0;
+
+ out:
+
+ gcry_free (pi);
+
+ return err;
+}
+
+static gpg_error_t
+ssh_key_extract_comment (gcry_sexp_t key, char **comment)
+{
+ gcry_sexp_t comment_list;
+ char *comment_new;
+ const char *data;
+ size_t data_n;
+ gpg_error_t err;
+
+ comment_list = gcry_sexp_find_token (key, "comment", 0);
+ if (! comment_list)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto out;
+ }
+
+ data = gcry_sexp_nth_data (comment_list, 1, &data_n);
+ if (! data)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto out;
+ }
+
+ comment_new = gcry_malloc (data_n + 1);
+ if (! comment_new)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
+
+ strncpy (comment_new, data, data_n);
+ comment_new[data_n] = 0;
+ *comment = comment_new;
+ err = 0;
out:
+ gcry_sexp_release (comment_list);
+
+ return err;
+}
+
+static gpg_error_t
+ssh_key_grip (gcry_sexp_t key, char *buffer)
+{
+ gpg_error_t err;
+ char *p;
+
+ /* FIXME: unsigned vs. signed. */
+
+ p = gcry_pk_get_keygrip (key, buffer);
+ if (! p)
+ err = gpg_error (GPG_ERR_INTERNAL); /* FIXME? */
+ else
+ err = 0;
+
return err;
}
-static gpg_err_code_t
-ssh_identity_register (ssh_key_secret_t *key, const char *comment, int ttl)
+static gpg_error_t
+ssh_key_to_buffer (gcry_sexp_t key, const char *passphrase,
+ unsigned char **buffer, size_t *buffer_n)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- unsigned char key_grip_raw[21] = { 0 };
- unsigned char *buffer = NULL;
- unsigned int buffer_n = 0;
- char passphrase[100] = { 0 };
- size_t description_length = 0;
- char *description = NULL;
+ unsigned char *buffer_new;
+ unsigned int buffer_new_n;
+ gpg_error_t err;
+
+ err = 0;
+ buffer_new_n = gcry_sexp_sprint (key, GCRYSEXP_FMT_CANON, NULL, 0);
+ buffer_new = gcry_malloc (buffer_new_n);
+ if (! buffer_new)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
+
+ gcry_sexp_sprint (key, GCRYSEXP_FMT_CANON, buffer_new, buffer_new_n);
+ /* FIXME: guarantee? */
+
+ err = agent_protect (buffer_new, passphrase, buffer, buffer_n);
+
+ out:
+
+ gcry_free (buffer_new);
+
+ return err;
+}
+
+static gpg_error_t
+ssh_identity_register (gcry_sexp_t key, int ttl)
+{
+ unsigned char key_grip_raw[21];
+ unsigned char *buffer;
+ unsigned int buffer_n;
+ char passphrase[100];
+ size_t description_length;
+ char *description;
char key_grip[41];
- int ret = 0;
+ char *comment;
+ gpg_error_t err;
+
+ int ret;
if (DBG_COMMAND)
log_debug ("[ssh-agent] registering identity `%s'\n", key_grip);
- err = ssh_key_grip (NULL, key, key_grip_raw);
+ description = NULL;
+ comment = NULL;
+ buffer = NULL;
+
+ err = ssh_key_grip (key, key_grip_raw);
if (err)
goto out;
+ key_grip_raw[sizeof (key_grip_raw) - 1] = 0;
ret = agent_key_available (key_grip_raw);
if (! ret)
goto out;
+ err = ssh_key_extract_comment (key, &comment);
+ if (err)
+ goto out;
+
description_length = 95 + (comment ? strlen (comment) : 0);
description = malloc (description_length);
if (! description)
@@ -1289,11 +1878,10 @@ ssh_identity_register (ssh_key_secret_t *key, const char *comment, int ttl)
comment ? comment : "");
err = get_passphrase (description, sizeof (passphrase), passphrase);
- free (description);
if (err)
goto out;
- err = ssh_key_to_sexp_buffer (key, comment, passphrase, &buffer, &buffer_n);
+ err = ssh_key_to_buffer (key, passphrase, &buffer, &buffer_n);
if (err)
goto out;
@@ -1308,21 +1896,27 @@ ssh_identity_register (ssh_key_secret_t *key, const char *comment, int ttl)
out:
free (buffer);
+ gcry_free (comment);
+ gcry_free (description);
+ /* FIXME: verify gcry_free vs free. */
return err;
}
-static gpg_err_code_t
-ssh_identity_drop (ssh_key_public_t *key)
+static gpg_error_t
+ssh_identity_drop (gcry_sexp_t key)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
unsigned char key_grip[21] = { 0 };
+ gpg_error_t err;
- err = ssh_key_grip (key, NULL, key_grip);
+ err = ssh_key_grip (key, key_grip);
if (err)
goto out;
- /* FIXME */
+ key_grip[sizeof (key_grip) - 1] = 0;
+
+ /* FIXME: What to do here - forgetting the passphrase or deleting
+ the key from key cache? */
if (DBG_COMMAND)
log_debug ("[ssh-agent] dropping identity `%s'\n", key_grip);
@@ -1332,34 +1926,38 @@ ssh_identity_drop (ssh_key_public_t *key)
return err;
}
-static void
-ssh_handler_add_identity (ctrl_t ctrl,
- gpg_stream_t request, gpg_stream_t response)
+static int
+ssh_handler_add_identity (ctrl_t ctrl, estream_t request, estream_t response)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- ssh_key_secret_t key = { 0 };
- unsigned char *comment = NULL;
- byte_t b = 0;
- int confirm = 0;
- int death = 0;
-
+ gpg_error_t err;
+ gcry_sexp_t key;
+ byte_t b;
+ int confirm;
+ int death;
+ int bad;
+
if (DBG_COMMAND)
log_debug ("[ssh-agent] add identity\n");
- err = ssh_receive_key_secret (request, &key);
- if (err)
- goto out;
+ confirm = 0;
+ death = 0;
+ key = NULL;
+ bad = 0;
- err = gpg_stream_read_string (request, 0, &comment, NULL);
+ /* FIXME? */
+ err = ssh_receive_key (request, &key, 1, 1, NULL);
if (err)
- goto out;
-
+ {
+ bad = 1;
+ goto out;
+ }
+
while (1)
{
- err = gpg_stream_read_byte (request, &b);
- if (err)
+ err = es_read_byte (request, &b);
+ if (gpg_err_code (err) == GPG_ERR_EOF)
{
- err = GPG_ERR_NO_ERROR;
+ err = 0;
break;
}
@@ -1369,7 +1967,7 @@ ssh_handler_add_identity (ctrl_t ctrl,
{
uint32_t n = 0;
- err = gpg_stream_read_uint32 (request, &n);
+ err = es_read_uint32 (request, &n);
if (! err)
death = time (NULL) + n;
break;
@@ -1382,6 +1980,7 @@ ssh_handler_add_identity (ctrl_t ctrl,
}
default:
+ /* FIXME: log/bad? */
break;
}
}
@@ -1393,147 +1992,135 @@ ssh_handler_add_identity (ctrl_t ctrl,
/* FIXME: are constraints used correctly? */
- err = ssh_identity_register (&key, comment, death);
- if (err)
- goto out;
+ err = ssh_identity_register (key, death);
out:
- free (comment);
-
- switch (key.type)
- {
- case SSH_KEY_TYPE_RSA:
- gcry_mpi_release (key.material.rsa.n);
- gcry_mpi_release (key.material.rsa.e);
- gcry_mpi_release (key.material.rsa.d);
- gcry_mpi_release (key.material.rsa.p);
- gcry_mpi_release (key.material.rsa.q);
- gcry_mpi_release (key.material.rsa.u);
- break;
+ gcry_sexp_release (key);
- case SSH_KEY_TYPE_NONE:
- break;
- }
+ if (! bad)
+ es_write_byte (response, err ? SSH_RESPONSE_FAILURE : SSH_RESPONSE_SUCCESS);
- gpg_stream_write_byte (response,
- err
- ? SSH_RESPONSE_FAILURE
- : SSH_RESPONSE_SUCCESS);
+ return bad;
}
-static void
-ssh_handler_remove_identity (ctrl_t ctrl,
- gpg_stream_t request, gpg_stream_t response)
+static int
+ssh_handler_remove_identity (ctrl_t ctrl, estream_t request, estream_t response)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- ssh_key_public_t key = { SSH_KEY_TYPE_NONE };
- unsigned char *key_blob = NULL;
- uint32_t key_blob_size = 0;
+ unsigned char *key_blob;
+ uint32_t key_blob_size;
+ gcry_sexp_t key;
+ gpg_error_t err;
+ int bad;
/* Receive key. */
if (DBG_COMMAND)
log_debug ("[ssh-agent] remove identity\n");
+
+ key_blob = NULL;
+ key = NULL;
+ bad = 0;
- err = gpg_stream_read_string (request, 0, &key_blob, NULL);
+ err = es_read_string (request, 0, &key_blob, &key_blob_size);
if (err)
- goto out;
+ {
+ bad = 1;
+ goto out;
+ }
- err = ssh_extract_key_public_from_blob (key_blob, key_blob_size, &key);
+ err = ssh_read_key_public_from_blob (key_blob, key_blob_size, &key, NULL);
if (err)
- goto out;
+ {
+ bad = 1;
+ goto out;
+ }
- err = ssh_identity_drop (&key);
- if (err)
- goto out;
+ err = ssh_identity_drop (key);
out:
gcry_free (key_blob);
-
- err = gpg_stream_write_byte (response,
- err
- ? SSH_RESPONSE_FAILURE
- : SSH_RESPONSE_SUCCESS);
+
+ if (! bad)
+ es_write_byte (response, err ? SSH_RESPONSE_FAILURE : SSH_RESPONSE_SUCCESS);
+
+ return bad;
}
-static gpg_err_code_t
+static gpg_error_t
ssh_identities_remove_all (void)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
+ gpg_error_t err;
if (DBG_COMMAND)
log_debug ("[ssh-agent] remove all identities\n");
+ err = 0;
+
/* FIXME: shall we remove _all_ cache entries or only those
registered through the ssh emulation? */
return err;
}
-static void
-ssh_handler_remove_all_identities (ctrl_t ctrl,
- gpg_stream_t request, gpg_stream_t response)
+static int
+ssh_handler_remove_all_identities (ctrl_t ctrl, estream_t request, estream_t response)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
-
+ gpg_error_t err;
+
err = ssh_identities_remove_all ();
+ es_write_byte (response, err ? SSH_RESPONSE_FAILURE : SSH_RESPONSE_SUCCESS);
- gpg_stream_write_byte (response,
- err
- ? SSH_RESPONSE_FAILURE
- : SSH_RESPONSE_SUCCESS);
+ return 0;
}
-static gpg_err_code_t
+static gpg_error_t
ssh_lock (void)
{
- gpg_err_code_t err = GPG_ERR_NOT_IMPLEMENTED;
+ gpg_error_t err;
if (DBG_COMMAND)
log_debug ("[ssh-agent] lock\n");
+ err = 0;
+
return err;
}
-static gpg_err_code_t
+static gpg_error_t
ssh_unlock (void)
{
- gpg_err_code_t err = GPG_ERR_NOT_IMPLEMENTED;
+ gpg_error_t err;
if (DBG_COMMAND)
log_debug ("[ssh-agent] unlock\n");
+ err = 0;
+
return err;
}
-static void
-ssh_handler_lock (ctrl_t ctrl,
- gpg_stream_t request, gpg_stream_t response)
+static int
+ssh_handler_lock (ctrl_t ctrl, estream_t request, estream_t response)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
-
+ gpg_error_t err;
+
err = ssh_lock ();
+ es_write_byte (response, err ? SSH_RESPONSE_FAILURE : SSH_RESPONSE_SUCCESS);
- gpg_stream_write_byte (response,
- err
- ? SSH_RESPONSE_FAILURE
- : SSH_RESPONSE_SUCCESS);
+ return 0;
}
-static void
-ssh_handler_unlock (ctrl_t ctrl,
- gpg_stream_t request, gpg_stream_t response)
+static int
+ssh_handler_unlock (ctrl_t ctrl, estream_t request, estream_t response)
{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
-
- err = ssh_unlock ();
+ gpg_error_t err;
+
+ err = ssh_unlock ();
+ es_write_byte (response, err ? SSH_RESPONSE_FAILURE : SSH_RESPONSE_SUCCESS);
- gpg_stream_write_byte (response,
- err
- ? SSH_RESPONSE_FAILURE
- : SSH_RESPONSE_SUCCESS);
+ return 0;
}
@@ -1555,14 +2142,15 @@ static ssh_request_spec_t request_specs[] =
-static gpg_err_code_t
-ssh_request_process (ctrl_t ctrl, gpg_stream_t request, gpg_stream_t response)
+static gpg_error_t
+ssh_request_process (ctrl_t ctrl, estream_t request, estream_t response)
{
- ssh_request_type_t request_type = 0;
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- unsigned int i = 0;
+ byte_t request_type;
+ gpg_error_t err;
+ unsigned int i;
+ int bad;
- err = gpg_stream_read_byte (request, &request_type);
+ err = es_read_byte (request, &request_type);
if (err)
goto out;
@@ -1574,111 +2162,69 @@ ssh_request_process (ctrl_t ctrl, gpg_stream_t request, gpg_stream_t response)
break;
if (i == DIM (request_specs))
{
- err = gpg_stream_write_byte (response, SSH_RESPONSE_FAILURE);
+ err = es_write_byte (response, SSH_RESPONSE_FAILURE);
goto out;
}
- (*request_specs[i].handler) (ctrl, request, response);
-
- out:
- return err;
-}
+ bad = (*request_specs[i].handler) (ctrl, request, response);
+ if (bad)
+ err = GPG_ERR_PROTOCOL_VIOLATION;
-static gpg_err_code_t
-gpg_stream_eof_p (gpg_stream_t stream, unsigned int *eof)
-{
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- size_t bytes_left = 0;
-
- err = gpg_stream_peek (stream, NULL, &bytes_left);
- if (! err)
- *eof = !bytes_left;
+ out:
return err;
}
-/* FIXME: Libgcrypt should provide this function. */
-static void *
-gcry_realloc_secure (void *mem, size_t size)
-{
- if (! mem)
- return gcry_malloc_secure (size);
- else
- return gcry_realloc (mem, size);
-}
-
void
start_command_handler_ssh (int sock_client)
{
- gpg_stream_spec_mem_t stream_spec_mem_secure = { NULL, 0, 1, 256,
- gcry_realloc_secure,
- gcry_free };
- gpg_stream_spec_mem_t stream_spec_mem = { NULL, 0, 1, STREAM_BLOCK_SIZE,
- gcry_realloc,
- gcry_free };
- gpg_stream_spec_fd_t stream_spec_fd = { sock_client };
- gpg_stream_buffer_spec_t buffer_spec_secure = { 256,
- gcry_realloc_secure,
- gcry_free };
- gpg_stream_buffer_spec_t buffer_spec = { 0,
- gcry_realloc,
- gcry_free };
- struct server_control_s ctrl = { NULL };
- gpg_err_code_t err = GPG_ERR_NO_ERROR;
- gpg_stream_t stream_sock = NULL;
- gpg_stream_t stream_request = NULL;
- gpg_stream_t stream_response = NULL;
- unsigned char *request = NULL;
- uint32_t request_size = 0;
- unsigned int eof = 0;
- size_t size = 0;
+ struct server_control_s ctrl;
+ gpg_error_t err;
+ estream_t stream_response;
+ estream_t stream_request;
+ estream_t stream_sock;
+ unsigned char *request;
+ uint32_t request_size;
+ size_t size;
+ int ret;
/* Setup control structure. */
if (DBG_COMMAND)
log_debug ("[ssh-agent] Starting command handler\n");
+ memset (&ctrl, 0, sizeof (ctrl));
ctrl.connection_fd = sock_client;
- err = gpg_stream_create (&stream_sock, &buffer_spec_secure,
- &stream_spec_fd,
- GPG_STREAM_FLAG_READ | GPG_STREAM_FLAG_WRITE,
- gpg_stream_functions_fd);
- if (err)
- goto out;
-
- while (1)
+ stream_response = NULL;
+ stream_request = NULL;
+ stream_sock = NULL;
+ request = NULL;
+
+ stream_sock = es_fdopen (sock_client, "r+");
+ if (! stream_sock)
{
- err = gpg_stream_eof_p (stream_sock, &eof);
- if (err || eof)
- break;
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
+ ret = es_setvbuf (stream_sock, NULL, _IONBF, 0);
+ if (ret)
+ {
+ err = gpg_error_from_errno (errno);
+ goto out;
+ }
+ while (1)
+ {
/* Create memory streams for request/response data. The entire
request will be stored in secure memory, since it might
contain secret key material. The response does not have to
be stored in secure memory, since we never give out secret
- keys. */
- gpg_stream_destroy (stream_request);
- stream_request = NULL;
- err = gpg_stream_create (&stream_request, &buffer_spec_secure,
- &stream_spec_mem_secure,
- GPG_STREAM_FLAG_READ | GPG_STREAM_FLAG_WRITE,
- gpg_stream_functions_mem);
- if (err)
- break;
- gpg_stream_destroy (stream_response);
- stream_response = NULL;
- err = gpg_stream_create (&stream_response, &buffer_spec, &stream_spec_mem,
- GPG_STREAM_FLAG_READ | GPG_STREAM_FLAG_WRITE,
- gpg_stream_functions_mem);
- if (err)
- break;
-
- /* Retrieve request length. */
- gcry_free (request);
- request = NULL;
- err = gpg_stream_read_string (stream_sock, 1, &request, &request_size);
+ keys. FIXME: wrong place. */
+
+ /* Retrieve request. */
+ err = es_read_string (stream_sock, 1, &request, &request_size);
if (err)
break;
@@ -1686,53 +2232,71 @@ start_command_handler_ssh (int sock_client)
log_debug ("[ssh-agent] Received request of length: %u\n",
request_size);
- /* Write request data to request stream. */
- err = gpg_stream_write (stream_request, request, request_size, NULL);
- if (err)
- break;
-
- err = gpg_stream_seek (stream_request, 0, SEEK_SET);
+ stream_request = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
+ if (! stream_request)
+ {
+ err = gpg_error_from_errno (errno);
+ break;
+ }
+ ret = es_setvbuf (stream_request, NULL, _IONBF, 0);
+ if (ret)
+ {
+ err = gpg_error_from_errno (errno);
+ break;
+ }
+ err = es_write_data (stream_request, request, request_size);
if (err)
break;
+ es_rewind (stream_request);
+ stream_response = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
+ if (! stream_response)
+ {
+ err = gpg_error_from_errno (errno);
+ break;
+ }
+
/* Process request. */
err = ssh_request_process (&ctrl, stream_request, stream_response);
if (err)
break;
/* Figure out size of response data. */
- err = gpg_stream_seek (stream_response, 0, SEEK_SET);
- if (err)
- break;
- err = gpg_stream_stat (stream_response, &size);
+ size = es_ftell (stream_response);
+ err = es_fseek (stream_response, 0, SEEK_SET);
if (err)
break;
/* Write response data to socket stream. */
- err = gpg_stream_write_uint32 (stream_sock, size);
+ err = es_write_uint32 (stream_sock, size);
if (err)
break;
- err = gpg_stream_copy (stream_sock, stream_response);
+ err = es_copy (stream_sock, stream_response);
if (err)
break;
- err = gpg_stream_flush (stream_sock);
+ err = es_fflush (stream_sock);
if (err)
break;
- };
- if (err)
- goto out;
+ es_fclose (stream_request);
+ stream_request = NULL;
+ es_fclose (stream_response);
+ stream_response = NULL;
+ };
out:
- gpg_stream_destroy (stream_sock);
- gpg_stream_destroy (stream_request);
- gpg_stream_destroy (stream_response);
- gcry_free (request);
+ /* FIXME: logging. */
+
+ if (stream_sock)
+ es_fclose (stream_sock);
+ if (stream_request)
+ es_fclose (stream_request);
+ if (stream_response)
+ es_fclose (stream_response);
+ gcry_free (request); /* FIXME? */
if (DBG_COMMAND)
log_debug ("[ssh-agent] Leaving ssh command handler: %s\n", gpg_strerror (err));
-
- /* fixme: make sure that stream_destroy closes client socket. */
}