agent: Include missing prototype.
[gnupg.git] / agent / command-ssh.c
index be2a838..39435aa 100644 (file)
@@ -1,11 +1,11 @@
 /* command-ssh.c - gpg-agent's ssh-agent emulation layer
 /* command-ssh.c - gpg-agent's ssh-agent emulation layer
- * Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2004, 2005, 2006, 2009, 2012 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA
+ * 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>
 
 
 #include <config.h>
 
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <dirent.h>
 #include <assert.h>
 
 #include "agent.h"
 
 #include <assert.h>
 
 #include "agent.h"
 
-#include "estream.h"
 #include "i18n.h"
 #include "i18n.h"
+#include "../common/ssh-utils.h"
+
 
 \f
 
 
 \f
 
 #define SSH_DSA_SIGNATURE_PADDING 20
 #define SSH_DSA_SIGNATURE_ELEMS    2
 #define SPEC_FLAG_USE_PKCS1V2 (1 << 0)
 #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"
 
 /* The blurb we put into the header of a newly created control file.  */
 static const char sshcontrolblurb[] =
 
 /* The blurb we put into the header of a newly created control file.  */
 static const char sshcontrolblurb[] =
@@ -71,7 +83,7 @@ static const char sshcontrolblurb[] =
 "# in the SSH protocol.  The ssh-add tool may add new entries to this\n"
 "# file to enable them; you may also add them manually.  Comment\n"
 "# lines, like this one, as well as empty lines are ignored.  Lines do\n"
 "# in the SSH protocol.  The ssh-add tool may add new entries to this\n"
 "# file to enable them; you may also add them manually.  Comment\n"
 "# lines, like this one, as well as empty lines are ignored.  Lines do\n"
-"# have a certain length limit but this is not serious limitation as\n" 
+"# have a certain length limit but this is not serious limitation as\n"
 "# the format of the entries is fixed and checked by gpg-agent. A\n"
 "# non-comment line starts with optional white spaces, followed by the\n"
 "# keygrip of the key given as 40 hex digits, optionally followed by a\n"
 "# the format of the entries is fixed and checked by gpg-agent. A\n"
 "# non-comment line starts with optional white spaces, followed by the\n"
 "# keygrip of the key given as 40 hex digits, optionally followed by a\n"
@@ -80,7 +92,6 @@ static const char sshcontrolblurb[] =
 "\n";
 
 
 "\n";
 
 
-
 /* Macros.  */
 
 /* Return a new uint32 with b0 being the most significant byte and b3
 /* Macros.  */
 
 /* Return a new uint32 with b0 being the most significant byte and b3
@@ -100,6 +111,10 @@ typedef gpg_error_t (*ssh_request_handler_t) (ctrl_t ctrl,
                                              estream_t request,
                                              estream_t response);
 
                                              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
 /* Type, which is used for associating request handlers with the
    appropriate request IDs.  */
 typedef struct ssh_request_spec
@@ -120,12 +135,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.  */
 /* 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.  */
                                                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;
 {
   /* Algorithm identifier as used by OpenSSH.  */
   const char *ssh_identifier;
@@ -158,9 +174,34 @@ typedef struct ssh_key_type_spec
      algorithm.  */
   ssh_signature_encoder_t signature_encoder;
 
      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;
   /* Misc flags.  */
   unsigned int flags;
-} 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. */
+  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;
+
 
 
 /* Prototypes.  */
 
 
 /* Prototypes.  */
@@ -187,15 +228,20 @@ 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);
                                       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);
                                               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);
                                               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);
 
 
 
 /* Global variables.  */
 
 
 
 /* Global variables.  */
-   
+
 
 /* Associating request types with the corresponding request
    handlers.  */
 
 /* Associating request types with the corresponding request
    handlers.  */
@@ -223,13 +269,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,
     {
       "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,
     },
     {
       "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
+    }
+
   };
 
 \f
   };
 
 \f
@@ -237,7 +299,7 @@ static ssh_key_type_spec_t ssh_key_types[] =
 
 
 /*
 
 
 /*
-   General utility functions. 
+   General utility functions.
  */
 
 /* A secure realloc, i.e. it makes sure to allocate secure memory if A
  */
 
 /* A secure realloc, i.e. it makes sure to allocate secure memory if A
@@ -248,7 +310,7 @@ static void *
 realloc_secure (void *a, size_t n)
 {
   void *p;
 realloc_secure (void *a, size_t n)
 {
   void *p;
-  
+
   if (a)
     p = gcry_realloc (a, n);
   else
   if (a)
     p = gcry_realloc (a, n);
   else
@@ -268,7 +330,7 @@ make_cstring (const char *data, size_t data_n)
   s = xtrymalloc (data_n + 1);
   if (s)
     {
   s = xtrymalloc (data_n + 1);
   if (s)
     {
-      strncpy (s, data, data_n);
+      memcpy (s, data, data_n);
       s[data_n] = 0;
     }
 
       s[data_n] = 0;
     }
 
@@ -278,8 +340,8 @@ make_cstring (const char *data, size_t data_n)
 
 
 
 
 
 
-/* 
-   Primitive I/O functions.  
+/*
+   Primitive I/O functions.
  */
 
 
  */
 
 
@@ -294,7 +356,7 @@ stream_read_byte (estream_t stream, unsigned char *b)
   if (ret == EOF)
     {
       if (es_ferror (stream))
   if (ret == EOF)
     {
       if (es_ferror (stream))
-       err = gpg_error_from_errno (errno);
+       err = gpg_error_from_syserror ();
       else
        err = gpg_error (GPG_ERR_EOF);
       *b = 0;
       else
        err = gpg_error (GPG_ERR_EOF);
       *b = 0;
@@ -317,13 +379,14 @@ stream_write_byte (estream_t stream, unsigned char b)
 
   ret = es_fputc (b, stream);
   if (ret == EOF)
 
   ret = es_fputc (b, stream);
   if (ret == EOF)
-    err = gpg_error_from_errno (errno);
+    err = gpg_error_from_syserror ();
   else
     err = 0;
 
   return err;
 }
 
   else
     err = 0;
 
   return err;
 }
 
+
 /* Read a uint32 from STREAM, store it in UINT32.  */
 static gpg_error_t
 stream_read_uint32 (estream_t stream, u32 *uint32)
 /* Read a uint32 from STREAM, store it in UINT32.  */
 static gpg_error_t
 stream_read_uint32 (estream_t stream, u32 *uint32)
@@ -335,7 +398,7 @@ stream_read_uint32 (estream_t stream, u32 *uint32)
 
   ret = es_read (stream, buffer, sizeof (buffer), &bytes_read);
   if (ret)
 
   ret = es_read (stream, buffer, sizeof (buffer), &bytes_read);
   if (ret)
