diff --git a/agent/ChangeLog b/agent/ChangeLog index 517d7ccdf..39888663c 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,72 +1,16 @@ -2004-08-09 Moritz Schulte +2004-09-27 Moritz Schulte - * findkey.c (modify_description): Accept description being NULL. + **MERGED FROM MAIN BRANCH, RE-PATCHED** -2004-08-07 Moritz Schulte - - * command-ssh.c (ssh_key_to_sexp_buffer): New argument: comment; - integrate into S-Exp. - (ssh_identity_register): New argument: comment; pass to - ssh_key_to_sexp_buffer(). - (ssh_handler_add_identity): Pass comment to - ssh_identity_register(). - (ssh_identity_register): Allocate description dynamically, insert - comment; new variable: description_length; removed variable: i. - (data_sign): Do not calculate key grip for integration in - description; removed variable: i. + * command-ssh.c: New file. * findkey.c (modify_description): New function. (agent_key_from_file): New variables: comment, comment_sexp, comment_length, desc_text_modified; extract comment from S-Exp, pass modified version to unprotect(). -2004-07-30 Moritz Schulte - - * command-ssh.c: Updated Libgpg-stream (more support for secure - memory), adjusted code for new API. - -2004-07-27 Moritz Schulte - - * findkey.c (key_io_lock): New variable. - - * command-ssh.c: Use gcrypt memory allocators, use secure memory - where necessary. - -2004-07-26 Moritz Schulte - - * command-ssh.c (data_sign): Do not forget to unsigned char when - constructing human-readable key grip. - - * Makefile.am (gpg_agent_SOURCES): Removed: buffer.c, buffer.h; - updated Libgpg-stream. - -2004-07-24 Moritz Schulte - - * gpg-stream-config.h: New file (was missing before). - -2004-07-19 Moritz Schulte - - * command-ssh.c: Only log debugging message if asked to do so. - - * command-ssh.c (gpg_stream_copy): Remove function. - Update Libgpg-stream. - - * command-ssh.c: Fix handling of iqmp vs. u. - - * command-ssh.c (ssh_identity_register): passphrase must not be freed. - - * Makefile.am (gpg_agent_SOURCES): Adding: gpg-stream.c, - gpg-stream.h, buffer.c, buffer.h, command-ssh.c. - - * pksign.c (agent_pksign_do): New function, based on code ripped - out from agent_pksign. - (agent_pksign): Use agent_pksign_do. - - * query.c (start_pinentry): Accept CTRL being NULL. - - * agent.h (start_command_handler_ssh): Declare function. - (agent_pksign_do): Declare function. - (opt): New member: ssh_support. + * agent.h: Declare: start_command_handler_ssh. + (struct opt): New member: ssh_support. * gpg-agent.c: Include . New configuration option: ssh-support. @@ -81,9 +25,52 @@ (main): ... use it. (main): Generate environment entries for ssh. - * command-ssh.c: New file, implementing the ssh-agent protocol. - * gpg-stream.c, gpg-stream.h, buffer.c, buffer.h: Merged - Libgpg-stream. + * query.c (start_pinentry): Accept CTRL being NULL. + +2004-09-25 Moritz Schulte + + * agent.h: Declare: agent_pksign_do. + (struct server_control_s): New member: raw_value. + + * pksign.c (do_encode_md): New argument: raw_value; support + generation of raw (non-pkcs1) data objects; adjust callers. + (agent_pksign_do): New function, based on code ripped + out from agent_pksign. + (agent_pksign): Use agent_pksign_do. + + * command.c (start_command_handler): Set ctrl.digest.raw_value. + +2004-09-09 Werner Koch + + * gpg-agent.c (check_for_running_agent): New. + (main): The default action is now to check for an already running + agent. + (parse_rereadable_options): Set logfile only on reread. + (main): Do not print the "is development version" note. + +2004-08-20 Werner Koch + + * gpg-agent.c: New option --max-cache-ttl. Suggested by Alexander + Belopolsky. + * cache.c (housekeeping): Use it here instead of the hardwired + default of 1 hour. + + * query.c (start_pinentry): Use a timeout for the pinentry lock. + +2004-08-18 Werner Koch + + * protect-tool.c (get_passphrase): Make sure that the default + prompts passed to gpg-agent are utf-8 encoded. Add new prompt values. + (import_p12_file, import_p12_file, export_p12_file): Changed calls + to get_passphrase so that better prompts are displayed. + (get_new_passphrase): New. + +2004-07-22 Werner Koch + + * trustlist.c (read_list): Allow colons in the fingerprint. + (headerblurb): Rephrased. + + * gpg-agent.c (handle_connections): Increase the stack size ot 256k. 2004-06-20 Moritz Schulte diff --git a/agent/agent.h b/agent/agent.h index d307a6d6c..50dc4816a 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -54,6 +54,7 @@ struct { const char *scdaemon_program; int no_grab; /* don't let the pinentry grab the keyboard */ unsigned long def_cache_ttl; + unsigned long max_cache_ttl; int running_detached; /* we are running detached from the tty. */ @@ -61,7 +62,7 @@ struct { int allow_mark_trusted; int keep_tty; /* don't switch the TTY (for pinentry) on request */ int keep_display; /* don't switch the DISPLAY (for pinentry) on request */ - int ssh_support; /* enable SSH-Agent emulation. */ + int ssh_support; /* Enable ssh-agent emulation. */ } opt; @@ -95,6 +96,7 @@ struct server_control_s { int algo; unsigned char value[MAX_DIGEST_LEN]; int valuelen; + int raw_value: 1; } digest; char keygrip[20]; int have_keygrip; 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 #include #include +#include #include #include @@ -32,7 +33,7 @@ #include -#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; - - - -/* 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 -{ - uint32_t length; - byte_t type; -} ssh_packet_header_t; - -/* A "Key type". */ -typedef enum ssh_key_type - { - SSH_KEY_TYPE_NONE, - SSH_KEY_TYPE_RSA, - } ssh_key_type_t; - -/* Type used for associating Key types with their string - representation. */ -typedef struct ssh_key_type_spec -{ - ssh_key_type_t type; - const char *name; -} 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; - -typedef void (*ssh_request_handler_t) (ctrl_t ctrl, - gpg_stream_t request, - gpg_stream_t response); +typedef int (*ssh_request_handler_t) (ctrl_t ctrl, + estream_t request, estream_t response); typedef struct ssh_request_spec { - ssh_request_type_t type; + byte_t type; ssh_request_handler_t handler; } ssh_request_spec_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); -/* Table associating numeric key types with their string - representation. */ -static ssh_key_type_spec_t ssh_key_types[] = - { - { SSH_KEY_TYPE_RSA, "ssh-rsa" } - }; +typedef struct ssh_key_type_spec +{ + 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; + + static uint32_t lifetime_default; - - /* Primitive I/O functions. */ -static gpg_err_code_t -gpg_stream_read_byte (gpg_stream_t stream, byte_t *b) +static gpg_error_t +es_read_byte (estream_t stream, byte_t *b) { - gpg_err_code_t err = GPG_ERR_NO_ERROR; - unsigned char buffer[1]; - size_t bytes_read = 0; + gpg_error_t err; + int ret; - 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]; - - return err; -} - -static gpg_err_code_t -gpg_stream_write_byte (gpg_stream_t stream, byte_t b) -{ - gpg_err_code_t err = GPG_ERR_NO_ERROR; - - err = gpg_stream_write (stream, &b, sizeof (b), NULL); - - return err; -} - -static gpg_err_code_t -gpg_stream_read_uint32 (gpg_stream_t stream, uint32_t *uint32) -{ - gpg_err_code_t err = GPG_ERR_NO_ERROR; - unsigned char buffer[4] = { 0 }; - size_t bytes_read = 0; - uint32_t n = 0; - - err = gpg_stream_read (stream, buffer, sizeof (buffer), &bytes_read); - if ((! err) && (bytes_read != sizeof (buffer))) - err = GPG_ERR_EOF; - - if (! err) + ret = es_fgetc (stream); + if (ret == EOF) { - 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 (es_ferror (stream)) + err = gpg_error_from_errno (errno); + else + err = gpg_error (GPG_ERR_EOF); + } + else + { + *b = ret & 0xFF; + 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_byte (estream_t stream, byte_t b) { - gpg_err_code_t err = GPG_ERR_NO_ERROR; - unsigned char buffer[4] = { 0 }; + gpg_error_t err; + int ret; - buffer[0] = uint32 >> 24; - buffer[1] = uint32 >> 16; - buffer[2] = uint32 >> 8; - buffer[3] = uint32 >> 0; - - err = gpg_stream_write (stream, buffer, sizeof (buffer), NULL); + ret = es_fputc (b, stream); + if (ret == EOF) + 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_uint32 (estream_t stream, uint32_t *uint32) { - gpg_err_code_t err = GPG_ERR_NO_ERROR; - unsigned char *buffer = NULL; - size_t bytes_read = 0; - uint32_t length = 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_error_t +es_write_uint32 (estream_t stream, uint32_t uint32) +{ + 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_error_t +es_read_data (estream_t stream, unsigned char *buffer, size_t size) +{ + gpg_error_t err; + size_t bytes_read; + int ret; + + ret = es_read (stream, buffer, size, &bytes_read); + if (ret) + err = gpg_error_from_errno (errno); + else + { + if (bytes_read != size) + err = gpg_error (GPG_ERR_EOF); + else + err = 0; + } + + return err; +} + +static gpg_error_t +es_write_data (estream_t stream, const unsigned char *buffer, size_t size) +{ + gpg_error_t err; + int ret; + + ret = es_write (stream, buffer, size, NULL); + if (ret) + err = gpg_error_from_errno (errno); + else + err = 0; + + return err; +} + +static gpg_error_t +es_read_string (estream_t stream, unsigned int secure, + unsigned char **string, uint32_t *string_size) +{ + 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); - if (err) - goto out; - - err = gpg_stream_write (stream, string, string_n, NULL); + err = es_read_string (stream, 0, &buffer, NULL); if (err) goto out; + + *string = (char *) buffer; out: return err; } -static gpg_err_code_t -gpg_stream_write_cstring (gpg_stream_t stream, char *string) +static gpg_error_t +es_write_string (estream_t stream, + const unsigned char *string, uint32_t string_n) { - 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_uint32 (stream, string_n); + if (err) + goto out; + + err = es_write_data (stream, string, string_n); + + out: + + return err; +} + +static gpg_error_t +es_write_cstring (estream_t stream, const char *string) +{ + gpg_error_t err; + + 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,48 +365,518 @@ 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; + unsigned char *buffer_new; + struct stat statbuf; + estream_t stream; + gpg_error_t err; + int ret; - err = gpg_stream_create_file (&stream, filename, GPG_STREAM_FLAG_READ); - if (err) - goto out; - - err = gpg_stream_stat (stream, &buffer_new_n); - if (err) - goto out; - - buffer_new = gcry_malloc (buffer_new_n); - if (! buffer_new) + buffer_new = NULL; + err = 0; + + stream = es_fopen (filename, "r"); + if (! stream) { - 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? */ + ret = fstat (es_fileno (stream), &statbuf); + if (ret) + { + err = gpg_error_from_errno (errno); + goto out; + } + + buffer_new = gcry_malloc (statbuf.st_size); + if (! buffer_new) + { + err = gpg_error_from_errno (errno); + goto out; + } + + 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; + } + } + + return err; +} + + + +/* MPI lists. */ + +static void +mpint_list_free (gcry_mpi_t *mpi_list) +{ + if (mpi_list) + { + unsigned int i; + + 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 (secret) + { + elems = key_spec.elems_key_secret; + elems_secret = key_spec.elems_secret; } else - gcry_free (buffer_new); + { + 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_error_t +ssh_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis) +{ + gcry_mpi_t p; + gcry_mpi_t q; + gcry_mpi_t u; + + if (strcmp (elems, "nedupq")) + /* Modifying only necessary for secret keys. */ + goto out; + + 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; + + 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++) + { + 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); + + gcry_free (data); + data = NULL; + } + if (err) + goto out; + + err = es_write_string (signature_blob, buffer, sizeof (buffer)); + + 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 (arg_list); + gcry_free (sexp_template); + if (err) + gcry_sexp_release (sexp_new); + + return err; +} + +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 + { + 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) + { + err = gpg_error (GPG_ERR_INV_SEXP); + break; + } + + 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_error_t +ssh_sexp_extract_key_type (gcry_sexp_t sexp, const char **key_type) +{ + 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; + } + + data = gcry_sexp_nth_data (sublist, 0, &data_n); + if (! data) + { + err = gpg_error (GPG_ERR_INV_SEXP); + goto out; + } + + 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; } @@ -412,458 +885,426 @@ gpg_stream_read_file (const char *filename, /* Key I/O. */ -static gpg_err_code_t -ssh_key_type_lookup (const char *key_type_identifier, ssh_key_type_t *key_type) +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; - unsigned int i = 0; + gpg_error_t err; + unsigned int i; for (i = 0; i < DIM (ssh_key_types); i++) - if (! strcmp (key_type_identifier, ssh_key_types[i].name)) + 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_ERR_NOT_FOUND; + err = gpg_error (GPG_ERR_NOT_FOUND); else - *key_type = ssh_key_types[i].type; + { + *spec = ssh_key_types[i]; + err = 0; + } 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_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret, int read_comment, + ssh_key_type_spec_t *key_spec) { - 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); + 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) + err = ssh_receive_mpint_list (stream, secret, spec, &mpi_list); + if (err) + goto out; + + if (read_comment) { - 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) - break; - - 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. */ - - key.material.rsa.u = gcry_mpi_snew (0); - gcry_mpi_invm (key.material.rsa.u, - key.material.rsa.p, key.material.rsa.q); - } - - break; - } - - case SSH_KEY_TYPE_NONE: - default: - err = GPG_ERR_INTERNAL; /* fixme: key type unsupported. */ - break; + 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); - gcry_mpi_release (mpi_iqmp); + if (read_comment) + gcry_free (comment); if (! err) - *key_secret = key; - else - { - 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; - - case SSH_KEY_TYPE_NONE: - break; - } - } + *key_new = key; return err; } -static gpg_err_code_t -ssh_send_key_public (gpg_stream_t stream, ssh_key_public_t *key_public) -{ - 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; - - break; - } - - case SSH_KEY_TYPE_NONE: - default: - err = GPG_ERR_INTERNAL; /* FIXME */ - } - - out: - - return err; -} - -static gpg_err_code_t -ssh_receive_key_public (gpg_stream_t stream, ssh_key_public_t *key_public) -{ - gpg_err_code_t err = GPG_ERR_NO_ERROR; - ssh_key_public_t key = { 0 }; - unsigned char *key_type = NULL; - - err = gpg_stream_read_string (stream, 0, &key_type, NULL); - if (err) - goto out; - - err = ssh_key_type_lookup (key_type, &key.type); - 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; - } - - case SSH_KEY_TYPE_NONE: - err = GPG_ERR_INTERNAL; /* fixme: key type unsupported. */ - break; - } - - if (err) - goto out; - - out: - - gcry_free (key_type); - - 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; - } - } - - 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) -{ - gpg_err_code_t err = GPG_ERR_NO_ERROR; - gpg_stream_t blob_stream = NULL; - - err = gpg_stream_create (&blob_stream, NULL, NULL, - GPG_STREAM_FLAG_READ | GPG_STREAM_FLAG_WRITE, - gpg_stream_functions_mem); - if (err) - goto out; - - err = gpg_stream_write (blob_stream, blob, blob_size, NULL); - if (err) - goto out; - - err = gpg_stream_seek (blob_stream, 0, SEEK_SET); - if (err) - goto out; - - err = ssh_receive_key_public (blob_stream, key_public); - if (err) - goto out; - - out: - - gpg_stream_destroy (blob_stream); - - return err; -} - -static gpg_err_code_t +static gpg_error_t ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size, - ssh_key_public_t *key_public) + 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 = NULL; - size_t blob_new_size = 0; - size_t bytes_read = 0; + unsigned char *blob_new; + long int blob_size_new; + estream_t stream; + gpg_error_t err; + unsigned int i; - err = gpg_stream_create (&blob_stream, NULL, NULL, - GPG_STREAM_FLAG_READ | GPG_STREAM_FLAG_WRITE, - gpg_stream_functions_mem); - if (err) - goto out; + blob_new = NULL; + stream = NULL; + err = 0; - err = ssh_send_key_public (blob_stream, key_public); - if (err) - goto out; - - err = gpg_stream_seek (blob_stream, 0, SEEK_SET); - if (err) - goto out; - - err = gpg_stream_stat (blob_stream, &blob_new_size); - if (err) - goto out; - - blob_new = gcry_malloc (blob_new_size); - if (! blob_new) + stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+"); + if (! stream) { - err = gpg_err_code_from_errno (errno); + err = gpg_error_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_cstring (stream, type); if (err) goto out; + for (i = 0; mpis[i] && (! err); i++) + err = es_write_mpi (stream, mpis[i]); + if (err) + goto out; + + 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; + + 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 (! err) - { - *blob = blob_new; - *blob_size = blob_new_size; - } - else + if (stream) + es_fclose (stream); + if (err) gcry_free (blob_new); return err; } + + +static gpg_error_t +ssh_send_key_public (estream_t stream, gcry_sexp_t key_public) +{ + 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_key_type_lookup (NULL, key_type, &spec); + if (err) + goto out; + + err = ssh_sexp_extract (key_public, spec, NULL, &mpi_list, &comment); + if (err) + goto out; + + err = ssh_convert_key_to_blob (&blob, &blob_n, spec.ssh_identifier, mpi_list); + if (err) + goto out; + + err = es_write_string (stream, blob, blob_n); + if (err) + goto out; + + err = es_write_cstring (stream, comment); + + out: + + mpint_list_free (mpi_list); + gcry_free ((void *) key_type); + gcry_free ((void *) comment); + gcry_free (blob); + + return err; +} + +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) +{ + estream_t blob_stream; + gpg_error_t err; + + 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; + + 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: + + if (blob_stream) + es_fclose (blob_stream); + + 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 +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; - 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; + 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; - case SSH_KEY_TYPE_NONE: - abort (); - break; + 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; + + 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; + } + + 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_error_from_errno (errno); + goto out; + } + + for (i = 0; i < elems_n; i++) + { + value_pair = gcry_sexp_find_token (key_secret, elems + i, 1); + if (! value_pair) + { + err = gpg_error (GPG_ERR_INV_SEXP); + break; + } + mpi = gcry_sexp_nth_mpi (value_pair, 1, GCRYMPI_FMT_USG); + if (! mpi) + { + err = gpg_error (GPG_ERR_INV_SEXP); + break; + } + gcry_sexp_release (value_pair); + value_pair = NULL; + + mpis[i] = mpi; + arglist[i] = &mpis[i]; + mpi = NULL; } if (err) goto out; - ret = gcry_pk_get_keygrip (sexp, buffer); - if (! ret) - { - err = GPG_ERR_INTERNAL; /* FIXME? */ - 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 (sexp); + gcry_sexp_release (value_pair); + gcry_free (template); + mpint_list_free (mpis); + gcry_free (arglist); + gcry_free (comment); 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 char * +make_cstring (const char *data, size_t data_n) { - 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; - - err = gcry_sexp_new (&key_stored, buffer, buffer_n, 1); - if (err) - goto out; + char *s; - identifier = gcry_sexp_nth_data (key_stored, 0, &identifier_n); - if (! identifier) + s = gcry_malloc (data_n + 1); + if (s) { - err = GPG_ERR_INTERNAL; - goto out; + strncpy (s, data, data_n); + s[data_n] = 0; } - if ((identifier_n == 21) - && (! strncmp (identifier, "protected-private-key", identifier_n))) - { - key_data = gcry_sexp_cadr (key_stored); - if (! key_data) - { - err = GPG_ERR_INTERNAL; - goto out; - } - identifier = gcry_sexp_nth_data (key_data, 0, &identifier_n); - if (! identifier) - { - err = GPG_ERR_INTERNAL; - goto out; - } - - 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); - } - } - } - - out: - - gcry_sexp_release (key_stored); - gcry_sexp_release (key_data); - gcry_sexp_release (value); - - return err; + 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) - goto out; + break; + + err = gcry_sexp_sscan (&key_secret, NULL, buffer, buffer_n); + if (err) + break; - /* Convert it into a public key. */ - err = ssh_key_public_from_stored_key (buffer, buffer_n, &key); gcry_free (buffer); buffer = NULL; - if (err) - goto out; - /* Convert public key to key blob. */ - err = ssh_convert_key_to_blob (&key_blob, &key_blob_n, &key); + err = ssh_sexp_extract_key_type (key_secret, &key_type); if (err) - goto out; + break; - /* 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 = ssh_key_type_lookup (NULL, key_type, &spec); if (err) - goto out; - err = gpg_stream_write_cstring (key_blobs, ""); + break; + + gcry_free ((void *) key_type); + key_type = NULL; + + err = key_secret_to_public (&key_public, spec, key_secret); if (err) - goto out; - + break; + + gcry_sexp_release (key_secret); + key_secret = NULL; + + err = ssh_send_key_public (key_blobs, key_public); + if (err) + 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); + gcry_sexp_release (key_secret); + gcry_sexp_release (key_public); - if (! ret) - ret = gpg_stream_write_uint32 (response, err ? 0 : key_counter); + 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); - if ((! ret) && (! err)) - gpg_stream_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); - if (err) - goto out; - - /* Receive data to sign. */ - - err = gpg_stream_read_string (request, 0, &data, &data_size); - if (err) - goto out; - - /* Read flags, FIXME? */ - - err = gpg_stream_read_uint32 (request, &flags); - if (err) - goto out; - - /* Hash data. */ - - hash_n = gcry_md_get_algo_dlen (GCRY_MD_SHA1); - if (! hash_n) { - err = GPG_ERR_INTERNAL; /* FIXME? */ + bad = 1; goto out; } + err = ssh_read_key_public_from_blob (key_blob, key_blob_size, &key, &spec); + if (err) + { + bad = 1; + goto out; + } + + /* Receive data to sign. */ + err = es_read_string (request, 0, &data, &data_size); + if (err) + { + bad = 1; + goto out; + } + + /* FIXME? */ + err = es_read_uint32 (request, &flags); + if (err) + { + bad = 1; + goto out; + } + + /* Hash data. */ + hash_n = gcry_md_get_algo_dlen (GCRY_MD_SHA1); + if (! hash_n) + { + 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); + + return bad; } -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) +static gpg_error_t +get_passphrase (const char *description, size_t passphrase_n, char *passphrase) { - 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; -} - -static gpg_err_code_t -get_passphrase (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_err_code_t -ssh_identity_register (ssh_key_secret_t *key, const char *comment, int ttl) +static gpg_error_t +ssh_key_grip (gcry_sexp_t key, char *buffer) { - 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; + 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_error_t +ssh_key_to_buffer (gcry_sexp_t key, const char *passphrase, + unsigned char **buffer, size_t *buffer_n) +{ + 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); + gcry_sexp_release (key); - 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; + if (! bad) + es_write_byte (response, err ? SSH_RESPONSE_FAILURE : SSH_RESPONSE_SUCCESS); - case SSH_KEY_TYPE_NONE: - break; - } - - 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"); - - err = gpg_stream_read_string (request, 0, &key_blob, NULL); - if (err) - goto out; - err = ssh_extract_key_public_from_blob (key_blob, key_blob_size, &key); - if (err) - goto out; + key_blob = NULL; + key = NULL; + bad = 0; - err = ssh_identity_drop (&key); + err = es_read_string (request, 0, &key_blob, &key_blob_size); if (err) - goto out; + { + bad = 1; + goto out; + } + + err = ssh_read_key_public_from_blob (key_blob, key_blob_size, &key, NULL); + if (err) + { + bad = 1; + 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; + gpg_error_t err; + + err = ssh_unlock (); + es_write_byte (response, err ? SSH_RESPONSE_FAILURE : SSH_RESPONSE_SUCCESS); - err = ssh_unlock (); - - 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); + + bad = (*request_specs[i].handler) (ctrl, request, response); + if (bad) + err = GPG_ERR_PROTOCOL_VIOLATION; out: - - return err; -} - -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; 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; - + stream_response = NULL; + stream_request = NULL; + stream_sock = NULL; + request = NULL; + + stream_sock = es_fdopen (sock_client, "r+"); + if (! stream_sock) + { + 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) { - err = gpg_stream_eof_p (stream_sock, &eof); - if (err || eof) - break; - /* 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. */ } diff --git a/agent/command.c b/agent/command.c index 1d1ae9704..02bbf3429 100644 --- a/agent/command.c +++ b/agent/command.c @@ -854,6 +854,7 @@ start_command_handler (int listen_fd, int fd) ctrl.server_local->assuan_ctx = ctx; ctrl.server_local->message_fd = -1; ctrl.server_local->use_cache_for_signing = 1; + ctrl.digest.raw_value = 0; if (DBG_ASSUAN) assuan_set_log_stream (ctx, log_get_stream ()); diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 5f7b6f843..a02ddba00 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -78,15 +78,17 @@ enum cmd_and_opt_values oLCctype, oLCmessages, oScdaemonProgram, - oDefCacheTTL, oDisablePth, + oDefCacheTTL, + oMaxCacheTTL, oIgnoreCacheForSigning, oAllowMarkTrusted, oKeepTTY, oKeepDISPLAY, - aTest, - oSSHSupport }; + oSSHSupport, + +aTest }; @@ -128,17 +130,18 @@ static ARGPARSE_OPTS opts[] = { { oDefCacheTTL, "default-cache-ttl", 4, N_("|N|expire cached PINs after N seconds")}, + { oMaxCacheTTL, "max-cache-ttl", 4, "@" }, { oIgnoreCacheForSigning, "ignore-cache-for-signing", 0, N_("do not use the PIN cache when signing")}, { oAllowMarkTrusted, "allow-mark-trusted", 0, N_("allow clients to mark keys as \"trusted\"")}, { oSSHSupport, "ssh-support", 0, "Enable SSH-Agent emulation" }, - {0} }; -#define DEFAULT_CACHE_TTL (10*60) /* 10 minutes */ +#define DEFAULT_CACHE_TTL (10*60) /* 10 minutes */ +#define MAX_CACHE_TTL (120*60) /* 2 hours */ static volatile int caught_fatal_sig = 0; @@ -181,6 +184,7 @@ static void handle_connections (int listen_fd, int listen_fd_ssh); GCRY_THREAD_OPTION_PTH_IMPL; #endif /*USE_GNU_PTH*/ +static void check_for_running_agent (void); @@ -286,6 +290,7 @@ set_debug (void) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1); gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); } + static void cleanup_do (char *name) @@ -353,6 +358,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) opt.pinentry_program = NULL; opt.scdaemon_program = NULL; opt.def_cache_ttl = DEFAULT_CACHE_TTL; + opt.max_cache_ttl = MAX_CACHE_TTL; opt.ignore_cache_for_signing = 0; opt.allow_mark_trusted = 0; return 1; @@ -368,8 +374,9 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) case oDebugLevel: debug_level = pargs->r.ret_str; break; case oLogFile: - if (!current_logfile || !pargs->r.ret_str - || strcmp (current_logfile, pargs->r.ret_str)) + if (reread + && (!current_logfile || !pargs->r.ret_str + || strcmp (current_logfile, pargs->r.ret_str))) { log_set_file (pargs->r.ret_str); xfree (current_logfile); @@ -383,6 +390,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) case oScdaemonProgram: opt.scdaemon_program = pargs->r.ret_str; break; case oDefCacheTTL: opt.def_cache_ttl = pargs->r.ret_ulong; break; + case oMaxCacheTTL: opt.max_cache_ttl = pargs->r.ret_ulong; break; case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break; @@ -394,6 +402,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) return 1; /* handled */ } + static void server_socket_create (int *sock, const char *identifier, char *name, int name_size) @@ -681,7 +690,9 @@ main (int argc, char **argv ) fprintf (stderr, "%s\n", strusage(15) ); } #ifdef IS_DEVELOPMENT_VERSION - log_info ("NOTE: this is a development version!\n"); + /* We don't want to print it here because gpg-agent is useful of its + own and quite matured. */ + /*log_info ("NOTE: this is a development version!\n");*/ #endif set_debug (); @@ -751,9 +762,15 @@ main (int argc, char **argv ) agent_exit (0); } + /* If this has been called without any options, we merely check + whether an agent is already running. We do this here so that we + don't clobber a logfile but print it directly to stderr. */ if (!pipe_server && !is_daemon) - log_info (_("please use the option `--daemon'" - " to run the program in the background\n")); + { + log_set_prefix (NULL, JNLIB_LOG_WITH_PREFIX); + check_for_running_agent (); + agent_exit (0); + } #ifdef ENABLE_NLS /* gpg-agent usually does not output any messages because it runs in @@ -787,12 +804,11 @@ main (int argc, char **argv ) start_command_handler (-1, -1); } else if (!is_daemon) - ; + ; /* NOTREACHED */ else - { /* regular server mode */ + { /* Regular server mode */ int fd = -1, fd_ssh = -1; pid_t pid; - //char *p; /* Remove the DISPLAY variable so that a pinentry does not default to a specific display. There is still a default @@ -806,7 +822,6 @@ main (int argc, char **argv ) server_socket_create (&fd_ssh, "-ssh", socket_name_ssh, sizeof (socket_name_ssh)); - if (opt.verbose) { log_info ("listening on socket `%s'\n", socket_name); @@ -814,6 +829,7 @@ main (int argc, char **argv ) log_info ("listening on socket `%s'\n", socket_name_ssh); } + fflush (NULL); pid = fork (); if (pid == (pid_t)-1) @@ -826,8 +842,6 @@ main (int argc, char **argv ) char *infostr, *infostr_ssh_sock, *infostr_ssh_pid; close (fd); - if (opt.ssh_support) - close (fd_ssh); /* create the info string: :: */ if (asprintf (&infostr, "GPG_AGENT_INFO=%s:%lu:1", @@ -858,7 +872,7 @@ main (int argc, char **argv ) *socket_name = 0; /* don't let cleanup() remove the socket - the child should do this from now on */ *socket_name_ssh = 0; - + if (argc) { /* run the program given on the commandline */ if (putenv (infostr)) @@ -994,7 +1008,7 @@ main (int argc, char **argv ) } close (fd); } - + return 0; } @@ -1235,6 +1249,7 @@ start_connection_thread (void *arg) return NULL; } + static void * start_connection_thread_ssh (void *arg) { @@ -1268,7 +1283,12 @@ handle_connections (int listen_fd, int listen_fd_ssh) int ret = -1; int fd; - sigemptyset (&sigs); + tattr = pth_attr_new(); + pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0); + pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 64); + pth_attr_set (tattr, PTH_ATTR_NAME, "gpg-agent"); + + sigemptyset (&sigs ); sigaddset (&sigs, SIGHUP); sigaddset (&sigs, SIGUSR1); sigaddset (&sigs, SIGUSR2); @@ -1276,11 +1296,6 @@ handle_connections (int listen_fd, int listen_fd_ssh) sigaddset (&sigs, SIGTERM); ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo); - tattr = pth_attr_new(); - pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0); - pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 32*1024); - pth_attr_set (tattr, PTH_ATTR_NAME, "gpg-agent"); - FD_ZERO (&fdset); FD_SET (listen_fd, &fdset); if (listen_fd_ssh != -1) @@ -1370,4 +1385,58 @@ handle_connections (int listen_fd, int listen_fd_ssh) cleanup (); log_info ("%s %s stopped\n", strusage(11), strusage(13)); } + #endif /*USE_GNU_PTH*/ + + +/* Figure out whether an agent is available and running. Prints an + error if not. */ +static void +check_for_running_agent () +{ + int rc; + char *infostr, *p; + assuan_context_t ctx; + int prot, pid; + + infostr = getenv ("GPG_AGENT_INFO"); + if (!infostr || !*infostr) + { + log_error (_("no gpg-agent running in this session\n")); + return; + } + + infostr = xstrdup (infostr); + if ( !(p = strchr (infostr, ':')) || p == infostr) + { + log_error (_("malformed GPG_AGENT_INFO environment variable\n")); + xfree (infostr); + return; + } + + *p++ = 0; + pid = atoi (p); + while (*p && *p != ':') + p++; + prot = *p? atoi (p+1) : 0; + if (prot != 1) + { + log_error (_("gpg-agent protocol version %d is not supported\n"), + prot); + xfree (infostr); + return; + } + + rc = assuan_socket_connect (&ctx, infostr, pid); + xfree (infostr); + if (rc) + { + log_error ("can't connect to the agent: %s\n", assuan_strerror (rc)); + return; + } + + if (!opt.quiet) + log_info ("gpg-agent running and available\n"); + + assuan_disconnect (ctx); +} diff --git a/agent/pksign.c b/agent/pksign.c index f2c61b43b..11e964837 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -32,24 +32,45 @@ static int -do_encode_md (const byte * md, size_t mdlen, int algo, gcry_sexp_t * r_hash) +do_encode_md (const byte * md, size_t mdlen, int algo, gcry_sexp_t * r_hash, + int raw_value) { gcry_sexp_t hash; - const char *s; - char tmp[16+1]; - int i, rc; + int rc; - s = gcry_md_algo_name (algo); - if (s && strlen (s) < 16) + if (! raw_value) { - for (i=0; i < strlen (s); i++) - tmp[i] = tolower (s[i]); - tmp[i] = '\0'; + const char *s; + char tmp[16+1]; + int i; + + s = gcry_md_algo_name (algo); + if (s && strlen (s) < 16) + { + for (i=0; i < strlen (s); i++) + tmp[i] = tolower (s[i]); + tmp[i] = '\0'; + } + + rc = gcry_sexp_build (&hash, NULL, + "(data (flags pkcs1) (hash %s %b))", + tmp, mdlen, md); } - rc = gcry_sexp_build (&hash, NULL, - "(data (flags pkcs1) (hash %s %b))", - tmp, - mdlen, md); + else + { + gcry_mpi_t mpi; + + rc = gcry_mpi_scan (&mpi, GCRYMPI_FMT_USG, md, mdlen, NULL); + if (! rc) + { + rc = gcry_sexp_build (&hash, NULL, + "(data (flags raw) (value %m))", + mpi); + gcry_mpi_release (mpi); + } + + } + *r_hash = hash; return rc; } @@ -115,7 +136,8 @@ agent_pksign_do (CTRL ctrl, const char *desc_text, rc = do_encode_md (ctrl->digest.value, ctrl->digest.valuelen, ctrl->digest.algo, - &s_hash); + &s_hash, + ctrl->digest.raw_value); if (rc) goto leave; @@ -143,8 +165,7 @@ agent_pksign_do (CTRL ctrl, const char *desc_text, leave: - if (! rc) - *signature_sexp = s_sig; + *signature_sexp = s_sig; gcry_sexp_release (s_skey); xfree (shadow_info); diff --git a/agent/query.c b/agent/query.c index 4fbe702c6..cdf6ff259 100644 --- a/agent/query.c +++ b/agent/query.c @@ -41,6 +41,14 @@ #define MAX_OPEN_FDS 20 #endif + +/* Because access to the pinentry must be serialized (it is and shall + be a global mutual dialog) we should better timeout further + requests after some time. 2 minutes seem to be a reasonable + time. */ +#define LOCK_TIMEOUT (1*60) + + static ASSUAN_CONTEXT entry_ctx = NULL; #ifdef USE_GNU_PTH static pth_mutex_t entry_lock = PTH_MUTEX_INIT; @@ -104,11 +112,23 @@ start_pinentry (CTRL ctrl) int i; #ifdef USE_GNU_PTH - if (!pth_mutex_acquire (&entry_lock, 0, NULL)) + { + pth_event_t evt; + + evt = pth_event (PTH_EVENT_TIME, pth_timeout (LOCK_TIMEOUT, 0)); + if (!pth_mutex_acquire (&entry_lock, 0, evt)) { - log_error ("failed to acquire the entry lock\n"); - return gpg_error (GPG_ERR_INTERNAL); + if (pth_event_occurred (evt)) + rc = gpg_error (GPG_ERR_TIMEOUT); + else + rc = gpg_error (GPG_ERR_INTERNAL); + pth_event_free (evt, PTH_FREE_THIS); + log_error (_("failed to acquire the pinentry lock: %s\n"), + gpg_strerror (rc)); + return rc; } + pth_event_free (evt, PTH_FREE_THIS); + } #endif if (entry_ctx) @@ -132,7 +152,7 @@ start_pinentry (CTRL ctrl) pgmname++; argv[0] = pgmname; - if ((ctrl && ctrl->display) && !opt.keep_display) + if (ctrl && ctrl->display && !opt.keep_display) { argv[1] = "--display"; argv[2] = ctrl->display;