[git] GnuPG - branch, STABLE-BRANCH-2-0, updated. gnupg-2.0.20-11-ga139884

by Werner Koch cvs at cvs.gnupg.org
Mon Jul 1 21:17:46 CEST 2013


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "The GNU Privacy Guard".

The branch, STABLE-BRANCH-2-0 has been updated
       via  a1398844adb84392f4a1da6db2e50b914a1aada1 (commit)
       via  e0659690186f833e0e91b0a1cfef655c2d10ed87 (commit)
       via  b4cb20cfc3fe2296a163355f386544096e48c147 (commit)
       via  4b4df62eaf7f46f83540536bfa373e78be2a6d7d (commit)
       via  cf7f9303272db65465ff45348cf18f7298e41e30 (commit)
       via  901162579119585ebd3df9001b0370e6d32934ab (commit)
       via  336112e519079f43278a8ca8c2937417bc667d8f (commit)
       via  fc7d033d8e62f6a289fdf7dba26af076accb5fd2 (commit)
       via  7ce72c97bfe1ab2f58248a6afe629aafa20d058b (commit)
      from  8ddf604659b93754ffa6dea295678a8adc293f90 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit a1398844adb84392f4a1da6db2e50b914a1aada1
Author: Werner Koch <wk at gnupg.org>
Date:   Mon Jul 1 20:49:50 2013 +0200

    Update NEWS.
    
    --

diff --git a/NEWS b/NEWS
index bf6e11c..4295ee9 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,9 @@
 Noteworthy changes in version 2.0.21 (unreleased)
 -------------------------------------------------
 
+ * The included ssh agent does now support ECDSA keys.
+
+
 Noteworthy changes in version 2.0.20 (2013-05-10)
 -------------------------------------------------
 

commit e0659690186f833e0e91b0a1cfef655c2d10ed87
Author: Werner Koch <wk at gnupg.org>
Date:   Mon Jul 1 20:34:55 2013 +0200

    ssh: Mark unused arg.
    
    * agent/command-ssh.c (ssh_signature_encoder_ecdsa): Cast spec to
    void.

diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index 826d175..5da1a71 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -1248,6 +1248,8 @@ ssh_signature_encoder_ecdsa (ssh_key_type_spec_t *spec,
   gpg_error_t err;
   int i;
 
+  (void)spec;
+
   innerlen = 0;
   for (i = 0; i < DIM(data); i++)
     {

commit b4cb20cfc3fe2296a163355f386544096e48c147
Author: Werner Koch <wk at gnupg.org>
Date:   Wed Dec 12 18:47:21 2012 +0100

    ssh: Support ECDSA keys.
    
    * agent/command-ssh.c (SPEC_FLAG_IS_ECDSA): New.
    (struct ssh_key_type_spec): Add fields CURVE_NAME and HASH_ALGO.
    (ssh_key_types): Add types ecdsa-sha2-nistp{256,384,521}.
    (ssh_signature_encoder_t): Add arg spec and adjust all callers.
    (ssh_signature_encoder_ecdsa): New.
    (sexp_key_construct, sexp_key_extract, ssh_receive_key)
    (ssh_convert_key_to_blob): Support ecdsa.
    (ssh_identifier_from_curve_name): New.
    (ssh_send_key_public): Retrieve and pass the curve_name.
    (key_secret_to_public): Ditto.
    (data_sign): Add arg SPEC and change callers to pass it.
    (ssh_handler_sign_request): Get the hash algo from SPEC.
    * common/ssh-utils.c (get_fingerprint): Support ecdsa.
    
    * agent/protect.c (protect_info): Add flag ECC_HACK.
    (agent_protect): Allow the use of the "curve" parameter.
    * agent/t-protect.c (test_agent_protect): Add a test case for ecdsa.
    
    * agent/command-ssh.c (ssh_key_grip): Print a better error code.
    --
    
    The 3 standard curves are now supported in gpg-agent's ssh-agent
    protocol implementation.  I tested this with all 3 curves and keys
    generated by OpenSSH 5.9p1.
    
    Using existing non-ssh generated keys will likely fail for now. To fix
    this, the code should first undergo some more cleanup; then the fixes
    are pretty straightforward.  And yes, the data structures are way too
    complicated.
    
    (cherry picked from commit 649b31c663b8674bc874b4ef283d714a13dc8cfe)
    
    Solved conflicts:
    
    	agent/protect.c
    	agent/t-protect.c
            common/ssh-utils.c (different variabale name)

diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index 6b73a5d..826d175 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -17,7 +17,18 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-/* Only v2 of the ssh-agent protocol is implemented.  */
+/* Only v2 of the ssh-agent protocol is implemented.  Relevant RFCs
+   are:
+
+   RFC-4250 - Protocol Assigned Numbers
+   RFC-4251 - Protocol Architecture
+   RFC-4252 - Authentication Protocol
+   RFC-4253 - Transport Layer Protocol
+   RFC-5656 - ECC support
+
+   The protocol for the agent is defined in OpenSSH's PROTOCL.agent
+   file.
+  */
 
 #include <config.h>
 
@@ -62,6 +73,7 @@
 #define SSH_DSA_SIGNATURE_PADDING 20
 #define SSH_DSA_SIGNATURE_ELEMS    2
 #define SPEC_FLAG_USE_PKCS1V2 (1 << 0)
+#define SPEC_FLAG_IS_ECDSA    (1 << 1)
 
 /* The name of the control file.  */
 #define SSH_CONTROL_FILE_NAME "sshcontrol"
@@ -100,6 +112,10 @@ typedef gpg_error_t (*ssh_request_handler_t) (ctrl_t ctrl,
 					      estream_t request,
 					      estream_t response);
 
+
+struct ssh_key_type_spec;
+typedef struct ssh_key_type_spec ssh_key_type_spec_t;
+
 /* Type, which is used for associating request handlers with the
    appropriate request IDs.  */
 typedef struct ssh_request_spec
@@ -120,12 +136,13 @@ typedef gpg_error_t (*ssh_key_modifier_t) (const char *elems,
 /* The encoding of a generated signature is dependent on the
    algorithm; therefore algorithm specific signature encoding
    functions are necessary.  */
-typedef gpg_error_t (*ssh_signature_encoder_t) (estream_t signature_blob,
+typedef gpg_error_t (*ssh_signature_encoder_t) (ssh_key_type_spec_t *spec,
+                                                estream_t signature_blob,
 						gcry_mpi_t *mpis);
 
 /* Type, which is used for boundling all the algorithm specific
    information together in a single object.  */
-typedef struct ssh_key_type_spec
+struct ssh_key_type_spec
 {
   /* Algorithm identifier as used by OpenSSH.  */
   const char *ssh_identifier;
@@ -158,9 +175,16 @@ typedef struct ssh_key_type_spec
      algorithm.  */
   ssh_signature_encoder_t signature_encoder;
 
+  /* The name of the ECC curve or NULL.  */
+  const char *curve_name;
+
+  /* The hash algorithm to be used with this key.  0 for using the
+     default.  */
+  int hash_algo;
+
   /* Misc flags.  */
   unsigned int flags;
-} ssh_key_type_spec_t;
+};
 
 
 /* An object used to access the sshcontrol file.  */
@@ -205,10 +229,15 @@ static gpg_error_t ssh_handler_unlock (ctrl_t ctrl,
 				       estream_t response);
 
 static gpg_error_t ssh_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis);
-static gpg_error_t ssh_signature_encoder_rsa (estream_t signature_blob,
+static gpg_error_t ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec,
+                                              estream_t signature_blob,
                                               gcry_mpi_t *mpis);
-static gpg_error_t ssh_signature_encoder_dsa (estream_t signature_blob,
+static gpg_error_t ssh_signature_encoder_dsa (ssh_key_type_spec_t *spec,
+                                              estream_t signature_blob,
                                               gcry_mpi_t *mpis);
+static gpg_error_t ssh_signature_encoder_ecdsa (ssh_key_type_spec_t *spec,
+                                                estream_t signature_blob,
+                                                gcry_mpi_t *mpis);
 
 
 
@@ -241,13 +270,29 @@ static ssh_key_type_spec_t ssh_key_types[] =
     {
       "ssh-rsa", "rsa", "nedupq", "en",   "s",  "nedpqu",
       ssh_key_modifier_rsa, ssh_signature_encoder_rsa,
-      SPEC_FLAG_USE_PKCS1V2
+      NULL, 0, SPEC_FLAG_USE_PKCS1V2
     },
     {
       "ssh-dss", "dsa", "pqgyx",  "pqgy", "rs", "pqgyx",
       NULL,                 ssh_signature_encoder_dsa,
-      0
+      NULL, 0, 0
+    },
+    {
+      "ecdsa-sha2-nistp256", "ecdsa", "qd",  "q", "rs", "qd",
+      NULL,                 ssh_signature_encoder_ecdsa,
+      "nistp256", GCRY_MD_SHA256, SPEC_FLAG_IS_ECDSA
     },
+    {
+      "ecdsa-sha2-nistp384", "ecdsa", "qd",  "q", "rs", "qd",
+      NULL,                 ssh_signature_encoder_ecdsa,
+      "nistp384", GCRY_MD_SHA384, SPEC_FLAG_IS_ECDSA
+    },
+    {
+      "ecdsa-sha2-nistp521", "ecdsa", "qd",  "q", "rs", "qd",
+      NULL,                 ssh_signature_encoder_ecdsa,
+      "nistp521", GCRY_MD_SHA512, SPEC_FLAG_IS_ECDSA
+    }
+
   };
 
 

@@ -342,6 +387,7 @@ stream_write_byte (estream_t stream, unsigned char b)
   return err;
 }
 
+
 /* Read a uint32 from STREAM, store it in UINT32.  */
 static gpg_error_t
 stream_read_uint32 (estream_t stream, u32 *uint32)
@@ -432,8 +478,9 @@ stream_write_data (estream_t stream, const unsigned char *buffer, size_t size)
 }
 
 /* Read a binary string from STREAM into STRING, store size of string
-   in STRING_SIZE; depending on SECURE use secure memory for
-   string.  */
+   in STRING_SIZE.  Append a hidden nul so that the result may
+   directly be used as a C string.  Depending on SECURE use secure
+   memory for STRING.  */
 static gpg_error_t
 stream_read_string (estream_t stream, unsigned int secure,
 		    unsigned char **string, u32 *string_size)
@@ -1115,13 +1162,16 @@ ssh_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis)
 
 /* Signature encoder function for RSA.  */
 static gpg_error_t
-ssh_signature_encoder_rsa (estream_t signature_blob, gcry_mpi_t *mpis)
+ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec,
+                           estream_t signature_blob, gcry_mpi_t *mpis)
 {
   unsigned char *data;
   size_t data_n;
   gpg_error_t err;
   gcry_mpi_t s;
 
+  (void)spec;
+
   s = mpis[0];
 
   err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &data, &data_n, s);