-    err = gpg_error_from_errno (errno);
+    err = gpg_error_from_syserror ();
   else
     {
       if (bytes_read != sizeof (buffer))
   else
     {
       if (bytes_read != sizeof (buffer))
@@ -368,7 +431,7 @@ stream_write_uint32 (estream_t stream, u32 uint32)
 
   ret = es_write (stream, buffer, sizeof (buffer), NULL);
   if (ret)
 
   ret = es_write (stream, buffer, sizeof (buffer), NULL);
   if (ret)
-    err = gpg_error_from_errno (errno);
+    err = gpg_error_from_syserror ();
   else
     err = 0;
 
   else
     err = 0;
 
@@ -385,7 +448,7 @@ stream_read_data (estream_t stream, unsigned char *buffer, size_t size)
 
   ret = es_read (stream, buffer, size, &bytes_read);
   if (ret)
 
   ret = es_read (stream, buffer, size, &bytes_read);
   if (ret)
-    err = gpg_error_from_errno (errno);
+    err = gpg_error_from_syserror ();
   else
     {
       if (bytes_read != size)
   else
     {
       if (bytes_read != size)
@@ -406,7 +469,7 @@ stream_write_data (estream_t stream, const unsigned char *buffer, size_t size)
 
   ret = es_write (stream, buffer, size, NULL);
   if (ret)
 
   ret = es_write (stream, buffer, size, NULL);
   if (ret)
-    err = gpg_error_from_errno (errno);
+    err = gpg_error_from_syserror ();
   else
     err = 0;
 
   else
     err = 0;
 
@@ -414,17 +477,16 @@ 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
 }
 
 /* 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)
 {
   gpg_error_t err;
 static gpg_error_t
 stream_read_string (estream_t stream, unsigned int secure,
                    unsigned char **string, u32 *string_size)
 {
   gpg_error_t err;
-  unsigned char *buffer;
-  u32 length;
-
-  buffer = NULL;
+  unsigned char *buffer = NULL;
+  u32 length = 0;
 
   /* Read string length.  */
   err = stream_read_uint32 (stream, &length);
 
   /* Read string length.  */
   err = stream_read_uint32 (stream, &length);
@@ -438,7 +500,7 @@ stream_read_string (estream_t stream, unsigned int secure,
     buffer = xtrymalloc (length + 1);
   if (! buffer)
     {
     buffer = xtrymalloc (length + 1);
   if (! buffer)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
       goto out;
     }
 
@@ -471,7 +533,7 @@ stream_read_cstring (estream_t stream, char **string)
   err = stream_read_string (stream, 0, &buffer, NULL);
   if (err)
     goto out;
   err = stream_read_string (stream, 0, &buffer, NULL);
   if (err)
     goto out;
-  
+
   *string = (char *) buffer;
 
  out:
   *string = (char *) buffer;
 
  out:
@@ -508,7 +570,7 @@ stream_write_cstring (estream_t stream, const char *string)
                             (const unsigned char *) string, strlen (string));
 
   return err;
                             (const unsigned char *) string, strlen (string));
 
   return err;
-}                        
+}
 
 /* Read an MPI from STREAM, store it in MPINT.  Depending on SECURE
    use secure memory.  */
 
 /* Read an MPI from STREAM, store it in MPINT.  Depending on SECURE
    use secure memory.  */
@@ -526,6 +588,15 @@ stream_read_mpi (estream_t stream, unsigned int secure, gcry_mpi_t *mpint)
   if (err)
     goto out;
 
   if (err)
     goto out;
 
+  /* To avoid excessive use of secure memory we check that an MPI is
+     not too large. */
+  if (mpi_data_size > 520)
+    {
+      log_error (_("ssh keys greater than %d bits are not supported\n"), 4096);
+      err = GPG_ERR_TOO_LARGE;
+      goto out;
+    }
+
   err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_STD, mpi_data, mpi_data_size, NULL);
   if (err)
     goto out;
   err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_STD, mpi_data, mpi_data_size, NULL);
   if (err)
     goto out;
@@ -578,13 +649,13 @@ stream_copy (estream_t dst, estream_t src)
       if (ret || (! bytes_read))
        {
          if (ret)
       if (ret || (! bytes_read))
        {
          if (ret)
-           err = gpg_error_from_errno (errno);
+           err = gpg_error_from_syserror ();
          break;
        }
       ret = es_write (dst, buffer, bytes_read, NULL);
       if (ret)
        {
          break;
        }
       ret = es_write (dst, buffer, bytes_read, NULL);
       if (ret)
        {
-         err = gpg_error_from_errno (errno);
+         err = gpg_error_from_syserror ();
          break;
        }
     }
          break;
        }
     }
@@ -610,25 +681,25 @@ file_to_buffer (const char *filename, unsigned char **buffer, size_t *buffer_n)
 
   buffer_new = NULL;
   err = 0;
 
   buffer_new = NULL;
   err = 0;
-  
+
   stream = es_fopen (filename, "r");
   if (! stream)
     {
   stream = es_fopen (filename, "r");
   if (! stream)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
   ret = fstat (es_fileno (stream), &statbuf);
   if (ret)
     {
       goto out;
     }
 
   ret = fstat (es_fileno (stream), &statbuf);
   if (ret)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
   buffer_new = xtrymalloc (statbuf.st_size);
   if (! buffer_new)
     {
       goto out;
     }
 
   buffer_new = xtrymalloc (statbuf.st_size);
   if (! buffer_new)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
       goto out;
     }
 
@@ -653,160 +724,332 @@ file_to_buffer (const char *filename, unsigned char **buffer, size_t *buffer_n)
 
 
 \f
 
 
 \f
-/* 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,
    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
 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;
 {
   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.  */
 
   /* 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.  */
   /* 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)
     {
     {
-      /* 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 (cf->fname, "wx,mode=-rw-r");
+      if (!stream)
         {
         {
-          err = gpg_error (gpg_err_code_from_errno (errno));
-          log_error (_("can't create `%s': %s\n"), fname, gpg_strerror (err));
-          xfree (fname);
-          return err;
+          err = gpg_error_from_syserror ();
+          log_error (_("can't create '%s': %s\n"),
+                     cf->fname, gpg_strerror (err));
+          goto leave;
         }
         }
-      fputs (sshcontrolblurb, fp);
-      fclose (fp);
-      fp = fopen (fname, append? "a+":"r");
+      es_fputs (sshcontrolblurb, stream);
+      es_fclose (stream);
+      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;  
 
 
-  return 0;
+  err = 0;
+
+ leave:
+  if (err && cf)
+    {
+      if (cf->fp)
+        fclose (cf->fp);
+      xfree (cf->fname);
+      xfree (cf);
+    }
+  else
+    *r_cf = cf;
+
+  return err;
 }
 
 
 }
 
 
-/* Search the file at stream FP 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.  */
+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)
+    return;
+  fclose (cf->fp);
+  xfree (cf->fname);
+  xfree (cf);
+}
+
+
+
+/* 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
 static gpg_error_t
-search_control_file (FILE *fp, const char *hexgrip, int *disabled)
+read_control_file_item (control_file_t cf)
 {
 {
-  int c, i;
-  char *p, line[256];
-  
-  assert (strlen (hexgrip) == 40 );
+  int c, i, n;
+  char *p, *pend, line[256];
+  long ttl = 0;
+
+  cf->item.valid = 0;
+  clearerr (cf->fp);
 
 
-  rewind (fp);
-  *disabled = 0;
- next_line:
   do
     {
   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_EOF);
-          return gpg_error (gpg_err_code_from_errno (errno));
+          return gpg_error_from_syserror ();
         }
         }
-      
+      cf->lnr++;
+
       if (!*line || line[strlen(line)-1] != '\n')
         {
           /* Eat until end of line */
       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);
         }
             ;
           return gpg_error (*line? GPG_ERR_LINE_TOO_LONG
                                  : GPG_ERR_INCOMPLETE_LINE);
         }
-      
+
       /* Allow for empty lines and spaces */
       for (p=line; spacep (p); p++)
         ;
     }
   while (!*p || *p == '\n' || *p == '#');
       /* Allow for empty lines and spaces */
       for (p=line; spacep (p); p++)
         ;
     }
   while (!*p || *p == '\n' || *p == '#');
-  
-  *disabled = 0;
+
+  cf->item.disabled = 0;
   if (*p == '!')
     {
   if (*p == '!')
     {
-      *disabled = 1;
+      cf->item.disabled = 1;
       for (p++; spacep (p); p++)
         ;
     }
 
   for (i=0; hexdigitp (p) && i < 40; p++, i++)
       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'))
     {
   if (i != 40 || !(spacep (p) || *p == '\n'))
     {
-      log_error ("invalid formatted line in ssh control file\n");
+      log_error ("%s:%d: invalid formatted line\n", cf->fname, cf->lnr);
       return gpg_error (GPG_ERR_BAD_DATA);
     }
 
       return gpg_error (GPG_ERR_BAD_DATA);
     }
 
-  /* Fixme: Get TTL and flags.  */
+  ttl = strtol (p, &pend, 10);
+  p = pend;
+  if (!(spacep (p) || *p == '\n') || (int)ttl < -1)
+    {
+      log_error ("%s:%d: invalid TTL value; assuming 0\n", cf->fname, cf->lnr);
+      cf->item.ttl = 0;
+    }
+  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++)
+        ;
+      if (!*p || *p == '\n')
+        break;
+      n = strcspn (p, "= \t\n");
+      if (p[n] == '=')
+        {
+          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))
+        {
+          cf->item.confirm = 1;
+        }
+      else
+        log_error ("%s:%d: invalid flag '%.*s'; ignored\n",
+                   cf->fname, cf->lnr, n, p);
+      p += n;
+    }
+
+  /* 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":""); */
 
 
-  return 0; /* Okay:  found it.  */
+  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;
 }
 
 
 
 /* Add an entry to the control file to mark the key with the keygrip
    HEXGRIP as usable for SSH; i.e. it will be returned when ssh asks
 }
 
 
 
 /* Add an entry to the control file to mark the key with the keygrip
    HEXGRIP as usable for SSH; i.e. it will be returned when ssh asks
-   for it.  This function is in general used to add a key received
-   through the ssh-add function.  We can assume that the user wants to
-   allow ssh using this key. */
+   for it.  FMTFPR is the fingerprint string.  This function is in
+   general used to add a key received through the ssh-add function.
+   We can assume that the user wants to allow ssh using this key. */
 static gpg_error_t
 static gpg_error_t
-add_control_entry (ctrl_t ctrl, const char *hexgrip, int ttl)
+add_control_entry (ctrl_t ctrl, const char *hexgrip, const char *fmtfpr,
+                   int ttl, int confirm)
 {
   gpg_error_t err;
 {
   gpg_error_t err;
-  FILE *fp;
+  control_file_t cf;
   int disabled;
 
   int disabled;
 
-  err = open_control_file (&fp, 1);
+  (void)ctrl;
+
+  err = open_control_file (&cf, 1);
   if (err)
     return err;
 
   if (err)
     return err;
 
-  err = search_control_file (fp, hexgrip, &disabled);
+  err = search_control_file (cf, hexgrip, &disabled, NULL, NULL);
   if (err && gpg_err_code(err) == GPG_ERR_EOF)
     {
       struct tm *tp;
       time_t atime = time (NULL);
 
   if (err && gpg_err_code(err) == GPG_ERR_EOF)
     {
       struct tm *tp;
       time_t atime = time (NULL);
 
-      /* Not yet in the file - add it. Becuase the file has been
+      /* 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);
          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%s %d\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,
                1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
                tp->tm_hour, tp->tm_min, tp->tm_sec,
-               hexgrip, ttl);
-               
+               fmtfpr, hexgrip, ttl, confirm? " confirm":"");
+
     }
     }
-  fclose (fp);
+  close_control_file (cf);
   return 0;
 }
 
 
   return 0;
 }
 
 
+/* Scan the sshcontrol file and return the TTL.  */
+static int
+ttl_from_sshcontrol (const char *hexgrip)
+{
+  control_file_t cf;
+  int disabled, ttl;
+
+  if (!hexgrip || strlen (hexgrip) != 40)
+    return 0;  /* Wrong input: Use global default.  */
+
+  if (open_control_file (&cf, 0))
+    return 0; /* Error: Use the global default TTL.  */
+
+  if (search_control_file (cf, hexgrip, &disabled, &ttl, NULL)
+      || disabled)
+    ttl = 0;  /* Use the global default if not found or disabled.  */
+
+  close_control_file (cf);
+
+  return ttl;
+}
+
+
+/* Scan the sshcontrol file and return the confirm flag.  */
+static int
+confirm_flag_from_sshcontrol (const char *hexgrip)
+{
+  control_file_t cf;
+  int disabled, confirm;
+
+  if (!hexgrip || strlen (hexgrip) != 40)
+    return 1;  /* Wrong input: Better ask for confirmation.  */
+
+  if (open_control_file (&cf, 0))
+    return 1; /* Error: Better ask for confirmation.  */
+
+  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.  */
+
+  close_control_file (cf);
+
+  return confirm;
+}
+
+
 
 \f
 
 /*
 
 
 \f
 
 /*
 
-  MPI lists. 
+  MPI lists.
 
  */
 
 
  */
 
@@ -832,7 +1075,6 @@ 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)
 {
 ssh_receive_mpint_list (estream_t stream, int secret,
                        ssh_key_type_spec_t key_spec, gcry_mpi_t **mpi_list)
 {
-  unsigned int elems_public_n;
   const char *elems_public;
   unsigned int elems_n;
   const char *elems;
   const char *elems_public;
   unsigned int elems_n;
   const char *elems;
@@ -843,7 +1085,7 @@ ssh_receive_mpint_list (estream_t stream, int secret,
 
   mpis = NULL;
   err = 0;
 
   mpis = NULL;
   err = 0;
-  
+
   if (secret)
     elems = key_spec.elems_key_secret;
   else
   if (secret)
     elems = key_spec.elems_key_secret;
   else
@@ -851,16 +1093,13 @@ ssh_receive_mpint_list (estream_t stream, int secret,
   elems_n = strlen (elems);
 
   elems_public = key_spec.elems_key_public;
   elems_n = strlen (elems);
 
   elems_public = key_spec.elems_key_public;
-  elems_public_n = strlen (elems_public);
 
 
-  mpis = xtrymalloc (sizeof (*mpis) * (elems_n + 1));
-  if (! mpis)
+  mpis = xtrycalloc (elems_n + 1, sizeof *mpis );
+  if (!mpis)
     {
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
       goto out;
     }
-  
-  memset (mpis, 0, sizeof (*mpis) * (elems_n + 1));
 
   elem_is_secret = 0;
   for (i = 0; i < elems_n; i++)
 
   elem_is_secret = 0;
   for (i = 0; i < elems_n; i++)
@@ -922,13 +1161,16 @@ ssh_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis)
 
 /* Signature encoder function for RSA.  */
 static gpg_error_t
 
 /* 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;
 
 {
   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);
   s = mpis[0];
 
   err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &data, &data_n, s);
@@ -946,7 +1188,8 @@ ssh_signature_encoder_rsa (estream_t signature_blob, gcry_mpi_t *mpis)
 
 /* Signature encoder function for DSA.  */
 static gpg_error_t
 
 /* 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;
 {
   unsigned char buffer[SSH_DSA_SIGNATURE_PADDING * SSH_DSA_SIGNATURE_ELEMS];
   unsigned char *data;
@@ -954,8 +1197,12 @@ ssh_signature_encoder_dsa (estream_t signature_blob, gcry_mpi_t *mpis)
   gpg_error_t err;
   int i;
 
   gpg_error_t err;
   int i;
 
+  (void)spec;
+
   data = NULL;
 
   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]);
   for (i = 0; i < 2; i++)
     {
       err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &data, &data_n, mpis[i]);
@@ -967,7 +1214,7 @@ ssh_signature_encoder_dsa (estream_t signature_blob, gcry_mpi_t *mpis)
          err = gpg_error (GPG_ERR_INTERNAL); /* FIXME?  */
          break;
        }
          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)
       memset (buffer + (i * SSH_DSA_SIGNATURE_PADDING), 0,
              SSH_DSA_SIGNATURE_PADDING - data_n);
       memcpy (buffer + (i * SSH_DSA_SIGNATURE_PADDING)
@@ -988,74 +1235,107 @@ ssh_signature_encoder_dsa (estream_t signature_blob, gcry_mpi_t *mpis)
   return err;
 }
 
   return err;
 }
 
-/* 
-   S-Expressions. 
+
+/* 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;
+
+  (void)spec;
+
+  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
  */
 
 
 /* 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
 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,
                    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" };
 {
   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;
   gpg_error_t err;
+  gcry_sexp_t sexp_new = NULL;
+  void *formatbuf = NULL;
+  void **arg_list = NULL;
+  int arg_idx;
+  estream_t format;
   const char *elems;
   size_t elems_n;
   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);
 
   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_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
       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 + 1 + elems_n + 1 + 1));
+  if (!arg_list)
     {
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
       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;
+  if (curve_name)
+    {
+      es_fputs ("(curve%s)", format);
+      arg_list[arg_idx++] = &curve_name;
+    }
 
 
-  *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++)
     {
   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++)
       if (secret)
        {
          for (j = 0; j < elems_n; j++)
@@ -1064,58 +1344,66 @@ sexp_key_construct (gcry_sexp_t *sexp,
        }
       else
        j = i;
        }
       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);
+  err = gcry_sexp_build_array (&sexp_new, NULL, formatbuf, arg_list);
   if (err)
     goto out;
 
   if (err)
     goto out;
 
-  *sexp = sexp_new;
+  *r_sexp = sexp_new;
+  err = 0;
 
  out:
 
  out:
-
+  es_fclose (format);
   xfree (arg_list);
   xfree (arg_list);
-  xfree (sexp_template);
+  xfree (formatbuf);
 
   return err;
 }
 
 
   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
 /* 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,
 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;
   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;
   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;
   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)
 
   data = gcry_sexp_nth_data (sexp, 0, &data_n);
   if (! data)
@@ -1143,13 +1431,12 @@ sexp_key_extract (gcry_sexp_t sexp,
     }
 
   elems_n = strlen (elems);
     }
 
   elems_n = strlen (elems);
-  mpis_new = xtrymalloc (sizeof (*mpis_new) * (elems_n + 1));
-  if (! mpis_new)
+  mpis_new = xtrycalloc (elems_n + 1, sizeof *mpis_new );
+  if (!mpis_new)
     {
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
       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)
 
   value_list = gcry_sexp_find_token (sexp, key_spec.identifier, 0);
   if (! value_list)
@@ -1182,6 +1469,51 @@ sexp_key_extract (gcry_sexp_t sexp,
   if (err)
     goto out;
 
   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;
   /* We do not require a comment sublist to be present here.  */
   data = NULL;
   data_n = 0;
@@ -1198,7 +1530,7 @@ sexp_key_extract (gcry_sexp_t sexp,
   comment_new = make_cstring (data, data_n);
   if (! comment_new)
     {
   comment_new = make_cstring (data, data_n);
   if (! comment_new)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
       goto out;
     }
 
@@ -1206,15 +1538,17 @@ sexp_key_extract (gcry_sexp_t sexp,
     *secret = is_secret;
   *mpis = mpis_new;
   *comment = comment_new;
     *secret = is_secret;
   *mpis = mpis_new;
   *comment = comment_new;
+  *r_curve = curve_name;
 
  out:
 
   gcry_sexp_release (value_list);
   gcry_sexp_release (value_pair);
   gcry_sexp_release (comment_list);
 
  out:
 
   gcry_sexp_release (value_list);
   gcry_sexp_release (value_pair);
   gcry_sexp_release (comment_list);
-  
+
   if (err)
     {
   if (err)
     {
+      xfree (curve_name);
       xfree (comment_new);
       mpint_list_free (mpis_new);
     }
       xfree (comment_new);
       mpint_list_free (mpis_new);
     }
@@ -1222,7 +1556,7 @@ sexp_key_extract (gcry_sexp_t sexp,
   return err;
 }
 
   return err;
 }
 
-/* Extract the car from SEXP, and create a newly created C-string 
+/* Extract the car from SEXP, and create a newly created C-string
    which is to be stored in IDENTIFIER.  */
 static gpg_error_t
 sexp_extract_identifier (gcry_sexp_t sexp, char **identifier)
    which is to be stored in IDENTIFIER.  */
 static gpg_error_t
 sexp_extract_identifier (gcry_sexp_t sexp, char **identifier)
@@ -1235,7 +1569,7 @@ sexp_extract_identifier (gcry_sexp_t sexp, char **identifier)
 
   identifier_new = NULL;
   err = 0;
 
   identifier_new = NULL;
   err = 0;
-  
+
   sublist = gcry_sexp_nth (sexp, 1);
   if (! sublist)
     {
   sublist = gcry_sexp_nth (sexp, 1);
   if (! sublist)
     {
@@ -1289,7 +1623,7 @@ ssh_key_type_lookup (const char *ssh_name, const char *name,
     if ((ssh_name && (! strcmp (ssh_name, ssh_key_types[i].ssh_identifier)))
        || (name && (! strcmp (name, ssh_key_types[i].identifier))))
       break;
     if ((ssh_name && (! strcmp (ssh_name, ssh_key_types[i].ssh_identifier)))
        || (name && (! strcmp (name, ssh_key_types[i].identifier))))
       break;
-  
+
   if (i == DIM (ssh_key_types))
     err = gpg_error (GPG_ERR_NOT_FOUND);
   else
   if (i == DIM (ssh_key_types))
     err = gpg_error (GPG_ERR_NOT_FOUND);
   else
@@ -1301,6 +1635,24 @@ ssh_key_type_lookup (const char *ssh_name, const char *name,
   return err;
 }
 
   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.
 /* 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.
@@ -1311,18 +1663,15 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
                  int read_comment, ssh_key_type_spec_t *key_spec)
 {
   gpg_error_t err;
                  int read_comment, ssh_key_type_spec_t *key_spec)
 {
   gpg_error_t err;
-  char *key_type;
-  char *comment;
-  gcry_sexp_t key;
+  char *key_type = NULL;
+  char *comment = NULL;
+  gcry_sexp_t key = NULL;
   ssh_key_type_spec_t spec;
   ssh_key_type_spec_t spec;
-  gcry_mpi_t *mpi_list;
+  gcry_mpi_t *mpi_list = NULL;
   const char *elems;
   const char *elems;
+  char *curve_name = NULL;
+
 
 
-  mpi_list = NULL;
-  key_type = NULL;
-  comment = "";
-  key = NULL;
-       
   err = stream_read_cstring (stream, &key_type);
   if (err)
     goto out;
   err = stream_read_cstring (stream, &key_type);
   if (err)
     goto out;
@@ -1331,6 +1680,50 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
   if (err)
     goto out;
 
   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;
   err = ssh_receive_mpint_list (stream, secret, spec, &mpi_list);
   if (err)
     goto out;
@@ -1354,20 +1747,20 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
        goto out;
     }
 
        goto out;
     }
 
-  err = sexp_key_construct (&key, spec, secret, mpi_list, comment);
+  err = sexp_key_construct (&key, spec, secret, curve_name, mpi_list,
+                            comment? comment:"");
   if (err)
     goto out;
 
   if (key_spec)
     *key_spec = spec;
   *key_new = key;
   if (err)
     goto out;
 
   if (key_spec)
     *key_spec = spec;
   *key_new = key;
-  
- out:
 
 
+ out:
   mpint_list_free (mpi_list);
   mpint_list_free (mpi_list);
+  xfree (curve_name);
   xfree (key_type);
   xfree (key_type);
-  if (read_comment)
-    xfree (comment);
+  xfree (comment);
 
   return err;
 }
 
   return err;
 }
@@ -1377,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,
    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;
 {
   unsigned char *blob_new;
   long int blob_size_new;
@@ -1395,26 +1789,43 @@ ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
   stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
   if (! stream)
     {
   stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
   if (! stream)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
       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)
     {
 
   blob_size_new = es_ftell (stream);
   if (blob_size_new == -1)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
       goto out;
     }
-  
+
   err = es_fseek (stream, 0, SEEK_SET);
   if (err)
     goto out;
   err = es_fseek (stream, 0, SEEK_SET);
   if (err)
     goto out;
@@ -1422,7 +1833,7 @@ ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
   blob_new = xtrymalloc (blob_size_new);
   if (! blob_new)
     {
   blob_new = xtrymalloc (blob_size_new);
   if (! blob_new)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
       goto out;
     }
 
@@ -1442,28 +1853,25 @@ ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
 
   return err;
 }
 
   return err;
 }
-                             
+
 
 /* Write the public key KEY_PUBLIC to STREAM in SSH key format.  If
    OVERRIDE_COMMENT is not NULL, it will be used instead of the
    comment stored in the key.  */
 static gpg_error_t
 
 /* Write the public key KEY_PUBLIC to STREAM in SSH key format.  If
    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;
                      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;
 
   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;
   err = sexp_extract_identifier (key_public, &key_type);
   if (err)
     goto out;
@@ -1472,27 +1880,27 @@ ssh_send_key_public (estream_t stream, gcry_sexp_t key_public,
   if (err)
     goto out;
 
   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;
 
   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;
   if (err)
     goto out;
-  
+
   err = stream_write_string (stream, blob, blob_n);
   if (err)
     goto out;
 
   err = stream_write_cstring (stream,
                               override_comment? override_comment : comment);
   err = stream_write_string (stream, blob, blob_n);
   if (err)
     goto out;
 
   err = stream_write_cstring (stream,
                               override_comment? override_comment : comment);
-  
+
  out:
 
   mpint_list_free (mpi_list);
  out:
 
   mpint_list_free (mpi_list);
-  xfree (key_type);
+  xfree (curve);
   xfree (comment);
   xfree (comment);
+  xfree (key_type);
   xfree (blob);
 
   return err;
   xfree (blob);
 
   return err;
@@ -1510,11 +1918,11 @@ ssh_read_key_public_from_blob (unsigned char *blob, size_t blob_size,
   gpg_error_t err;
 
   err = 0;
   gpg_error_t err;
 
   err = 0;
-  
+
   blob_stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
   if (! blob_stream)
     {
   blob_stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
   if (! blob_stream)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
       goto out;
     }
 
@@ -1545,11 +1953,15 @@ static gpg_error_t
 ssh_key_grip (gcry_sexp_t key, unsigned char *buffer)
 {
   if (!gcry_pk_get_keygrip (key, buffer))
 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;
 }
 
 
   return 0;
 }
 
+
 /* Converts the secret key KEY_SECRET into a public key, storing it in
    KEY_PUBLIC.  SPEC is the according key specification.  Returns zero
    on success or an error code.  */
 /* Converts the secret key KEY_SECRET into a public key, storing it in
    KEY_PUBLIC.  SPEC is the according key specification.  Returns zero
    on success or an error code.  */
@@ -1557,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)
 {
 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;
   char *comment;
   gcry_mpi_t *mpis;
   gpg_error_t err;
@@ -1565,16 +1978,18 @@ key_secret_to_public (gcry_sexp_t *key_public,
   comment = NULL;
   mpis = NULL;
 
   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;
 
   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);
 
  out:
 
   mpint_list_free (mpis);
   xfree (comment);
+  xfree (curve);
 
   return err;
 }
 
   return err;
 }
@@ -1619,7 +2034,7 @@ card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn)
     }
   if (err)
     {
     }
   if (err)
     {
-      log_error (_("error getting default authentication keyID of card: %s\n"),
+      log_error (_("no authentication key for ssh on card: %s\n"),
                  gpg_strerror (err));
       xfree (serialno);
       return err;
                  gpg_strerror (err));
       xfree (serialno);
       return err;
@@ -1674,11 +2089,11 @@ card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn)
       /* (Shadow)-key is not available in our key storage.  */
       unsigned char *shadow_info;
       unsigned char *tmp;
       /* (Shadow)-key is not available in our key storage.  */
       unsigned char *shadow_info;
       unsigned char *tmp;
-      
+
       shadow_info = make_shadow_info (serialno, authkeyid);
       if (!shadow_info)
         {
       shadow_info = make_shadow_info (serialno, authkeyid);
       if (!shadow_info)
         {
-          err = gpg_error_from_errno (errno);
+          err = gpg_error_from_syserror ();
           xfree (pkbuf);
           gcry_sexp_release (s_pk);
           xfree (serialno);
           xfree (pkbuf);
           gcry_sexp_release (s_pk);
           xfree (serialno);
@@ -1728,7 +2143,7 @@ card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn)
         *cardsn = xtryasprintf ("cardno:%s", serialno);
       if (!*cardsn)
         {
         *cardsn = xtryasprintf ("cardno:%s", serialno);
       if (!*cardsn)
         {
-          err = gpg_error_from_errno (errno);
+          err = gpg_error_from_syserror ();
           xfree (pkbuf);
           gcry_sexp_release (s_pk);
           xfree (serialno);
           xfree (pkbuf);
           gcry_sexp_release (s_pk);
           xfree (serialno);
@@ -1761,77 +2176,41 @@ static gpg_error_t
 ssh_handler_request_identities (ctrl_t ctrl,
                                 estream_t request, estream_t response)
 {
 ssh_handler_request_identities (ctrl_t ctrl,
                                 estream_t request, estream_t response)
 {
-  char *key_type;
   ssh_key_type_spec_t spec;
   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;
   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;
   gpg_error_t err;
   int ret;
-  FILE *ctrl_fp = NULL;
+  control_file_t cf = NULL;
   char *cardsn;
   gpg_error_t ret_err;
 
   char *cardsn;
   gpg_error_t ret_err;
 
+  (void)request;
+
   /* Prepare buffer stream.  */
 
   /* Prepare buffer stream.  */
 
-  key_directory = NULL;
   key_secret = NULL;
   key_public = NULL;
   key_secret = NULL;
   key_public = NULL;
-  key_type = NULL;
-  key_path = NULL;
   key_counter = 0;
   key_counter = 0;
-  buffer = NULL;
-  dir = NULL;
   err = 0;
 
   key_blobs = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
   if (! key_blobs)
     {
   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);
-  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);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
       goto out;
     }
 
-
-
   /* First check whether a key is currently available in the card
      reader - this should be allowed even without being listed in
      sshcontrol. */
 
   /* First check whether a key is currently available in the card
      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);
     {
       err = ssh_send_key_public (key_blobs, key_public, cardsn);
       gcry_sexp_release (key_public);
@@ -1839,133 +2218,133 @@ ssh_handler_request_identities (ctrl_t ctrl,
       xfree (cardsn);
       if (err)
         goto out;
       xfree (cardsn);
       if (err)
         goto out;
-      
+
       key_counter++;
     }
 
 
       key_counter++;
     }
 
 
-  /* 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;
+      }
 
 
+    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);
+  }
 
 
-  /* 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. */
-  /* FIXME: make sure that buffer gets deallocated properly.  */
-  err = open_control_file (&ctrl_fp, 0);
+  /* Then look at all the registered and non-disabled keys. */
+  err = open_control_file (&cf, 0);
   if (err)
     goto out;
 
   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 (ctrl_fp, hexgrip, &disabled)
-              || disabled)
-            continue;
+      if (!cf->item.valid)
+        continue; /* Should not happen.  */
+      if (cf->item.disabled)
+        continue;
+      assert (strlen (cf->item.hexgrip) == 40);
+
+      stpcpy (stpcpy (fnameptr, cf->item.hexgrip), ".key");
 
 
-          strncpy (key_path + key_directory_n + 1, dir_entry->d_name, 40);
+      /* Read file content.  */
+      {
+        unsigned char *buffer;
+        size_t buffer_n;
 
 
-          /* Read file content.  */
-          err = file_to_buffer (key_path, &buffer, &buffer_n);
-          if (err)
-            goto out;
-             
-          err = gcry_sexp_sscan (&key_secret, NULL, (char*)buffer, buffer_n);
-          if (err)
-            goto out;
+        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;
-             
-          err = ssh_send_key_public (key_blobs, key_public, NULL);
-          if (err)
-            goto out;
+      gcry_sexp_release (key_secret);
+      key_secret = NULL;
 
 
-          gcry_sexp_release (key_public);
-          key_public = NULL;
+      err = ssh_send_key_public (key_blobs, key_public, NULL);
+      if (err)
+        goto out;
 
 
-          key_counter++;
-        }
+      gcry_sexp_release (key_public);
+      key_public = NULL;
+
+      key_counter++;
     }
     }
-  
+  err = 0;
+
   ret = es_fseek (key_blobs, 0, SEEK_SET);
   if (ret)
     {
   ret = es_fseek (key_blobs, 0, SEEK_SET);
   if (ret)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
  out:
       goto out;
     }
 
  out:
-
   /* Send response.  */
 
   gcry_sexp_release (key_secret);
   gcry_sexp_release (key_public);
 
   /* 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);
     {
       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);
     }
   else
     {
       ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
-      goto leave;
-    };
-
- leave:
-
-  if (key_blobs)
-    es_fclose (key_blobs);
-  if (dir)
-    closedir (dir);
-
-  if (ctrl_fp)
-    fclose (ctrl_fp);
+    }
 
 
-  free (key_directory);
-  xfree (key_path);
-  xfree (buffer);
-  xfree (key_type);
+  es_fclose (key_blobs);
+  close_control_file (cf);
+  xfree (key_fname);
 
   return ret_err;
 }
 
 
   return ret_err;
 }
 
+
 /* This function hashes the data contained in DATA of size DATA_N
    according to the message digest algorithm specified by MD_ALGORITHM
    and writes the message digest to HASH, which needs to large enough
 /* This function hashes the data contained in DATA of size DATA_N
    according to the message digest algorithm specified by MD_ALGORITHM
    and writes the message digest to HASH, which needs to large enough
@@ -1983,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
    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;
           unsigned char **sig, size_t *sig_n)
 {
   gpg_error_t err;
@@ -1994,24 +2373,66 @@ 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;
   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;
   size_t elems_n;
   gcry_mpi_t *mpis = NULL;
   int ret;
   unsigned int i;
   const char *elems;
   size_t elems_n;
   gcry_mpi_t *mpis = NULL;
+  char hexgrip[40+1];
 
   *sig = NULL;
   *sig_n = 0;
 
 
   *sig = NULL;
   *sig_n = 0;
 
+  /* Quick check to see whether we have a valid keygrip and convert it
+     to hex.  */
+  if (!ctrl->have_keygrip)
+    {
+      err = gpg_error (GPG_ERR_NO_SECKEY);
+      goto out;
+    }
+  bin2hex (ctrl->keygrip, 20, hexgrip);
+
+  /* Ask for confirmation if needed.  */
+  if (confirm_flag_from_sshcontrol (hexgrip))
+    {
+      gcry_sexp_t key;
+      char *fpr, *prompt;
+      char *comment = NULL;
+
+      err = agent_raw_key_from_file (ctrl, ctrl->keygrip, &key);
+      if (err)
+        goto out;
+      err = ssh_get_fingerprint_string (key, &fpr);
+      if (!err)
+        {
+          gcry_sexp_t tmpsxp = gcry_sexp_find_token (key, "comment", 0);
+          if (tmpsxp)
+            comment = gcry_sexp_nth_string (tmpsxp, 1);
+          gcry_sexp_release (tmpsxp);
+        }
+      gcry_sexp_release (key);
+      if (err)
+        goto out;
+      prompt = xtryasprintf (_("An ssh process requested the use of key%%0A"
+                               "  %s%%0A"
+                               "  (%s)%%0A"
+                               "Do you want to allow this?"),
+                             fpr, comment? comment:"");
+      xfree (fpr);
+      gcry_free (comment);
+      err = agent_get_confirmation (ctrl, prompt, _("Allow"), _("Deny"), 0);
+      xfree (prompt);
+      if (err)
+        goto out;
+    }
+
+  /* Create signature.  */
   ctrl->use_auth_call = 1;
   ctrl->use_auth_call = 1;
-  err = agent_pksign_do (ctrl,
+  err = agent_pksign_do (ctrl, NULL,
                          _("Please enter the passphrase "
                          _("Please enter the passphrase "
-                           "for the ssh key%0A  %c"), &signature_sexp,
-                         CACHE_MODE_SSH);
+                           "for the ssh key%%0A  %F%%0A  (%c)"),
+                         &signature_sexp,
+                         CACHE_MODE_SSH, ttl_from_sshcontrol);
   ctrl->use_auth_call = 0;
   if (err)
     goto out;
   ctrl->use_auth_call = 0;
   if (err)
     goto out;
@@ -2026,46 +2447,27 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
   stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
   if (! stream)
     {
   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);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
       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;
 
   if (err)
     goto out;
 
-  elems = spec.elems_signature;
+  elems = spec->elems_signature;
   elems_n = strlen (elems);
 
   elems_n = strlen (elems);
 
-  mpis = xtrymalloc (sizeof (*mpis) * (elems_n + 1));
-  if (! mpis)
+  mpis = xtrycalloc (elems_n + 1, sizeof *mpis);
+  if (!mpis)
     {
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
       goto out;
     }
-  memset (mpis, 0, sizeof (*mpis) * (elems_n + 1));
 
   for (i = 0; i < elems_n; i++)
     {
 
   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);
       if (! sublist)
        {
          err = gpg_error (GPG_ERR_INV_SEXP);
@@ -2086,38 +2488,38 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
   if (err)
     goto out;
 
   if (err)
     goto out;
 
-  err = (*sig_encoder) (stream, mpis);
+  err = spec->signature_encoder (spec, stream, mpis);
   if (err)
     goto out;
 
   sig_blob_n = es_ftell (stream);
   if (sig_blob_n == -1)
     {
   if (err)
     goto out;
 
   sig_blob_n = es_ftell (stream);
   if (sig_blob_n == -1)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
   sig_blob = xtrymalloc (sig_blob_n);
   if (! sig_blob)
     {
       goto out;
     }
 
   sig_blob = xtrymalloc (sig_blob_n);
   if (! sig_blob)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
   ret = es_fseek (stream, 0, SEEK_SET);
   if (ret)
     {
       goto out;
     }
 
   ret = es_fseek (stream, 0, SEEK_SET);
   if (ret)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
       goto out;
-    }    
+    }
 
   err = stream_read_data (stream, sig_blob, sig_blob_n);
   if (err)
     goto out;
 
   err = stream_read_data (stream, sig_blob, sig_blob_n);
   if (err)
     goto out;
-  
+
   *sig = sig_blob;
   *sig_n = sig_blob_n;
   *sig = sig_blob;
   *sig_n = sig_blob_n;
-  
+
  out:
 
   if (err)
  out:
 
   if (err)
@@ -2129,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);
   gcry_sexp_release (signature_sexp);
   gcry_sexp_release (sublist);
   mpint_list_free (mpis);
-  xfree (identifier);
 
   return err;
 }
 
   return err;
 }