@@ -1139,7 +1189,8 @@ ssh_signature_encoder_rsa (estream_t signature_blob, gcry_mpi_t *mpis)
 
 /* Signature encoder function for DSA.  */
 static gpg_error_t
-ssh_signature_encoder_dsa (estream_t signature_blob, gcry_mpi_t *mpis)
+ssh_signature_encoder_dsa (ssh_key_type_spec_t *spec,
+                           estream_t signature_blob, gcry_mpi_t *mpis)
 {
   unsigned char buffer[SSH_DSA_SIGNATURE_PADDING * SSH_DSA_SIGNATURE_ELEMS];
   unsigned char *data;
@@ -1147,8 +1198,12 @@ ssh_signature_encoder_dsa (estream_t signature_blob, gcry_mpi_t *mpis)
   gpg_error_t err;
   int i;
 
+  (void)spec;
+
   data = NULL;
 
+  /* FIXME: Why this complicated code?  Why collecting boths mpis in a
+     buffer instead of writing them out one after the other?  */
   for (i = 0; i < 2; i++)
     {
       err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &data, &data_n, mpis[i]);
@@ -1181,23 +1236,63 @@ ssh_signature_encoder_dsa (estream_t signature_blob, gcry_mpi_t *mpis)
   return err;
 }
 