@@ -2152,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;
   u32 flags;
   gpg_error_t err;
   gpg_error_t ret_err;
+  int hash_algo;
 
   key_blob = NULL;
   data = NULL;
 
   key_blob = NULL;
   data = NULL;
@@ -2159,7 +2561,7 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
   key = NULL;
 
   /* Receive key.  */
   key = NULL;
 
   /* Receive key.  */
-  
+
   err = stream_read_string (request, 0, &key_blob, &key_blob_size);
   if (err)
     goto out;
   err = stream_read_string (request, 0, &key_blob, &key_blob_size);
   if (err)
     goto out;
@@ -2178,14 +2580,18 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
   if (err)
     goto out;
 
   if (err)
     goto out;
 
+  hash_algo = spec.hash_algo;
+  if (!hash_algo)
+    hash_algo = GCRY_MD_SHA1;  /* Use the default.  */
+
   /* Hash data.  */
   /* 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;
     }
   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;
 
   if (err)
     goto out;
 
@@ -2196,15 +2602,18 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
 
   /* Sign data.  */
 
 
   /* 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;
   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);
 
   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:
 
   /* Done.  */
  out:
 
   /* Done.  */
@@ -2224,7 +2633,7 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
       if (ret_err)
        goto leave;
     }
       if (ret_err)
        goto leave;
     }