+
+/* Signature encoder function for ECDSA.  */
+static gpg_error_t
+ssh_signature_encoder_ecdsa (ssh_key_type_spec_t *spec,
+                             estream_t stream, gcry_mpi_t *mpis)
+{
+  unsigned char *data[2] = {NULL, NULL};
+  size_t data_n[2];
+  size_t innerlen;
+  gpg_error_t err;
+  int i;
+
+  innerlen = 0;
+  for (i = 0; i < DIM(data); i++)
+    {
+      err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &data[i], &data_n[i], mpis[i]);
+      if (err)
+	goto out;
+      innerlen += 4 + data_n[i];
+    }
+
+  err = stream_write_uint32 (stream, innerlen);
+  if (err)
+    goto out;
+
+  for (i = 0; i < DIM(data); i++)
+    {
+      err = stream_write_string (stream, data[i], data_n[i]);
+      if (err)
+        goto out;
+    }
+
+ out:
+  for (i = 0; i < DIM(data); i++)
+    xfree (data[i]);
+  return err;
+}
+
+
 /*
    S-Expressions.
  */
 
 
 /* This function constructs a new S-Expression for the key identified
-   by the KEY_SPEC, SECRET, MPIS and COMMENT, which is to be stored in
-   *SEXP.  Returns usual error code.  */
+   by the KEY_SPEC, SECRET, CURVE_NAME, MPIS, and COMMENT, which is to
+   be stored at R_SEXP.  Returns an error code.  */
 static gpg_error_t
 sexp_key_construct (gcry_sexp_t *r_sexp,
 		    ssh_key_type_spec_t key_spec, int secret,
-		    gcry_mpi_t *mpis, const char *comment)
+		    const char *curve_name, gcry_mpi_t *mpis,
+                    const char *comment)
 {
   const char *key_identifier[] = { "public-key", "private-key" };
   gpg_error_t err;
   gcry_sexp_t sexp_new = NULL;
-  char *formatbuf = NULL;
+  void *formatbuf = NULL;
   void **arg_list = NULL;
   int arg_idx;
   estream_t format;
@@ -1220,7 +1315,7 @@ sexp_key_construct (gcry_sexp_t *r_sexp,
 
   /* Key identifier, algorithm identifier, mpis, comment, and a NULL
      as a safeguard. */
-  arg_list = xtrymalloc (sizeof (*arg_list) * (2 + elems_n + 1 + 1));
+  arg_list = xtrymalloc (sizeof (*arg_list) * (2 + 1 + elems_n + 1 + 1));
   if (!arg_list)
     {
       err = gpg_error_from_syserror ();
@@ -1231,6 +1326,11 @@ sexp_key_construct (gcry_sexp_t *r_sexp,
   es_fputs ("(%s(%s", format);
   arg_list[arg_idx++] = &key_identifier[secret];
   arg_list[arg_idx++] = &key_spec.identifier;
+  if (curve_name)
+    {
+      es_fputs ("(curve%s)", format);
+      arg_list[arg_idx++] = &curve_name;
+    }
 
   for (i = 0; i < elems_n; i++)
     {
@@ -1262,7 +1362,6 @@ sexp_key_construct (gcry_sexp_t *r_sexp,
     }
   format = NULL;
 
-  log_debug ("sexp formatbuf='%s' nargs=%d\n", formatbuf, arg_idx);
   err = gcry_sexp_build_array (&sexp_new, NULL, formatbuf, arg_list);
   if (err)
     goto out;
@@ -1282,34 +1381,28 @@ sexp_key_construct (gcry_sexp_t *r_sexp,
 /* This functions breaks up the key contained in the S-Expression SEXP
    according to KEY_SPEC.  The MPIs are bundled in a newly create
    list, which is to be stored in MPIS; a newly allocated string
-   holding the comment will be stored in COMMENT; SECRET will be
-   filled with a boolean flag specifying what kind of key it is.
-   Returns usual error code.  */
+   holding the curve name may be stored at RCURVE, and a comment will
+   be stored at COMMENT; SECRET will be filled with a boolean flag
+   specifying what kind of key it is.  Returns an error code.  */
 static gpg_error_t
 sexp_key_extract (gcry_sexp_t sexp,
 		  ssh_key_type_spec_t key_spec, int *secret,
-		  gcry_mpi_t **mpis, char **comment)
+		  gcry_mpi_t **mpis, char **r_curve, char **comment)
 {
-  gpg_error_t err;
-  gcry_sexp_t value_list;
-  gcry_sexp_t value_pair;
-  gcry_sexp_t comment_list;
+  gpg_error_t err = 0;
+  gcry_sexp_t value_list = NULL;
+  gcry_sexp_t value_pair = NULL;
+  gcry_sexp_t comment_list = NULL;
   unsigned int i;
-  char *comment_new;
+  char *comment_new = NULL;
   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 *mpis_new = NULL;
   gcry_mpi_t mpi;
-
-  err = 0;
-  value_list = NULL;
-  value_pair = NULL;
-  comment_list = NULL;
-  comment_new = NULL;
-  mpis_new = NULL;
+  char *curve_name = NULL;
 
   data = gcry_sexp_nth_data (sexp, 0, &data_n);
   if (! data)
@@ -1375,6 +1468,51 @@ sexp_key_extract (gcry_sexp_t sexp,
   if (err)
     goto out;
 
+  if ((key_spec.flags & SPEC_FLAG_IS_ECDSA))
+    {
+      /* Parse the "curve" parameter.  We currently expect the curve
+         name for ECC and not the parameters of the curve.  This can
+         easily be changed but then we need to find the curve name
+         from the parameters using gcry_pk_get_curve.  */
+      const char *mapped;
+
+      value_pair = gcry_sexp_find_token (value_list, "curve", 5);
+      if (!value_pair)
+	{
+	  err = gpg_error (GPG_ERR_INV_CURVE);
+	  goto out;
+        }
+      curve_name = gcry_sexp_nth_string (value_pair, 1);
+      if (!curve_name)
+	{
+	  err = gpg_error (GPG_ERR_INV_CURVE); /* (Or out of core.)  */
+	  goto out;
+        }
+
+      /* Fixme: The mapping should be done by using gcry_pk_get_curve
+         et al to iterate over all name aliases.  */
+      if (!strcmp (curve_name, "NIST P-256"))
+        mapped = "nistp256";
+      else if (!strcmp (curve_name, "NIST P-384"))
+        mapped = "nistp384";
+      else if (!strcmp (curve_name, "NIST P-521"))
+        mapped = "nistp521";
+      else
+        mapped = NULL;
+      if (mapped)
+        {
+          xfree (curve_name);
+          curve_name = xtrystrdup (mapped);
+          if (!curve_name)
+            {
+              err = gpg_error_from_syserror ();
+              goto out;
+            }
+        }
+      gcry_sexp_release (value_pair);
+      value_pair = NULL;
+    }
+
   /* We do not require a comment sublist to be present here.  */
   data = NULL;
   data_n = 0;
@@ -1399,6 +1537,7 @@ sexp_key_extract (gcry_sexp_t sexp,
     *secret = is_secret;
   *mpis = mpis_new;
   *comment = comment_new;
+  *r_curve = curve_name;
 
  out:
 
@@ -1408,6 +1547,7 @@ sexp_key_extract (gcry_sexp_t sexp,
 
   if (err)
     {
+      xfree (curve_name);
       xfree (comment_new);
       mpint_list_free (mpis_new);
     }
@@ -1494,6 +1634,24 @@ ssh_key_type_lookup (const char *ssh_name, const char *name,
   return err;
 }
 
+
+/* Lookup the ssh-identifier for the ECC curve CURVE_NAME.  Returns
+   NULL if not found.  */
+static const char *
+ssh_identifier_from_curve_name (const char *curve_name)
+{
+  int i;
+
+  for (i = 0; i < DIM (ssh_key_types); i++)
+    if (ssh_key_types[i].curve_name
+        && !strcmp (ssh_key_types[i].curve_name, curve_name))
+      return ssh_key_types[i].ssh_identifier;
+
+  return NULL;
+}
+
+
+
 /* Receive a key from STREAM, according to the key specification given
    as KEY_SPEC.  Depending on SECRET, receive a secret or a public
    key.  If READ_COMMENT is true, receive a comment string as well.
@@ -1510,6 +1668,8 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
   ssh_key_type_spec_t spec;
   gcry_mpi_t *mpi_list = NULL;
   const char *elems;
+  char *curve_name = NULL;
+
 
 
   err = stream_read_cstring (stream, &key_type);
@@ -1520,6 +1680,50 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
   if (err)
     goto out;
 
+  if ((spec.flags & SPEC_FLAG_IS_ECDSA))
+    {
+      /* The format of an ECDSA key is:
+       *   string	key_type ("ecdsa-sha2-nistp256" |
+       *                          "ecdsa-sha2-nistp384" |
+       *		          "ecdsa-sha2-nistp521" )
+       *   string	ecdsa_curve_name
+       *   string	ecdsa_public_key
+       *   mpint	ecdsa_private
+       *
+       * Note that we use the mpint reader instead of the string
+       * reader for ecsa_public_key.
+       */
+      unsigned char *buffer;
+      const char *mapped;
+
+      err = stream_read_string (stream, 0, &buffer, NULL);
+      if (err)
+        goto out;
+      curve_name = buffer;
+      /* Fixme: Check that curve_name matches the keytype.  */
+      /* Because Libgcrypt < 1.6 has no support for the "nistpNNN"
+         curve names, we need to translate them here to Libgcrypt's
+         native names.  */
+      if (!strcmp (curve_name, "nistp256"))
+        mapped = "NIST P-256";
+      else if (!strcmp (curve_name, "nistp384"))
+        mapped = "NIST P-384";
+      else if (!strcmp (curve_name, "nistp521"))
+        mapped = "NIST P-521";
+      else
+        mapped = NULL;
+      if (mapped)
+        {
+          xfree (curve_name);
+          curve_name = xtrystrdup (mapped);
+          if (!curve_name)
+            {
+              err = gpg_error_from_syserror ();
+              goto out;
+            }
+        }
+  }
+
   err = ssh_receive_mpint_list (stream, secret, spec, &mpi_list);
   if (err)
     goto out;
@@ -1543,7 +1747,8 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
 	goto out;
     }
 
-  err = sexp_key_construct (&key, spec, secret, mpi_list, comment? comment:"");
+  err = sexp_key_construct (&key, spec, secret, curve_name, mpi_list,
+                            comment? comment:"");
   if (err)
     goto out;
 
@@ -1552,8 +1757,8 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
   *key_new = key;
 
  out:
-
   mpint_list_free (mpi_list);
+  xfree (curve_name);
   xfree (key_type);
   xfree (comment);
 
@@ -1565,7 +1770,8 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
    BLOB/BLOB_SIZE.  Returns zero on success or an error code.  */
 static gpg_error_t
 ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
-			 const char *type, gcry_mpi_t *mpis)
+			 ssh_key_type_spec_t *spec,
+                         const char *curve_name, gcry_mpi_t *mpis)
 {
   unsigned char *blob_new;
   long int blob_size_new;
@@ -1587,14 +1793,31 @@ ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
       goto out;
     }
 
-  err = stream_write_cstring (stream, type);
-  if (err)
-    goto out;
+  if ((spec->flags & SPEC_FLAG_IS_ECDSA) && curve_name)
+    {
+      const char *sshname = ssh_identifier_from_curve_name (curve_name);
+      if (!curve_name)
+        {
+          err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
+          goto out;
+        }
+      err = stream_write_cstring (stream, sshname);
+      if (err)
+        goto out;
+      err = stream_write_cstring (stream, curve_name);
+      if (err)
+        goto out;
+    }
+  else
+    {
+      err = stream_write_cstring (stream, spec->ssh_identifier);
+      if (err)
+        goto out;
+    }
 
-  for (i = 0; mpis[i] && (! err); i++)
-    err = stream_write_mpi (stream, mpis[i]);
-  if (err)
-    goto out;
+  for (i = 0; mpis[i]; i++)
+    if ((err = stream_write_mpi (stream, mpis[i])))
+      goto out;
 
   blob_size_new = es_ftell (stream);
   if (blob_size_new == -1)
@@ -1636,22 +1859,19 @@ ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
    OVERRIDE_COMMENT is not NULL, it will be used instead of the
    comment stored in the key.  */
 static gpg_error_t
-ssh_send_key_public (estream_t stream, gcry_sexp_t key_public,
+ssh_send_key_public (estream_t stream,
+                     gcry_sexp_t key_public,
                      const char *override_comment)
 {
   ssh_key_type_spec_t spec;
-  gcry_mpi_t *mpi_list;
-  char *key_type;
-  char *comment;
-  unsigned char *blob;
+  gcry_mpi_t *mpi_list = NULL;
+  char *key_type = NULL;
+  char *curve;
+  char *comment = NULL;
+  unsigned char *blob = NULL;
   size_t blob_n;
   gpg_error_t err;
 
-  key_type = NULL;
-  mpi_list = NULL;
-  comment = NULL;
-  blob = NULL;
-
   err = sexp_extract_identifier (key_public, &key_type);
   if (err)
     goto out;
@@ -1660,12 +1880,11 @@ ssh_send_key_public (estream_t stream, gcry_sexp_t key_public,
   if (err)
     goto out;
 
-  err = sexp_key_extract (key_public, spec, NULL, &mpi_list, &comment);
+  err = sexp_key_extract (key_public, spec, NULL, &mpi_list, &curve, &comment);
   if (err)
     goto out;
 
-  err = ssh_convert_key_to_blob (&blob, &blob_n,
-                                 spec.ssh_identifier, mpi_list);
+  err = ssh_convert_key_to_blob (&blob, &blob_n, &spec, curve, mpi_list);
   if (err)
     goto out;
 
@@ -1679,8 +1898,9 @@ ssh_send_key_public (estream_t stream, gcry_sexp_t key_public,
  out:
 
   mpint_list_free (mpi_list);
-  xfree (key_type);
+  xfree (curve);
   xfree (comment);
+  xfree (key_type);
   xfree (blob);
 
   return err;
@@ -1733,7 +1953,10 @@ static gpg_error_t
 ssh_key_grip (gcry_sexp_t key, unsigned char *buffer)
 {
   if (!gcry_pk_get_keygrip (key, buffer))
-    return gpg_error (GPG_ERR_INTERNAL);
+    {
+      gpg_error_t err = gcry_pk_testkey (key);
+      return err? err : gpg_error (GPG_ERR_INTERNAL);
+    }
 
   return 0;
 }
@@ -1746,6 +1969,7 @@ static gpg_error_t
 key_secret_to_public (gcry_sexp_t *key_public,
 		      ssh_key_type_spec_t spec, gcry_sexp_t key_secret)
 {
+  char *curve;
   char *comment;
   gcry_mpi_t *mpis;
   gpg_error_t err;
@@ -1754,16 +1978,18 @@ key_secret_to_public (gcry_sexp_t *key_public,
   comment = NULL;
   mpis = NULL;
 
-  err = sexp_key_extract (key_secret, spec, &is_secret, &mpis, &comment);
+  err = sexp_key_extract (key_secret, spec, &is_secret, &mpis,
+                          &curve, &comment);
   if (err)
     goto out;
 
-  err = sexp_key_construct (key_public, spec, 0, mpis, comment);
+  err = sexp_key_construct (key_public, spec, 0, curve, mpis, comment);
 
  out:
 
   mpint_list_free (mpis);
   xfree (comment);
+  xfree (curve);
 
   return err;
 }
@@ -2136,7 +2362,7 @@ data_hash (unsigned char *data, size_t data_n,
    signature in newly allocated memory in SIG and it's size in SIG_N;
    SIG_ENCODER is the signature encoder to use.  */
 static gpg_error_t
-data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
+data_sign (ctrl_t ctrl, ssh_key_type_spec_t *spec,
 	   unsigned char **sig, size_t *sig_n)
 {
   gpg_error_t err;
@@ -2147,10 +2373,6 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
   gcry_mpi_t sig_value = NULL;
   unsigned char *sig_blob = NULL;
   size_t sig_blob_n = 0;
-  char *identifier = NULL;
-  const char *identifier_raw;
-  size_t identifier_n;
-  ssh_key_type_spec_t spec;
   int ret;
   unsigned int i;
   const char *elems;
@@ -2229,29 +2451,11 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
       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_syserror ();
-      goto out;
-    }
-
-  err = ssh_key_type_lookup (NULL, identifier, &spec);
-  if (err)
-    goto out;
-
-  err = stream_write_cstring (stream, spec.ssh_identifier);
+  err = stream_write_cstring (stream, spec->ssh_identifier);
   if (err)
     goto out;
 
-  elems = spec.elems_signature;
+  elems = spec->elems_signature;
   elems_n = strlen (elems);
 
   mpis = xtrycalloc (elems_n + 1, sizeof *mpis);
@@ -2263,7 +2467,7 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
 
   for (i = 0; i < elems_n; i++)
     {
-      sublist = gcry_sexp_find_token (valuelist, spec.elems_signature + i, 1);
+      sublist = gcry_sexp_find_token (valuelist, spec->elems_signature + i, 1);
       if (! sublist)
 	{
 	  err = gpg_error (GPG_ERR_INV_SEXP);
@@ -2284,7 +2488,7 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
   if (err)
     goto out;
 
-  err = (*sig_encoder) (stream, mpis);
+  err = spec->signature_encoder (spec, stream, mpis);
   if (err)
     goto out;
 
@@ -2327,7 +2531,6 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
   gcry_sexp_release (signature_sexp);
   gcry_sexp_release (sublist);
   mpint_list_free (mpis);
-  xfree (identifier);
 
   return err;
 }
@@ -2350,6 +2553,7 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
   u32 flags;
   gpg_error_t err;
   gpg_error_t ret_err;
+  int hash_algo;
 
   key_blob = NULL;
   data = NULL;
@@ -2376,14 +2580,18 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
   if (err)
     goto out;
 
+  hash_algo = spec.hash_algo;
+  if (!hash_algo)
+    hash_algo = GCRY_MD_SHA1;  /* Use the default.  */
+
   /* Hash data.  */
-  hash_n = gcry_md_get_algo_dlen (GCRY_MD_SHA1);
+  hash_n = gcry_md_get_algo_dlen (hash_algo);
   if (! hash_n)
     {
       err = gpg_error (GPG_ERR_INTERNAL);
       goto out;
     }
-  err = data_hash (data, data_size, GCRY_MD_SHA1, hash);
+  err = data_hash (data, data_size, hash_algo, hash);
   if (err)
     goto out;
 
@@ -2394,14 +2602,17 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
 
   /* Sign data.  */
 
-  ctrl->digest.algo = GCRY_MD_SHA1;
+  ctrl->digest.algo = hash_algo;
   memcpy (ctrl->digest.value, hash, hash_n);
   ctrl->digest.valuelen = hash_n;
-  ctrl->digest.raw_value = ! (spec.flags & SPEC_FLAG_USE_PKCS1V2);
+  if ((spec.flags & SPEC_FLAG_USE_PKCS1V2))
+    ctrl->digest.raw_value = 0;
+  else
+    ctrl->digest.raw_value = 1;
   ctrl->have_keygrip = 1;
   memcpy (ctrl->keygrip, key_grip, 20);
 
-  err = data_sign (ctrl, spec.signature_encoder, &sig, &sig_n);
+  err = data_sign (ctrl, &spec, &sig, &sig_n);
 
  out:
 
@@ -2522,6 +2733,7 @@ reenter_compare_cb (struct pin_entry_info_s *pi)
   return -1;
 }
 
+
 /* Store the ssh KEY into our local key storage and protect it after
    asking for a passphrase.  Cache that passphrase.  TTL is the
    maximum caching time for that key.  If the key already exists in
@@ -2572,7 +2784,6 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl, int confirm)
       goto out;
     }
 
-
   pi = gcry_calloc_secure (2, sizeof (*pi) + 100 + 1);
   if (!pi)
     {
diff --git a/agent/protect.c b/agent/protect.c
index d4d7e00..2eefd6d 100644
--- a/agent/protect.c
+++ b/agent/protect.c
@@ -51,10 +51,14 @@ static struct {
   const char *algo;
   const char *parmlist;
   int prot_from, prot_to;
+  int ecc_hack;
 } protect_info[] = {
   { "rsa",  "nedpqu", 2, 5 },
   { "dsa",  "pqgyx", 4, 4 },
   { "elg",  "pgyx", 3, 3 },
+  { "ecdsa","pabgnqd", 6, 6, 1 },
+  { "ecdh", "pabgnqd", 6, 6, 1 },
+  { "ecc",  "pabgnqd", 6, 6, 1 },
   { NULL }
 };
 
@@ -401,6 +405,8 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
                unsigned char **result, size_t *resultlen)
 {
   int rc;
+  const char *parmlist;
+  int prot_from_idx, prot_to_idx;
   const unsigned char *s;
   const unsigned char *hash_begin, *hash_end;
   const unsigned char *prot_begin, *prot_end, *real_end;
@@ -445,10 +451,13 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
   if (!protect_info[infidx].algo)
     return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
 
+  parmlist      = protect_info[infidx].parmlist;
+  prot_from_idx = protect_info[infidx].prot_from;
+  prot_to_idx   = protect_info[infidx].prot_to;
   prot_begin = prot_end = NULL;
-  for (i=0; (c=protect_info[infidx].parmlist[i]); i++)
+  for (i=0; (c=parmlist[i]); i++)
     {
-      if (i == protect_info[infidx].prot_from)
+      if (i == prot_from_idx)
         prot_begin = s;
       if (*s != '(')
         return gpg_error (GPG_ERR_INV_SEXP);
@@ -458,7 +467,20 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
       if (!n)
         return gpg_error (GPG_ERR_INV_SEXP);
       if (n != 1 || c != *s)
-        return gpg_error (GPG_ERR_INV_SEXP);
+        {
+          if (n == 5 && !memcmp (s, "curve", 5)
+              && !i && protect_info[infidx].ecc_hack)
+            {
+              /* This is a private ECC key but the first parameter is
+                 the name of the curve.  We change the parameter list
+                 here to the one we expect in this case.  */
+              parmlist = "?qd";
+              prot_from_idx = 2;
+              prot_to_idx = 2;
+            }
+          else
+            return gpg_error (GPG_ERR_INV_SEXP);
+        }
       s += n;
       n = snext (&s);
       if (!n)
@@ -467,7 +489,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
       if (*s != ')')
         return gpg_error (GPG_ERR_INV_SEXP);
       depth--;
-      if (i == protect_info[infidx].prot_to)
+      if (i == prot_to_idx)
         prot_end = s;
       s++;
     }
@@ -484,7 +506,6 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
   assert (!depth);
   real_end = s-1;
 
-
   /* Hash the stuff.  Because the timestamp_exp won't get protected,
      we can't simply hash a continuous buffer but need to use several
      md_writes.  */
diff --git a/agent/t-protect.c b/agent/t-protect.c
index 0e29caf..16ff7d7 100644
--- a/agent/t-protect.c
+++ b/agent/t-protect.c
@@ -137,7 +137,24 @@ test_agent_protect (void)
       "\x9B\x7B\xE8\xDD\x1F\x87\x4E\x79\x7B\x50\x12\xA7\xB4\x8B\x52\x38\xEC\x7C\xBB\xB9"
       "\x55\x87\x11\x1C\x74\xE7\x7F\xA0\xBA\xE3\x34\x5D\x61\xBF\x29\x29\x29\x00"
     };
-      
+
+  struct key_spec key_ecdsa_valid =
+    {
+      "\x28\x31\x31\x3A\x70\x72\x69\x76\x61\x74\x65\x2D\x6B\x65\x79\x28"
+      "\x35\x3A\x65\x63\x64\x73\x61\x28\x35\x3A\x63\x75\x72\x76\x65\x31"
+      "\x30\x3A\x4E\x49\x53\x54\x20\x50\x2D\x32\x35\x36\x29\x28\x31\x3A"
+      "\x71\x36\x35\x3A\x04\x64\x5A\x12\x6F\x86\x7C\x43\x87\x2B\x7C\xAF"
+      "\x77\xFE\xD8\x22\x31\xEA\xE6\x89\x9F\xAA\xEA\x63\x26\xBC\x49\xED"
+      "\x85\xC6\xD2\xC9\x8B\x38\xD2\x78\x75\xE6\x1C\x27\x57\x01\xC5\xA1"
+      "\xE3\xF9\x1F\xBE\xCF\xC1\x72\x73\xFE\xA4\x58\xB6\x6A\x92\x7D\x33"
+      "\x1D\x02\xC9\xCB\x12\x29\x28\x31\x3A\x64\x33\x33\x3A\x00\x81\x2D"
+      "\x69\x9A\x5F\x5B\x6F\x2C\x99\x61\x36\x15\x6B\x44\xD8\x06\xC1\x54"
+      "\xC1\x4C\xFB\x70\x6A\xB6\x64\x81\x78\xF3\x94\x2F\x30\x5D\x29\x29"
+      "\x28\x37\x3A\x63\x6F\x6D\x6D\x65\x6E\x74\x32\x32\x3A\x2F\x68\x6F"
+      "\x6D\x65\x2F\x77\x6B\x2F\x2E\x73\x73\x68\x2F\x69\x64\x5F\x65\x63"
+      "\x64\x73\x61\x29\x29"
+    };
+
   struct
   {
     const char *key;
@@ -167,6 +184,9 @@ test_agent_protect (void)
       { key_rsa_bogus_1.string,
 	"passphrase", 0, 0, NULL, 0, GPG_ERR_INV_SEXP, NULL, 0 },
 
+      { key_ecdsa_valid.string,
+	"passphrase", 0, 0, NULL, 0, 0, NULL, 0 },
+
       /* FIXME: add more test data.  */
     };
 
@@ -177,12 +197,12 @@ test_agent_protect (void)
 			   &specs[i].result, &specs[i].resultlen);
       if (gpg_err_code (ret) != specs[i].ret_expected)
 	{
-	  printf ("agent_protect() returned `%i/%s'; expected `%i/%s'\n",
-		  ret, gpg_strerror (ret),
+	  printf ("agent_protect(%d) returned '%i/%s'; expected '%i/%s'\n",
+		  i, ret, gpg_strerror (ret),
 		  specs[i].ret_expected, gpg_strerror (specs[i].ret_expected));
 	  abort ();
 	}
-      
+
       if (specs[i].no_result_expected)
 	{
 	  assert (! specs[i].result);
@@ -234,14 +254,14 @@ static void
 test_make_shadow_info (void)
 {
 #if 0
-  static struct 
+  static struct
   {
-    const char *snstr; 
+    const char *snstr;
     const char *idstr;
     const char *expected;
   } data[] = {
     { "", "", NULL },
-    
+
   };
   int i;
   unsigned char *result;
@@ -298,7 +318,7 @@ main (int argc, char **argv)
   (void)argv;
 
   gcry_control (GCRYCTL_DISABLE_SECMEM);
-  
+
   test_agent_protect ();
   test_agent_unprotect ();
   test_agent_private_key_type ();
diff --git a/common/ssh-utils.c b/common/ssh-utils.c
index e2de802..d8f057d 100644
--- a/common/ssh-utils.c
+++ b/common/ssh-utils.c
@@ -89,6 +89,34 @@ get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len,
       elems = "pqgy";
       gcry_md_write (md, "\0\0\0\x07ssh-dss", 11);
       break;
+    case GCRY_PK_ECDSA:
+      /* We only support the 3 standard curves for now.  It is just a
+         quick hack.  */
+      elems = "q";
+      gcry_md_write (md, "\0\0\0\x13" "ecdsa-sha2-nistp", 20);
+      l2 = gcry_sexp_find_token (list, "curve", 0);
+      if (!l2)
+        elems = "";
+      else
+        {
+          gcry_free (name);
+          name = gcry_sexp_nth_string (l2, 1);
+          gcry_sexp_release (l2);
+          l2 = NULL;
+          if (!name)
+            elems = "";
+          else if (!strcmp (name, "NIST P-256") || !strcmp (name, "nistp256"))
+            gcry_md_write (md, "256\0\0\0\x08nistp256", 15);
+          else if (!strcmp (name, "NIST P-384") || !strcmp (name, "nistp384"))
+            gcry_md_write (md, "384\0\0\0\x08nistp521", 15);
+          else if (!strcmp (name, "NIST P-521") || !strcmp (name, "nistp521"))
+            gcry_md_write (md, "521\0\0\0\x08nistp521", 15);
+          else
+            elems = "";
+        }
+      if (!*elems)
+        err = gpg_err_make (errsource, GPG_ERR_UNKNOWN_CURVE);
+      break;
     default:
       elems = "";
       err = gpg_err_make (errsource, GPG_ERR_PUBKEY_ALGO);

commit 4b4df62eaf7f46f83540536bfa373e78be2a6d7d
Author: Werner Koch <wk at gnupg.org>
Date:   Mon Jul 1 20:27:39 2013 +0200

    estream: New function es_fclose_snatch.
    
    * common/estream.c (cookie_ioctl_function_t): New type.
    (es_fclose_snatch): New function.
    (COOKIE_IOCTL_SNATCH_BUFFER): New constant.
    (struct estream_internal): Add field FUNC_IOCTL.
    (es_initialize): Clear FUNC_IOCTL.
    (es_func_mem_ioctl): New function.
    (es_fopenmem): Init FUNC_IOCTL.
    --
    
    (back ported from commit id 7737a2c269657189a583cde7f214f20871d264f8)
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/common/estream.c b/common/estream.c
index 9c781a0..304e0e6 100644
--- a/common/estream.c
+++ b/common/estream.c
@@ -217,8 +217,16 @@ static int estream_pth_killed;
 
 #define ES_DEFAULT_OPEN_MODE (S_IRUSR | S_IWUSR)
 
-/* An internal stream object.  */
+/* A private cookie function to implement an internal IOCTL
+   service.  */
+typedef int (*cookie_ioctl_function_t) (void *cookie, int cmd,
+                                       void *ptr, size_t *len);
+/* IOCTL commands for the private cookie function.  */
+#define COOKIE_IOCTL_SNATCH_BUFFER 1
+
 
+
+/* An internal stream object.  */
 struct estream_internal
 {
   unsigned char buffer[BUFFER_BLOCK_SIZE];
@@ -232,6 +240,7 @@ struct estream_internal
   es_cookie_read_function_t func_read;
   es_cookie_write_function_t func_write;
   es_cookie_seek_function_t func_seek;
+  cookie_ioctl_function_t func_ioctl;
   es_cookie_close_function_t func_close;
   int strategy;
   int fd;
@@ -773,6 +782,34 @@ es_func_mem_seek (void *cookie, off_t *offset, int whence)
 }
 
 
+/* An IOCTL function for memory objects.  */
+static int
+es_func_mem_ioctl (void *cookie, int cmd, void *ptr, size_t *len)
+{
+  estream_cookie_mem_t mem_cookie = cookie;
+  int ret;
+
+  if (cmd == COOKIE_IOCTL_SNATCH_BUFFER)
+    {
+      /* Return the internal buffer of the stream to the caller and
+         invalidate it for the stream.  */
+      *(void**)ptr = mem_cookie->memory;
+      *len = mem_cookie->offset;
+      mem_cookie->memory = NULL;
+      mem_cookie->memory_size = 0;
+      mem_cookie->offset = 0;
+      ret = 0;
+    }
+  else
+    {
+      _set_errno (EINVAL);
+      ret = -1;
+    }
+
+  return ret;
+}
+
+
 /* Destroy function for memory objects.  */
 static int
 es_func_mem_destroy (void *cookie)
@@ -1312,6 +1349,7 @@ es_initialize (estream_t stream,
   stream->intern->func_read = functions.func_read;
   stream->intern->func_write = functions.func_write;
   stream->intern->func_seek = functions.func_seek;
+  stream->intern->func_ioctl = NULL;
   stream->intern->func_close = functions.func_close;
   stream->intern->strategy = _IOFBF;
   stream->intern->fd = fd;
@@ -2317,6 +2355,9 @@ es_fopenmem (size_t memlimit, const char *ES__RESTRICT mode)
   if (es_create (&stream, cookie, -1, estream_functions_mem, modeflags, 0))
     (*estream_functions_mem.func_close) (cookie);
 
+  if (stream)
+    stream->intern->func_ioctl = es_func_mem_ioctl;
+
   return stream;
 }
 
@@ -2604,6 +2645,66 @@ es_fclose (estream_t stream)
   return err;
 }
 
+
+/* This is a special version of es_fclose which can be used with
+   es_fopenmem to return the memory buffer.  This is feature is useful
+   to write to a memory buffer using estream.  Note that the function
+   does not close the stream if the stream does not support snatching
+   the buffer.  On error NULL is stored at R_BUFFER.  Note that if no
+   write operation has happened, NULL may also be stored at BUFFER on
+   success.  The caller needs to release the returned memory using
+   es_free.  */
+int
+es_fclose_snatch (estream_t stream, void **r_buffer, size_t *r_buflen)
+{
+  int err;
+
+  /* Note: There is no need to lock the stream in a close call.  The
+     object will be destroyed after the close and thus any other
+     contender for the lock would work on a closed stream.  */
+
+  if (r_buffer)
+    {
+      cookie_ioctl_function_t func_ioctl = stream->intern->func_ioctl;
+      size_t buflen;
+
+      *r_buffer = NULL;
+
+      if (!func_ioctl)
+        {
+          _set_errno (EOPNOTSUPP);
+          err = -1;
+          goto leave;
+        }
+
+      if (stream->flags.writing)
+        {
+          err = es_flush (stream);
+          if (err)
+            goto leave;
+          stream->flags.writing = 0;
+        }
+
+      err = func_ioctl (stream->intern->cookie, COOKIE_IOCTL_SNATCH_BUFFER,
+                        r_buffer, &buflen);
+      if (err)
+        goto leave;
+      if (r_buflen)
+        *r_buflen = buflen;
+    }
+
+  err = es_destroy (stream, 0);
+
+ leave:
+  if (err && r_buffer)
+    {
+      mem_free (*r_buffer);
+      *r_buffer = NULL;
+    }
+  return err;
+}
+
+
 int
 es_fileno_unlocked (estream_t stream)
 {
diff --git a/common/estream.h b/common/estream.h
index 69f19f4..f72e4c6 100644
--- a/common/estream.h
+++ b/common/estream.h
@@ -85,6 +85,7 @@
 #define es_freopen            _ESTREAM_PREFIX(es_freopen)
 #define es_fopencookie        _ESTREAM_PREFIX(es_fopencookie)
 #define es_fclose             _ESTREAM_PREFIX(es_fclose)
+#define es_fclose_snatch      _ESTREAM_PREFIX(es_fclose_snatch)
 #define es_fileno             _ESTREAM_PREFIX(es_fileno)
 #define es_fileno_unlocked    _ESTREAM_PREFIX(es_fileno_unlocked)
 #define es_flockfile          _ESTREAM_PREFIX(es_flockfile)
@@ -253,6 +254,7 @@ estream_t es_fopencookie (void *ES__RESTRICT cookie,
 			  const char *ES__RESTRICT mode,
 			  es_cookie_io_functions_t functions);
 int es_fclose (estream_t stream);
+int es_fclose_snatch (estream_t stream, void **r_buffer, size_t *r_buflen);
 int es_fileno (estream_t stream);
 int es_fileno_unlocked (estream_t stream);
 

commit cf7f9303272db65465ff45348cf18f7298e41e30
Author: Werner Koch <wk at gnupg.org>
Date:   Tue Dec 11 14:50:34 2012 +0100

    ssh: Rewrite a function for better maintainability
    
    * agent/command-ssh.c (ssh_signature_encoder_dsa): Rewrite.
    --
    
    Using es_fopenmem instead of a preallocated buffer is safer and easier
    to read.
    (cherry picked from commit f76a0312c3794afd81fe1e172df15eb0612deae0)

diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index 18e155d..6b73a5d 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -1190,65 +1190,51 @@ ssh_signature_encoder_dsa (estream_t signature_blob, gcry_mpi_t *mpis)
    by the KEY_SPEC, SECRET, MPIS and COMMENT, which is to be stored in
    *SEXP.  Returns usual error code.  */
 static gpg_error_t
-sexp_key_construct (gcry_sexp_t *sexp,
+sexp_key_construct (gcry_sexp_t *r_sexp,
 		    ssh_key_type_spec_t key_spec, int secret,
 		    gcry_mpi_t *mpis, const char *comment)
 {
   const char *key_identifier[] = { "public-key", "private-key" };
-  gcry_sexp_t sexp_new;
-  char *sexp_template;
-  size_t sexp_template_n;
   gpg_error_t err;
+  gcry_sexp_t sexp_new = NULL;
+  char *formatbuf = NULL;
+  void **arg_list = NULL;
+  int arg_idx;
+  estream_t format;
   const char *elems;
   size_t elems_n;
-  unsigned int i;
-  unsigned int j;
-  void **arg_list;
+  unsigned int i, j;
 
-  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);
 
-  /*
-    Calculate size for sexp_template_n:
-
-    "(%s(%s<mpis>)(comment%s))" -> 20 + sizeof (<mpis>).
-
-    mpi: (X%m) -> 5.
-
-  */
-  sexp_template_n = 20 + (elems_n * 5);
-  sexp_template = xtrymalloc (sexp_template_n);
-  if (! sexp_template)
+  format = es_fopenmem (0, "a+b");
+  if (!format)
     {
       err = gpg_error_from_syserror ();
       goto out;
     }
 
-  /* Key identifier, algorithm identifier, mpis, comment.  */
-  arg_list = xtrymalloc (sizeof (*arg_list) * (2 + elems_n + 1));
-  if (! arg_list)
+  /* Key identifier, algorithm identifier, mpis, comment, and a NULL
+     as a safeguard. */
+  arg_list = xtrymalloc (sizeof (*arg_list) * (2 + elems_n + 1 + 1));
+  if (!arg_list)
     {
       err = gpg_error_from_syserror ();
       goto out;
     }
+  arg_idx = 0;
 
-  i = 0;
-  arg_list[i++] = &key_identifier[secret];
-  arg_list[i++] = &key_spec.identifier;
+  es_fputs ("(%s(%s", format);
+  arg_list[arg_idx++] = &key_identifier[secret];
+  arg_list[arg_idx++] = &key_spec.identifier;
 
-  *sexp_template = 0;
-  sexp_template_n = 0;
-  sexp_template_n = sprintf (sexp_template + sexp_template_n, "(%%s(%%s");
   for (i = 0; i < elems_n; i++)
     {
-      sexp_template_n += sprintf (sexp_template + sexp_template_n, "(%c%%m)",
-				  elems[i]);
+      es_fprintf (format, "(%c%%m)", elems[i]);
       if (secret)
 	{
 	  for (j = 0; j < elems_n; j++)
@@ -1257,27 +1243,42 @@ sexp_key_construct (gcry_sexp_t *sexp,
 	}
       else
 	j = i;
-      arg_list[i + 2] = &mpis[j];
+      arg_list[arg_idx++] = &mpis[j];
     }
-  sexp_template_n += sprintf (sexp_template + sexp_template_n,
-			      ")(comment%%s))");
+  es_fputs (")(comment%s))", format);
+  arg_list[arg_idx++] = &comment;
+  arg_list[arg_idx] = NULL;
 
-  arg_list[i + 2] = &comment;
+  es_putc (0, format);
+  if (es_ferror (format))
+    {
+      err = gpg_error_from_syserror ();
+      goto out;
+    }
+  if (es_fclose_snatch (format, &formatbuf, NULL))
+    {
+      err = gpg_error_from_syserror ();
+      goto out;
+    }
+  format = NULL;
 
-  err = gcry_sexp_build_array (&sexp_new, NULL, sexp_template, arg_list);
+  log_debug ("sexp formatbuf='%s' nargs=%d\n", formatbuf, arg_idx);
+  err = gcry_sexp_build_array (&sexp_new, NULL, formatbuf, arg_list);
   if (err)
     goto out;
 
-  *sexp = sexp_new;
+  *r_sexp = sexp_new;
+  err = 0;
 
  out:
-
+  es_fclose (format);
   xfree (arg_list);
-  xfree (sexp_template);
+  xfree (formatbuf);
 
   return err;
 }
 
+
 /* This functions breaks up the key contained in the S-Expression SEXP
    according to KEY_SPEC.  The MPIs are bundled in a newly create
    list, which is to be stored in MPIS; a newly allocated string

commit 901162579119585ebd3df9001b0370e6d32934ab
Author: Werner Koch <wk at gnupg.org>
Date:   Mon Dec 10 18:27:23 2012 +0100

    ssh: Improve key lookup for many keys.
    
    * agent/command-ssh.c: Remove dirent.h.
    (control_file_s): Add struct item.
    (rewind_control_file): New.
    (search_control_file): Factor code out to ...
    (read_control_file_item): New.
    (ssh_handler_request_identities): Change to iterate over entries in
    sshcontrol.
    --
    
    Formerly we scanned the private key directory for matches of entries
    in sshcontrol.  This patch changes it to scan the sshcontrol file and
    thus considers only keys configured there.  The rationale for this is
    that it is common to have only a few ssh keys but many private keys.
    Even if that assumption does not hold true, the scanning of the
    sshcontrol file is faster than reading the directory and only then
    scanning the ssh control for each directory entry.
    
    (cherry picked from commit d2777f84be0ded5906a9bec3bc23cfed0a9be02f)

diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index 5be86be..18e155d 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -27,7 +27,6 @@
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <dirent.h>
 #include <assert.h>
 
 #include "agent.h"
@@ -169,6 +168,14 @@ struct control_file_s
 {
   char *fname;  /* Name of the file.  */
   FILE *fp;     /* This is never NULL. */
+  int lnr;      /* The current line number.  */
+  struct {
+    int valid;           /* True if the data of this structure is valid.  */
+    int disabled;        /* The item is disabled.  */
+    int ttl;             /* The TTL of the item.   */
+    int confirm;         /* The confirm flag is set.  */
+    char hexgrip[40+1];  /* The hexgrip of the item (uppercase).  */
+  } item;
 };
 typedef struct control_file_s *control_file_t;
 
@@ -742,6 +749,15 @@ open_control_file (control_file_t *r_cf, int append)
 
 
 static void
+rewind_control_file (control_file_t cf)
+{
+  fseek (cf->fp, 0, SEEK_SET);
+  cf->lnr = 0;
+  clearerr (cf->fp);
+}
+
+
+static void
 close_control_file (control_file_t cf)
 {
   if (!cf)
@@ -752,29 +768,19 @@ close_control_file (control_file_t cf)
 }
 
 
-/* Search the control file CF from the beginning until a matching
-   HEXGRIP is found; return success in this case and store true at
-   DISABLED if the found key has been disabled.  If R_TTL is not NULL
-   a specified TTL for that key is stored there.  If R_CONFIRM is not
-   NULL it is set to 1 if the key has the confirm flag set. */
+
+/* Read the next line from the control file and store the data in CF.
+   Returns 0 on success, GPG_ERR_EOF on EOF, or other error codes. */
 static gpg_error_t
-search_control_file (control_file_t cf, const char *hexgrip,
-                     int *r_disabled, int *r_ttl, int *r_confirm)
+read_control_file_item (control_file_t cf)
 {
   int c, i, n;
   char *p, *pend, line[256];
-  long ttl;
-  int lnr = 0;
+  long ttl = 0;
 
-  assert (strlen (hexgrip) == 40 );
-
-  if (r_confirm)
-    *r_confirm = 0;
-
-  fseek (cf->fp, 0, SEEK_SET);
+  cf->item.valid = 0;
   clearerr (cf->fp);
-  *r_disabled = 0;
- next_line:
+
   do
     {
       if (!fgets (line, DIM(line)-1, cf->fp) )
@@ -783,7 +789,7 @@ search_control_file (control_file_t cf, const char *hexgrip,
             return gpg_error (GPG_ERR_EOF);
           return gpg_error_from_syserror ();
         }
-      lnr++;
+      cf->lnr++;
 
       if (!*line || line[strlen(line)-1] != '\n')
         {
@@ -800,35 +806,34 @@ search_control_file (control_file_t cf, const char *hexgrip,
     }
   while (!*p || *p == '\n' || *p == '#');
 
-  *r_disabled = 0;
+  cf->item.disabled = 0;
   if (*p == '!')
     {
-      *r_disabled = 1;
+      cf->item.disabled = 1;
       for (p++; spacep (p); p++)
         ;
     }
 
   for (i=0; hexdigitp (p) && i < 40; p++, i++)
-    if (hexgrip[i] != (*p >= 'a'? (*p & 0xdf): *p))
-      goto next_line;
+    cf->item.hexgrip[i] = (*p >= 'a'? (*p & 0xdf): *p);
+  cf->item.hexgrip[i] = 0;
   if (i != 40 || !(spacep (p) || *p == '\n'))
     {
-      log_error ("invalid formatted line in `%s', line %d\n", cf->fname, lnr);
+      log_error ("%s:%d: invalid formatted line\n", cf->fname, cf->lnr);
       return gpg_error (GPG_ERR_BAD_DATA);
     }
 
   ttl = strtol (p, &pend, 10);
   p = pend;
-  if (!(spacep (p) || *p == '\n') || ttl < -1)
+  if (!(spacep (p) || *p == '\n') || (int)ttl < -1)
     {
-      log_error ("invalid TTL value in `%s', line %d; assuming 0\n",
-                 cf->fname, lnr);
-      ttl = 0;
+      log_error ("%s:%d: invalid TTL value; assuming 0\n", cf->fname, cf->lnr);
+      cf->item.ttl = 0;
     }
-  if (r_ttl)
-    *r_ttl = ttl;
+  cf->item.ttl = ttl;
 
   /* Now check for key-value pairs of the form NAME[=VALUE]. */
+  cf->item.confirm = 0;
   while (*p)
     {
       for (; spacep (p) && *p != '\n'; p++)
@@ -838,22 +843,68 @@ search_control_file (control_file_t cf, const char *hexgrip,
       n = strcspn (p, "= \t\n");
       if (p[n] == '=')
         {
-          log_error ("assigning a value to a flag is not yet supported; "
-                     "in `%s', line %d; flag ignored\n", cf->fname, lnr);
+          log_error ("%s:%d: assigning a value to a flag is not yet supported; "
+                     "flag ignored\n", cf->fname, cf->lnr);
           p++;
         }
       else if (n == 7 && !memcmp (p, "confirm", 7))
         {
-          if (r_confirm)
-            *r_confirm = 1;
+          cf->item.confirm = 1;
         }
       else
-        log_error ("invalid flag `%.*s' in `%s', line %d; ignored\n",
-                   n, p, cf->fname, lnr);
+        log_error ("%s:%d: invalid flag '%.*s'; ignored\n",
+                   cf->fname, cf->lnr, n, p);
       p += n;
     }
 
-  return 0; /* Okay:  found it.  */
+  /* log_debug ("%s:%d: grip=%s ttl=%d%s%s\n", */
+  /*            cf->fname, cf->lnr, */
+  /*            cf->item.hexgrip, cf->item.ttl, */
+  /*            cf->item.disabled? " disabled":"", */
+  /*            cf->item.confirm? " confirm":""); */
+
+  cf->item.valid = 1;
+  return 0; /* Okay: valid entry found.  */
+}
+
+
+
+/* Search the control file CF from the beginning until a matching
+   HEXGRIP is found; return success in this case and store true at
+   DISABLED if the found key has been disabled.  If R_TTL is not NULL
+   a specified TTL for that key is stored there.  If R_CONFIRM is not
+   NULL it is set to 1 if the key has the confirm flag set. */
+static gpg_error_t
+search_control_file (control_file_t cf, const char *hexgrip,
+                     int *r_disabled, int *r_ttl, int *r_confirm)
+{
+  gpg_error_t err;
+
+  assert (strlen (hexgrip) == 40 );
+
+  *r_disabled = 0;
+  if (r_ttl)
+    *r_ttl = 0;
+  if (r_confirm)
+    *r_confirm = 0;
+
+  rewind_control_file (cf);
+  while (!(err=read_control_file_item (cf)))
+    {
+      if (!cf->item.valid)
+        continue; /* Should not happen.  */
+      if (!strcmp (hexgrip, cf->item.hexgrip))
+        break;
+    }
+  if (!err)
+    {
+      *r_disabled = cf->item.disabled;
+      if (r_ttl)
+        *r_ttl = cf->item.ttl;
+      if (r_confirm)
+        *r_confirm = cf->item.confirm;
+    }
+  return err;
 }
 
 
@@ -1898,19 +1949,13 @@ static gpg_error_t
 ssh_handler_request_identities (ctrl_t ctrl,
                                 estream_t request, estream_t response)
 {
-  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;
+  char *key_fname = NULL;
+  char *fnameptr;
   u32 key_counter;
   estream_t key_blobs;
   gcry_sexp_t key_secret;
   gcry_sexp_t key_public;
-  DIR *dir;
   gpg_error_t err;
   int ret;
   control_file_t cf = NULL;
@@ -1921,14 +1966,9 @@ ssh_handler_request_identities (ctrl_t ctrl,
 
   /* Prepare buffer stream.  */
 
-  key_directory = NULL;
   key_secret = NULL;
   key_public = NULL;
-  key_type = NULL;
-  key_path = NULL;
   key_counter = 0;
-  buffer = NULL;
-  dir = NULL;
   err = 0;
 
   key_blobs = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
@@ -1938,34 +1978,6 @@ ssh_handler_request_identities (ctrl_t ctrl,
       goto out;
     }
 
-  /* Open key directory.  */
-  key_directory = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, NULL);
-  if (! key_directory)
-    {
-      err = gpg_err_code_from_errno (errno);
-      goto out;
-    }
-  key_directory_n = strlen (key_directory);
-
-  key_path = xtrymalloc (key_directory_n + 46);
-  if (! key_path)
-    {
-      err = gpg_err_code_from_errno (errno);
-      goto out;
-    }
-
-  sprintf (key_path, "%s/", key_directory);
-  sprintf (key_path + key_directory_n + 41, ".key");
-
-  dir = opendir (key_directory);
-  if (! dir)
-    {
-      err = gpg_err_code_from_errno (errno);
-      goto out;
-    }
-
-
-
   /* First check whether a key is currently available in the card
      reader - this should be allowed even without being listed in
      sshcontrol. */
@@ -1984,77 +1996,93 @@ ssh_handler_request_identities (ctrl_t ctrl,
     }
 
 
-  /* Then look at all the registered an allowed keys. */
+  /* Prepare buffer for key name construction.  */
+  {
+    char *dname;
 
+    dname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, NULL);
+    if (!dname)
+      {
+        err = gpg_err_code_from_syserror ();
+        goto out;
+      }
 
-  /* Fixme: We should better iterate over the control file and check
-     whether the key file is there.  This is better in respect to
-     performance if there are a lot of keys in our key storage. */
-  /* FIXME: make sure that buffer gets deallocated properly.  */
+    key_fname = xtrymalloc (strlen (dname) + 1 + 40 + 4 + 1);
+    if (!key_fname)
+      {
+        err = gpg_err_code_from_syserror ();
+        xfree (dname);
+        goto out;
+      }
+    fnameptr = stpcpy (stpcpy (key_fname, dname), "/");
+    xfree (dname);
+  }
+
+  /* Then look at all the registered and non-disabled keys. */
   err = open_control_file (&cf, 0);
   if (err)
     goto out;
 
-  while ( (dir_entry = readdir (dir)) )
+  while (!read_control_file_item (cf))
     {
-      if ((strlen (dir_entry->d_name) == 44)
-          && (! strncmp (dir_entry->d_name + 40, ".key", 4)))
-        {
-          char hexgrip[41];
-          int disabled;
-
-          /* We do only want to return keys listed in our control
-             file. */
-          strncpy (hexgrip, dir_entry->d_name, 40);
-          hexgrip[40] = 0;
-          if ( strlen (hexgrip) != 40 )
-            continue;
-          if (search_control_file (cf, hexgrip, &disabled, NULL, NULL)
-              || disabled)
-            continue;
-
-          strncpy (key_path + key_directory_n + 1, dir_entry->d_name, 40);
+      if (!cf->item.valid)
+        continue; /* Should not happen.  */
+      if (cf->item.disabled)
+        continue;
+      assert (strlen (cf->item.hexgrip) == 40);
 
-          /* Read file content.  */
-          err = file_to_buffer (key_path, &buffer, &buffer_n);
-          if (err)
-            goto out;
+      stpcpy (stpcpy (fnameptr, cf->item.hexgrip), ".key");
 
-          err = gcry_sexp_sscan (&key_secret, NULL, (char*)buffer, buffer_n);
-          if (err)
-            goto out;
+      /* Read file content.  */
+      {
+        unsigned char *buffer;
+        size_t buffer_n;
+
+        err = file_to_buffer (key_fname, &buffer, &buffer_n);
+        if (err)
+          {
+            log_error ("%s:%d: key '%s' skipped: %s\n",
+                       cf->fname, cf->lnr, cf->item.hexgrip,
+                       gpg_strerror (err));
+            continue;
+          }
 
-          xfree (buffer);
-          buffer = NULL;
+        err = gcry_sexp_sscan (&key_secret, NULL, (char*)buffer, buffer_n);
+        xfree (buffer);
+        if (err)
+          goto out;
+      }
 
-          err = sexp_extract_identifier (key_secret, &key_type);
-          if (err)
-            goto out;
+      {
+        char *key_type = NULL;
 
-          err = ssh_key_type_lookup (NULL, key_type, &spec);
-          if (err)
-            goto out;
+        err = sexp_extract_identifier (key_secret, &key_type);
+        if (err)
+          goto out;
 
-          xfree (key_type);
-          key_type = NULL;
+        err = ssh_key_type_lookup (NULL, key_type, &spec);
+        xfree (key_type);
+        if (err)
+          goto out;
+      }
 
-          err = key_secret_to_public (&key_public, spec, key_secret);
-          if (err)
-            goto out;
+      err = key_secret_to_public (&key_public, spec, key_secret);
+      if (err)
+        goto out;
 
-          gcry_sexp_release (key_secret);
-          key_secret = NULL;
+      gcry_sexp_release (key_secret);
+      key_secret = NULL;
 
-          err = ssh_send_key_public (key_blobs, key_public, NULL);
-          if (err)
-            goto out;
+      err = ssh_send_key_public (key_blobs, key_public, NULL);
+      if (err)
+        goto out;
 
-          gcry_sexp_release (key_public);
-          key_public = NULL;
+      gcry_sexp_release (key_public);
+      key_public = NULL;
 
-          key_counter++;
-        }
+      key_counter++;
     }
+  err = 0;
 
   ret = es_fseek (key_blobs, 0, SEEK_SET);
   if (ret)
@@ -2064,43 +2092,27 @@ ssh_handler_request_identities (ctrl_t ctrl,
     }
 
  out:
-
   /* Send response.  */
 
   gcry_sexp_release (key_secret);
   gcry_sexp_release (key_public);
 
-  if (! err)
+  if (!err)
     {
       ret_err = stream_write_byte (response, SSH_RESPONSE_IDENTITIES_ANSWER);
-      if (ret_err)
-	goto leave;
-      ret_err = stream_write_uint32 (response, key_counter);
-      if (ret_err)
-	goto leave;
-      ret_err = stream_copy (response, key_blobs);
-      if (ret_err)
-	goto leave;
+      if (!ret_err)
+        ret_err = stream_write_uint32 (response, key_counter);
+      if (!ret_err)
+        ret_err = stream_copy (response, key_blobs);
     }
   else
     {
       ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
-      goto leave;
-    };
-
- leave:
-
-  if (key_blobs)
-    es_fclose (key_blobs);
-  if (dir)
-    closedir (dir);
+    }
 
+  es_fclose (key_blobs);
   close_control_file (cf);
-
-  xfree (key_directory);
-  xfree (key_path);
-  xfree (buffer);
-  xfree (key_type);
+  xfree (key_fname);
 
   return ret_err;
 }

commit 336112e519079f43278a8ca8c2937417bc667d8f
Author: Werner Koch <wk at gnupg.org>
Date:   Mon Dec 10 16:39:12 2012 +0100

    ssh: Cleanup sshcontrol file access code.
    
    * agent/command-ssh.c (SSH_CONTROL_FILE_NAME): New macro to replace
    the direct use of the string.
    (struct control_file_s, control_file_t): New.
    (open_control_file, close_control_file): New.  Use them instead of
    using fopen/fclose directly.
    --
    
    (cherry picked from commit 25fb53ab4ae7e1c098500229c776d29b82713a20)
    
    Fixed conflicts in some variabale names.

diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index fe0980e..5be86be 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -1,5 +1,5 @@
 /* command-ssh.c - gpg-agent's ssh-agent emulation layer
- * Copyright (C) 2004, 2005, 2006, 2009 Free Software Foundation, Inc.
+ * Copyright (C) 2004, 2005, 2006, 2009, 2012 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -64,6 +64,8 @@
 #define SSH_DSA_SIGNATURE_ELEMS    2
 #define SPEC_FLAG_USE_PKCS1V2 (1 << 0)
 
+/* The name of the control file.  */
+#define SSH_CONTROL_FILE_NAME "sshcontrol"
 
 /* The blurb we put into the header of a newly created control file.  */
 static const char sshcontrolblurb[] =
@@ -80,7 +82,6 @@ static const char sshcontrolblurb[] =
 "\n";
 
 
-
 /* Macros.  */
 
 /* Return a new uint32 with b0 being the most significant byte and b3
@@ -163,6 +164,16 @@ typedef struct ssh_key_type_spec
 } ssh_key_type_spec_t;
 
 
+/* An object used to access the sshcontrol file.  */
+struct control_file_s
+{
+  char *fname;  /* Name of the file.  */
+  FILE *fp;     /* This is never NULL. */
+};
+typedef struct control_file_s *control_file_t;
+
+
+
 /* Prototypes.  */
 static gpg_error_t ssh_handler_request_identities (ctrl_t ctrl,
 						   estream_t request,
@@ -660,92 +671,124 @@ file_to_buffer (const char *filename, unsigned char **buffer, size_t *buffer_n)
 
 
 

-/* Open the ssh control file and create it if not available. With
+/* Open the ssh control file and create it if not available.  With
    APPEND passed as true the file will be opened in append mode,
-   otherwise in read only mode.  On success a file pointer is stored
-   at the address of R_FP. */
+   otherwise in read only mode.  On success 0 is returned and a new
+   control file object stored at R_CF.  On error an error code is
+   returned and NULL is stored at R_CF.  */
 static gpg_error_t
-open_control_file (FILE **r_fp, int append)
+open_control_file (control_file_t *r_cf, int append)
 {
   gpg_error_t err;
-  char *fname;
-  FILE *fp;
+  control_file_t cf;
+
+  cf = xtrycalloc (1, sizeof *cf);
+  if (!cf)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
 
   /* Note: As soon as we start to use non blocking functions here
      (i.e. where Pth might switch threads) we need to employ a
      mutex.  */
-  *r_fp = NULL;
-  fname = make_filename (opt.homedir, "sshcontrol", NULL);
+  cf->fname = make_filename_try (opt.homedir, SSH_CONTROL_FILE_NAME, NULL);
+  if (!cf->fname)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
   /* FIXME: With "a+" we are not able to check whether this will will
      be created and thus the blurb needs to be written first.  */
-  fp = fopen (fname, append? "a+":"r");
-  if (!fp && errno == ENOENT)
+  cf->fp = fopen (cf->fname, append? "a+":"r");
+  if (!cf->fp && errno == ENOENT)
     {
-      estream_t stream = es_fopen (fname, "wx,mode=-rw-r");
+      estream_t stream = es_fopen (cf->fname, "wx,mode=-rw-r");
       if (!stream)
         {
           err = gpg_error_from_syserror ();
-          log_error (_("can't create `%s': %s\n"), fname, gpg_strerror (err));
-          xfree (fname);
-          return err;
+          log_error (_("can't create `%s': %s\n"),
+                     cf->fname, gpg_strerror (err));
+          goto leave;
         }
       es_fputs (sshcontrolblurb, stream);
       es_fclose (stream);
-      fp = fopen (fname, append? "a+":"r");
+      cf->fp = fopen (cf->fname, append? "a+":"r");
     }
 
-  if (!fp)
+  if (!cf->fp)
     {
-      err = gpg_error (gpg_err_code_from_errno (errno));
-      log_error (_("can't open `%s': %s\n"), fname, gpg_strerror (err));
-      xfree (fname);
-      return err;
+      err = gpg_error_from_syserror ();
+      log_error (_("can't open `%s': %s\n"),
+                 cf->fname, gpg_strerror (err));
+      goto leave;
     }
 
-  *r_fp = fp;
+  err = 0;
 
-  return 0;
+ leave:
+  if (err && cf)
+    {
+      if (cf->fp)
+        fclose (cf->fp);
+      xfree (cf->fname);
+      xfree (cf);
+    }
+  else
+    *r_cf = cf;
+
+  return err;
+}
+
+
+static void
+close_control_file (control_file_t cf)
+{
+  if (!cf)
+    return;
+  fclose (cf->fp);
+  xfree (cf->fname);
+  xfree (cf);
 }
 
 
-/* Search the file at stream FP from the beginning until a matching
+/* Search the control file CF from the beginning until a matching
    HEXGRIP is found; return success in this case and store true at
    DISABLED if the found key has been disabled.  If R_TTL is not NULL
    a specified TTL for that key is stored there.  If R_CONFIRM is not
    NULL it is set to 1 if the key has the confirm flag set. */
 static gpg_error_t
-search_control_file (FILE *fp, const char *hexgrip,
+search_control_file (control_file_t cf, const char *hexgrip,
                      int *r_disabled, int *r_ttl, int *r_confirm)
 {
   int c, i, n;
   char *p, *pend, line[256];
   long ttl;
   int lnr = 0;
-  const char fname[] = "sshcontrol";
 
   assert (strlen (hexgrip) == 40 );
 
   if (r_confirm)
     *r_confirm = 0;
 
-  fseek (fp, 0, SEEK_SET);
-  clearerr (fp);
+  fseek (cf->fp, 0, SEEK_SET);
+  clearerr (cf->fp);
   *r_disabled = 0;
  next_line:
   do
     {
-      if (!fgets (line, DIM(line)-1, fp) )
+      if (!fgets (line, DIM(line)-1, cf->fp) )
         {
-          if (feof (fp))
+          if (feof (cf->fp))
             return gpg_error (GPG_ERR_EOF);
-          return gpg_error (gpg_err_code_from_errno (errno));
+          return gpg_error_from_syserror ();
         }
       lnr++;
 
       if (!*line || line[strlen(line)-1] != '\n')
         {
           /* Eat until end of line */
-          while ( (c=getc (fp)) != EOF && c != '\n')
+          while ( (c=getc (cf->fp)) != EOF && c != '\n')
             ;
           return gpg_error (*line? GPG_ERR_LINE_TOO_LONG
                                  : GPG_ERR_INCOMPLETE_LINE);
@@ -770,7 +813,7 @@ search_control_file (FILE *fp, const char *hexgrip,
       goto next_line;
   if (i != 40 || !(spacep (p) || *p == '\n'))
     {
-      log_error ("invalid formatted line in `%s', line %d\n", fname, lnr);
+      log_error ("invalid formatted line in `%s', line %d\n", cf->fname, lnr);
       return gpg_error (GPG_ERR_BAD_DATA);
     }
 
@@ -779,7 +822,7 @@ search_control_file (FILE *fp, const char *hexgrip,
   if (!(spacep (p) || *p == '\n') || ttl < -1)
     {
       log_error ("invalid TTL value in `%s', line %d; assuming 0\n",
-                 fname, lnr);
+                 cf->fname, lnr);
       ttl = 0;
     }
   if (r_ttl)
@@ -796,7 +839,7 @@ search_control_file (FILE *fp, const char *hexgrip,
       if (p[n] == '=')
         {
           log_error ("assigning a value to a flag is not yet supported; "
-                     "in `%s', line %d; flag ignored\n", fname, lnr);
+                     "in `%s', line %d; flag ignored\n", cf->fname, lnr);
           p++;
         }
       else if (n == 7 && !memcmp (p, "confirm", 7))
@@ -806,7 +849,7 @@ search_control_file (FILE *fp, const char *hexgrip,
         }
       else
         log_error ("invalid flag `%.*s' in `%s', line %d; ignored\n",
-                   n, p, fname, lnr);
+                   n, p, cf->fname, lnr);
       p += n;
     }
 
@@ -825,16 +868,16 @@ add_control_entry (ctrl_t ctrl, const char *hexgrip, const char *fmtfpr,
                    int ttl, int confirm)
 {
   gpg_error_t err;
-  FILE *fp;
+  control_file_t cf;
   int disabled;
 
   (void)ctrl;
 
-  err = open_control_file (&fp, 1);
+  err = open_control_file (&cf, 1);
   if (err)
     return err;
 
-  err = search_control_file (fp, hexgrip, &disabled, NULL, NULL);
+  err = search_control_file (cf, hexgrip, &disabled, NULL, NULL);
   if (err && gpg_err_code(err) == GPG_ERR_EOF)
     {
       struct tm *tp;
@@ -843,15 +886,16 @@ add_control_entry (ctrl_t ctrl, const char *hexgrip, const char *fmtfpr,
       /* Not yet in the file - add it. Because the file has been
          opened in append mode, we simply need to write to it.  */
       tp = localtime (&atime);
-      fprintf (fp, ("# Key added on: %04d-%02d-%02d %02d:%02d:%02d\n"
-                    "# Fingerprint:  %s\n"
-                    "%s %d%s\n"),
+      fprintf (cf->fp,
+               ("# Key added on: %04d-%02d-%02d %02d:%02d:%02d\n"
+                "# Fingerprint:  %s\n"
+                "%s %d%s\n"),
                1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
                tp->tm_hour, tp->tm_min, tp->tm_sec,
                fmtfpr, hexgrip, ttl, confirm? " confirm":"");
 
     }
-  fclose (fp);
+  close_control_file (cf);
   return 0;
 }
 
@@ -860,20 +904,20 @@ add_control_entry (ctrl_t ctrl, const char *hexgrip, const char *fmtfpr,
 static int
 ttl_from_sshcontrol (const char *hexgrip)
 {
-  FILE *fp;
+  control_file_t cf;
   int disabled, ttl;
 
   if (!hexgrip || strlen (hexgrip) != 40)
     return 0;  /* Wrong input: Use global default.  */
 
-  if (open_control_file (&fp, 0))
+  if (open_control_file (&cf, 0))
     return 0; /* Error: Use the global default TTL.  */
 
-  if (search_control_file (fp, hexgrip, &disabled, &ttl, NULL)
+  if (search_control_file (cf, hexgrip, &disabled, &ttl, NULL)
       || disabled)
     ttl = 0;  /* Use the global default if not found or disabled.  */
 
-  fclose (fp);
+  close_control_file (cf);
 
   return ttl;
 }
@@ -883,21 +927,21 @@ ttl_from_sshcontrol (const char *hexgrip)
 static int
 confirm_flag_from_sshcontrol (const char *hexgrip)
 {
-  FILE *fp;
+  control_file_t cf;
   int disabled, confirm;
 
   if (!hexgrip || strlen (hexgrip) != 40)
     return 1;  /* Wrong input: Better ask for confirmation.  */
 
-  if (open_control_file (&fp, 0))
+  if (open_control_file (&cf, 0))
     return 1; /* Error: Better ask for confirmation.  */
 
-  if (search_control_file (fp, hexgrip, &disabled, NULL, &confirm)
+  if (search_control_file (cf, hexgrip, &disabled, NULL, &confirm)
       || disabled)
     confirm = 0;  /* If not found or disabled, there is no reason to
                      ask for confirmation.  */
 
-  fclose (fp);
+  close_control_file (cf);
 
   return confirm;
 }
@@ -1869,7 +1913,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
   DIR *dir;
   gpg_error_t err;
   int ret;
-  FILE *ctrl_fp = NULL;
+  control_file_t cf = NULL;
   char *cardsn;
   gpg_error_t ret_err;
 
@@ -1944,10 +1988,10 @@ ssh_handler_request_identities (ctrl_t ctrl,
 
 
   /* Fixme: We should better iterate over the control file and check
-     whether the key file is there.  This is better in resepct to
-     performance if tehre are a lot of key sin our key storage. */
+     whether the key file is there.  This is better in respect to
+     performance if there are a lot of keys in our key storage. */
   /* FIXME: make sure that buffer gets deallocated properly.  */
-  err = open_control_file (&ctrl_fp, 0);
+  err = open_control_file (&cf, 0);
   if (err)
     goto out;
 
@@ -1965,7 +2009,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
           hexgrip[40] = 0;
           if ( strlen (hexgrip) != 40 )
             continue;
-          if (search_control_file (ctrl_fp, hexgrip, &disabled, NULL, NULL)
+          if (search_control_file (cf, hexgrip, &disabled, NULL, NULL)
               || disabled)
             continue;
 
@@ -2051,8 +2095,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
   if (dir)
     closedir (dir);
 
-  if (ctrl_fp)
-    fclose (ctrl_fp);
+  close_control_file (cf);
 
   xfree (key_directory);
   xfree (key_path);

commit fc7d033d8e62f6a289fdf7dba26af076accb5fd2
Author: Werner Koch <wk at gnupg.org>
Date:   Mon Jul 1 18:29:21 2013 +0200

    ssh: Do not look for a card based ssh key if scdaemon is disabled.
    
    * agent/command-ssh.c (ssh_handler_request_identities): Do not call
    card_key_available if the scdaemon is disabled.
    --
    
    (back ported from commit id 781e9746dff21fc2721373205e63d1d09722d590)

diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index e96d6f5..fe0980e 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -1926,7 +1926,8 @@ ssh_handler_request_identities (ctrl_t ctrl,
      reader - this should be allowed even without being listed in
      sshcontrol. */
 
-  if (!card_key_available (ctrl, &key_public, &cardsn))
+  if (!opt.disable_scdaemon
+      && !card_key_available (ctrl, &key_public, &cardsn))
     {
       err = ssh_send_key_public (key_blobs, key_public, cardsn);
       gcry_sexp_release (key_public);

commit 7ce72c97bfe1ab2f58248a6afe629aafa20d058b
Author: Werner Koch <wk at gnupg.org>
Date:   Mon Jul 1 18:08:56 2013 +0200

    ssh: Make the mode extension "x" portable by a call to es_fopen.
    
    * agent/command-ssh.c (open_control_file): Use_es_fopen to support
    the "wx" mode flag.
    
    --
    
    This also patch also specifies a file mode parameter.  However, this
    will only be used with an updated version of es_stream which we have
    not yet done.

diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index 2f96ef5..e96d6f5 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -681,18 +681,16 @@ open_control_file (FILE **r_fp, int append)
   fp = fopen (fname, append? "a+":"r");
   if (!fp && errno == ENOENT)
     {
-      /* Fixme: "x" is a GNU extension.  We might want to use the es_
-         functions here.  */
-      fp = fopen (fname, "wx");
-      if (!fp)
+      estream_t stream = es_fopen (fname, "wx,mode=-rw-r");
+      if (!stream)
         {
-          err = gpg_error (gpg_err_code_from_errno (errno));
+          err = gpg_error_from_syserror ();
           log_error (_("can't create `%s': %s\n"), fname, gpg_strerror (err));
           xfree (fname);
           return err;
         }
-      fputs (sshcontrolblurb, fp);
-      fclose (fp);
+      es_fputs (sshcontrolblurb, stream);
+      es_fclose (stream);
       fp = fopen (fname, append? "a+":"r");
     }
 

-----------------------------------------------------------------------

Summary of changes:
 NEWS                |    3 +
 agent/command-ssh.c |  950 +++++++++++++++++++++++++++++++++------------------
 agent/protect.c     |   31 ++-
 agent/t-protect.c   |   36 ++-
 common/estream.c    |  103 ++++++-
 common/estream.h    |    2 +
 common/ssh-utils.c  |   28 ++
 7 files changed, 798 insertions(+), 355 deletions(-)


hooks/post-receive
-- 
The GNU Privacy Guard
http://git.gnupg.org




More information about the Gnupg-commits mailing list