-  
+
  leave:
 
   gcry_sexp_release (key);
  leave:
 
   gcry_sexp_release (key);
@@ -2253,7 +2662,7 @@ ssh_key_extract_comment (gcry_sexp_t key, char **comment)
       err = gpg_error (GPG_ERR_INV_SEXP);
       goto out;
     }
       err = gpg_error (GPG_ERR_INV_SEXP);
       goto out;
     }
-  
+
   data = gcry_sexp_nth_data (comment_list, 1, &data_n);
   if (! data)
     {
   data = gcry_sexp_nth_data (comment_list, 1, &data_n);
   if (! data)
     {
@@ -2264,7 +2673,7 @@ ssh_key_extract_comment (gcry_sexp_t key, char **comment)
   comment_new = make_cstring (data, data_n);
   if (! comment_new)
     {
   comment_new = make_cstring (data, data_n);
   if (! comment_new)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
       goto out;
     }
 
@@ -2294,14 +2703,14 @@ ssh_key_to_protected_buffer (gcry_sexp_t key, const char *passphrase,
   buffer_new = xtrymalloc_secure (buffer_new_n);
   if (! buffer_new)
     {
   buffer_new = xtrymalloc_secure (buffer_new_n);
   if (! buffer_new)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
       goto out;
     }
-  
+
   gcry_sexp_sprint (key, GCRYSEXP_FMT_CANON, buffer_new, buffer_new_n);
   /* FIXME: guarantee?  */
 
   gcry_sexp_sprint (key, GCRYSEXP_FMT_CANON, buffer_new, buffer_new_n);
   /* FIXME: guarantee?  */
 
-  err = agent_protect (buffer_new, passphrase, buffer, buffer_n);
+  err = agent_protect (buffer_new, passphrase, buffer, buffer_n, 0);
 
  out:
 
 
  out:
 
@@ -2312,23 +2721,39 @@ ssh_key_to_protected_buffer (gcry_sexp_t key, const char *passphrase,
 
 
 
 
 
 
+/* Callback function to compare the first entered PIN with the one
+   currently being entered. */
+static int
+reenter_compare_cb (struct pin_entry_info_s *pi)
+{
+  const char *pin1 = pi->check_cb_arg;
+
+  if (!strcmp (pin1, pi->pin))
+    return 0; /* okay */
+  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
    our key storage, don't do anything.  When entering a new key also
    add an entry to the sshcontrol file.  */
 static gpg_error_t
 /* 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
    our key storage, don't do anything.  When entering a new key also
    add an entry to the sshcontrol file.  */
 static gpg_error_t
-ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
+ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl, int confirm)
 {
   gpg_error_t err;
   unsigned char key_grip_raw[20];
   char key_grip[41];
   unsigned char *buffer = NULL;
 {
   gpg_error_t err;
   unsigned char key_grip_raw[20];
   char key_grip[41];
   unsigned char *buffer = NULL;
-  unsigned int buffer_n;
+  size_t buffer_n;
   char *description = NULL;
   char *description = NULL;
+  const char *description2 = _("Please re-enter this passphrase");
   char *comment = NULL;
   char *comment = NULL;
+  char *key_fpr = NULL;
+  const char *initial_errtext = NULL;
   unsigned int i;
   unsigned int i;
-  struct pin_entry_info_s *pi = NULL;
+  struct pin_entry_info_s *pi = NULL, *pi2;
 
   err = ssh_key_grip (key, key_grip_raw);
   if (err)
 
   err = ssh_key_grip (key, key_grip_raw);
   if (err)
@@ -2339,7 +2764,10 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
   if ( !agent_key_available (key_grip_raw) )
     goto out; /* Yes, key is available.  */
 
   if ( !agent_key_available (key_grip_raw) )
     goto out; /* Yes, key is available.  */
 
-  
+  err = ssh_get_fingerprint_string (key, &key_fpr);
+  if (err)
+    goto out;
+
   err = ssh_key_extract_comment (key, &comment);
   if (err)
     goto out;
   err = ssh_key_extract_comment (key, &comment);
   if (err)
     goto out;
@@ -2348,26 +2776,46 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
                  _("Please enter a passphrase to protect"
                    " the received secret key%%0A"
                    "   %s%%0A"
                  _("Please enter a passphrase to protect"
                    " the received secret key%%0A"
                    "   %s%%0A"
+                   "   %s%%0A"
                    "within gpg-agent's key storage"),
                    "within gpg-agent's key storage"),
-                 comment ? comment : "?") < 0)
+                 key_fpr, comment ? comment : "") < 0)
     {
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
       goto out;
     }
 
-
-  pi = gcry_calloc_secure (1, sizeof (*pi) + 100 + 1);
+  pi = gcry_calloc_secure (2, sizeof (*pi) + 100 + 1);
   if (!pi)
     {
   if (!pi)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
       goto out;
     }
+  pi2 = pi + (sizeof *pi + 100 + 1);
   pi->max_length = 100;
   pi->max_tries = 1;
   pi->max_length = 100;
   pi->max_tries = 1;
-  err = agent_askpin (ctrl, description, NULL, NULL, pi);
+  pi2->max_length = 100;
+  pi2->max_tries = 1;
+  pi2->check_cb = reenter_compare_cb;
+  pi2->check_cb_arg = pi->pin;
+
+ next_try:
+  err = agent_askpin (ctrl, description, NULL, initial_errtext, pi);
+  initial_errtext = NULL;
   if (err)
     goto out;
 
   if (err)
     goto out;
 
+  /* Unless the passphrase is empty, ask to confirm it.  */
+  if (pi->pin && *pi->pin)
+    {
+      err = agent_askpin (ctrl, description2, NULL, NULL, pi2);
+      if (err == -1)
+       { /* The re-entered one did not match and the user did not
+            hit cancel. */
+         initial_errtext = _("does not match - try again");
+         goto next_try;
+       }
+    }
+
   err = ssh_key_to_protected_buffer (key, pi->pin, &buffer, &buffer_n);
   if (err)
     goto out;
   err = ssh_key_to_protected_buffer (key, pi->pin, &buffer, &buffer_n);
   if (err)
     goto out;
@@ -2386,7 +2834,7 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
     goto out;
 
   /* And add an entry to the sshcontrol file.  */
     goto out;
 
   /* And add an entry to the sshcontrol file.  */
-  err = add_control_entry (ctrl, key_grip, ttl);
+  err = add_control_entry (ctrl, key_grip, key_fpr, ttl, confirm);
 
 
  out:
 
 
  out:
@@ -2395,7 +2843,8 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
   xfree (pi);
   xfree (buffer);
   xfree (comment);
   xfree (pi);
   xfree (buffer);
   xfree (comment);
-  free (description); /* (asprintf allocated, thus regular free.)  */
+  xfree (key_fpr);
+  xfree (description);
 
   return err;
 }
 
   return err;
 }
@@ -2434,7 +2883,7 @@ ssh_handler_add_identity (ctrl_t ctrl, estream_t request, estream_t response)
   unsigned char b;
   int confirm;
   int ttl;
   unsigned char b;
   int confirm;
   int ttl;
-  
+
   confirm = 0;
   key = NULL;
   ttl = 0;
   confirm = 0;
   key = NULL;
   ttl = 0;
@@ -2479,9 +2928,7 @@ ssh_handler_add_identity (ctrl_t ctrl, estream_t request, estream_t response)
   if (err)
     goto out;
 
   if (err)
     goto out;
 
-  /* FIXME: are constraints used correctly?  */
-
-  err = ssh_identity_register (ctrl, key, ttl);
+  err = ssh_identity_register (ctrl, key, ttl, confirm);
 
  out:
 
 
  out:
 
@@ -2506,11 +2953,13 @@ ssh_handler_remove_identity (ctrl_t ctrl,
   gpg_error_t ret_err;
   gpg_error_t err;
 
   gpg_error_t ret_err;
   gpg_error_t err;
 
+  (void)ctrl;
+
   /* Receive key.  */
 
   key_blob = NULL;
   key = NULL;
   /* Receive key.  */
 
   key_blob = NULL;
   key = NULL;
-  
+
   err = stream_read_string (request, 0, &key_blob, &key_blob_size);
   if (err)
     goto out;
   err = stream_read_string (request, 0, &key_blob, &key_blob_size);
   if (err)
     goto out;
@@ -2518,7 +2967,7 @@ ssh_handler_remove_identity (ctrl_t ctrl,
   err = ssh_read_key_public_from_blob (key_blob, key_blob_size, &key, NULL);
   if (err)
     goto out;
   err = ssh_read_key_public_from_blob (key_blob, key_blob_size, &key, NULL);
   if (err)
     goto out;
-  
+
   err = ssh_identity_drop (key);
 
  out:
   err = ssh_identity_drop (key);
 
  out:
@@ -2544,7 +2993,7 @@ ssh_identities_remove_all (void)
 
   /* FIXME: shall we remove _all_ cache entries or only those
      registered through the ssh emulation?  */
 
   /* FIXME: shall we remove _all_ cache entries or only those
      registered through the ssh emulation?  */
-  
+
   return err;
 }
 
   return err;
 }
 
@@ -2555,7 +3004,10 @@ ssh_handler_remove_all_identities (ctrl_t ctrl,
 {
   gpg_error_t ret_err;
   gpg_error_t err;
 {
   gpg_error_t ret_err;
   gpg_error_t err;
-  
+
+  (void)ctrl;
+  (void)request;
+
   err = ssh_identities_remove_all ();
 
   if (! err)
   err = ssh_identities_remove_all ();
 
   if (! err)
@@ -2597,7 +3049,10 @@ ssh_handler_lock (ctrl_t ctrl, estream_t request, estream_t response)
 {
   gpg_error_t ret_err;
   gpg_error_t err;
 {
   gpg_error_t ret_err;
   gpg_error_t err;
-  
+
+  (void)ctrl;
+  (void)request;
+
   err = ssh_lock ();
 
   if (! err)
   err = ssh_lock ();
 
   if (! err)
@@ -2614,7 +3069,10 @@ ssh_handler_unlock (ctrl_t ctrl, estream_t request, estream_t response)
 {
   gpg_error_t ret_err;
   gpg_error_t err;
 {
   gpg_error_t ret_err;
   gpg_error_t err;
-  
+
+  (void)ctrl;
+  (void)request;
+
   err = ssh_unlock ();
 
   if (! err)
   err = ssh_unlock ();
 
   if (! err)
@@ -2641,7 +3099,8 @@ request_spec_lookup (int type)
       break;
   if (i == DIM (request_specs))
     {
       break;
   if (i == DIM (request_specs))
     {
-      log_info ("ssh request %u is not supported\n", type);
+      if (opt.verbose)
+        log_info ("ssh request %u is not supported\n", type);
       spec = NULL;
     }
   else
       spec = NULL;
     }
   else
@@ -2675,12 +3134,15 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
   /* 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
   /* 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. 
+     secure memory, since we never give out secret keys.
+
+     Note: we only have little secure memory, but there is NO
+     possibility of DoS here; only trusted clients are allowed to
+     connect to the agent.  What could happen is that the agent
+     returns out-of-secure-memory errors on requests in case the
+     agent's owner floods his own agent with many large messages.
+     -moritz */
 
 
-     FIXME: This is a pretty good DoS.  We only have a limited amount
-     of secure memory, we can't throw in everything we get from a
-     client -wk */
-      
   /* Retrieve request.  */
   err = stream_read_string (stream_sock, 1, &request_data, &request_data_size);
   if (err)
   /* Retrieve request.  */
   err = stream_read_string (stream_sock, 1, &request_data, &request_data_size);
   if (err)
@@ -2712,13 +3174,13 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
     request = es_mopen (NULL, 0, 0, 1, gcry_realloc, gcry_free, "r+");
   if (! request)
     {
     request = es_mopen (NULL, 0, 0, 1, gcry_realloc, gcry_free, "r+");
   if (! request)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
   ret = es_setvbuf (request, NULL, _IONBF, 0);
   if (ret)
     {
       goto out;
     }
   ret = es_setvbuf (request, NULL, _IONBF, 0);
   if (ret)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
   err = stream_write_data (request, request_data + 1, request_data_size - 1);
       goto out;
     }
   err = stream_write_data (request, request_data + 1, request_data_size - 1);
@@ -2729,7 +3191,7 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
   response = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
   if (! response)
     {
   response = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
   if (! response)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
       goto out;
     }
 
@@ -2813,39 +3275,48 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
 
 /* Start serving client on SOCK_CLIENT.  */
 void
 
 /* Start serving client on SOCK_CLIENT.  */
 void
-start_command_handler_ssh (int sock_client)
+start_command_handler_ssh (ctrl_t ctrl, gnupg_fd_t sock_client)
 {
 {
-  struct server_control_s ctrl;
-  estream_t stream_sock;
-  gpg_error_t err;
+  estream_t stream_sock = NULL;
+  gpg_error_t err = 0;
   int ret;
 
   int ret;
 
-  /* Setup control structure.  */
-
-  memset (&ctrl, 0, sizeof (ctrl));
-  agent_init_default_ctrl (&ctrl);
-  ctrl.connection_fd = sock_client;
-
   /* Because the ssh protocol does not send us information about the
      the current TTY setting, we resort here to use those from startup
      or those explictly set.  */
   /* Because the ssh protocol does not send us information about the
      the current TTY setting, we resort here to use those from startup
      or those explictly set.  */
-  if (!ctrl.display && opt.startup_display)
-    ctrl.display = strdup (opt.startup_display);
-  if (!ctrl.ttyname && opt.startup_ttyname)
-    ctrl.ttyname = strdup (opt.startup_ttyname);
-  if (!ctrl.ttytype && opt.startup_ttytype)
-    ctrl.ttytype = strdup (opt.startup_ttytype);
-  if (!ctrl.lc_ctype && opt.startup_lc_ctype)
-    ctrl.lc_ctype = strdup (opt.startup_lc_ctype);
-  if (!ctrl.lc_messages && opt.startup_lc_messages)
-    ctrl.lc_messages = strdup (opt.startup_lc_messages);
+  {
+    static const char *names[] =
+      {"GPG_TTY", "DISPLAY", "TERM", "XAUTHORITY", "PINENTRY_USER_DATA", NULL};
+    int idx;
+    const char *value;
+
+    for (idx=0; !err && names[idx]; idx++)
+      if (!session_env_getenv (ctrl->session_env, names[idx])
+          && (value = session_env_getenv (opt.startup_env, names[idx])))
+        err = session_env_setenv (ctrl->session_env, names[idx], value);
+
+    if (!err && !ctrl->lc_ctype && opt.startup_lc_ctype)
+      if (!(ctrl->lc_ctype = xtrystrdup (opt.startup_lc_ctype)))
+        err = gpg_error_from_syserror ();
+
+    if (!err && !ctrl->lc_messages && opt.startup_lc_messages)
+      if (!(ctrl->lc_messages = xtrystrdup (opt.startup_lc_messages)))
+        err = gpg_error_from_syserror ();
+
+    if (err)
+      {
+        log_error ("error setting default session environment: %s\n",
+                   gpg_strerror (err));
+        goto out;
+      }
+  }
 
 
   /* Create stream from socket.  */
 
 
   /* Create stream from socket.  */
-  stream_sock = es_fdopen (sock_client, "r+");
+  stream_sock = es_fdopen (FD2INT(sock_client), "r+");
   if (!stream_sock)
     {
   if (!stream_sock)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       log_error (_("failed to create stream from socket: %s\n"),
                 gpg_strerror (err));
       goto out;
       log_error (_("failed to create stream from socket: %s\n"),
                 gpg_strerror (err));
       goto out;
@@ -2855,27 +3326,30 @@ start_command_handler_ssh (int sock_client)
   ret = es_setvbuf (stream_sock, NULL, _IONBF, 0);
   if (ret)
     {
   ret = es_setvbuf (stream_sock, NULL, _IONBF, 0);
   if (ret)
     {
-      err = gpg_error_from_errno (errno);
-      log_error (_("failed to disable buffering "
-                   "on socket stream: %s\n"), gpg_strerror (err));
+      err = gpg_error_from_syserror ();
+      log_error ("failed to disable buffering "
+                 "on socket stream: %s\n", gpg_strerror (err));
       goto out;
     }
 
   /* Main processing loop. */
       goto out;
     }
 
   /* Main processing loop. */
-  while ( !ssh_request_process (&ctrl, stream_sock) )
-    ;
+  while ( !ssh_request_process (ctrl, stream_sock) )
+    {
+      /* Check wether we have reached EOF before trying to read
+        another request.  */
+      int c;
+
+      c = es_fgetc (stream_sock);
+      if (c == EOF)
+        break;
+      es_ungetc (c, stream_sock);
+    }
 
   /* Reset the SCD in case it has been used. */
 
   /* Reset the SCD in case it has been used. */
-  agent_reset_scd (&ctrl);
+  agent_reset_scd (ctrl);
 
 
  out:
   if (stream_sock)
     es_fclose (stream_sock);
 
 
  out:
   if (stream_sock)
     es_fclose (stream_sock);
-
-  free (ctrl.display);
-  free (ctrl.ttyname);
-  free (ctrl.ttytype);
-  free (ctrl.lc_ctype);
-  free (ctrl.lc_messages);
 }
 }