Reworked the estream memory buffer allocation.
[gnupg.git] / agent / command-ssh.c
index 030cc70..76f310a 100644 (file)
@@ -1,11 +1,11 @@
 /* command-ssh.c - gpg-agent's ssh-agent emulation layer
- * Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2004, 2005, 2006, 2009 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
- * 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,
@@ -14,9 +14,7 @@
  * 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.  */
@@ -268,7 +266,7 @@ make_cstring (const char *data, size_t data_n)
   s = xtrymalloc (data_n + 1);
   if (s)
     {
-      strncpy (s, data, data_n);
+      memcpy (s, data, data_n);
       s[data_n] = 0;
     }
 
@@ -294,9 +292,10 @@ stream_read_byte (estream_t stream, unsigned char *b)
   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
     {
@@ -316,7 +315,7 @@ stream_write_byte (estream_t stream, unsigned char b)
 
   ret = es_fputc (b, stream);
   if (ret == EOF)
-    err = gpg_error_from_errno (errno);
+    err = gpg_error_from_syserror ();
   else
     err = 0;
 
@@ -334,7 +333,7 @@ stream_read_uint32 (estream_t stream, u32 *uint32)
 
   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))
@@ -367,7 +366,7 @@ stream_write_uint32 (estream_t stream, u32 uint32)
 
   ret = es_write (stream, buffer, sizeof (buffer), NULL);
   if (ret)
-    err = gpg_error_from_errno (errno);
+    err = gpg_error_from_syserror ();
   else
     err = 0;
 
@@ -384,7 +383,7 @@ stream_read_data (estream_t stream, unsigned char *buffer, size_t size)
 
   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)
@@ -405,7 +404,7 @@ stream_write_data (estream_t stream, const unsigned char *buffer, size_t size)
 
   ret = es_write (stream, buffer, size, NULL);
   if (ret)
-    err = gpg_error_from_errno (errno);
+    err = gpg_error_from_syserror ();
   else
     err = 0;
 
@@ -420,10 +419,8 @@ 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);
@@ -437,7 +434,7 @@ stream_read_string (estream_t stream, unsigned int secure,
     buffer = xtrymalloc (length + 1);
   if (! buffer)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
@@ -525,6 +522,15 @@ stream_read_mpi (estream_t stream, unsigned int secure, gcry_mpi_t *mpint)
   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;
@@ -577,13 +583,13 @@ stream_copy (estream_t dst, estream_t src)
       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)
        {
-         err = gpg_error_from_errno (errno);
+         err = gpg_error_from_syserror ();
          break;
        }
     }
@@ -604,27 +610,30 @@ file_to_buffer (const char *filename, unsigned char **buffer, size_t *buffer_n)
   gpg_error_t err;
   int ret;
 
+  *buffer = NULL;
+  *buffer_n = 0;
+
   buffer_new = NULL;
   err = 0;
   
   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)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
   buffer_new = xtrymalloc (statbuf.st_size);
   if (! buffer_new)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
@@ -701,17 +710,20 @@ open_control_file (FILE **r_fp, int append)
 
 /* 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.  */
+   DISABLED if the found key has been disabled.  If R_TTL is not NULL
+   a specified TTL for that key is stored there. */
 static gpg_error_t
-search_control_file (FILE *fp, const char *hexgrip, int *disabled)
+search_control_file (FILE *fp, const char *hexgrip, 
+                     int *r_disabled, int *r_ttl)
 {
   int c, i;
-  char *p, line[256];
-  
+  char *p, *pend, line[256];
+  long ttl;
+
   assert (strlen (hexgrip) == 40 );
 
   rewind (fp);
-  *disabled = 0;
+  *r_disabled = 0;
  next_line:
   do
     {
@@ -737,10 +749,10 @@ search_control_file (FILE *fp, const char *hexgrip, int *disabled)
     }
   while (!*p || *p == '\n' || *p == '#');
   
-  *disabled = 0;
+  *r_disabled = 0;
   if (*p == '!')
     {
-      *disabled = 1;
+      *r_disabled = 1;
       for (p++; spacep (p); p++)
         ;
     }
@@ -754,7 +766,17 @@ search_control_file (FILE *fp, const char *hexgrip, int *disabled)
       return gpg_error (GPG_ERR_BAD_DATA);
     }
 
-  /* Fixme: Get TTL and flags.  */
+  ttl = strtol (p, &pend, 10);
+  p = pend;
+  if (!(spacep (p) || *p == '\n') || ttl < -1)
+    {
+      log_error ("invalid TTL value in ssh control file; assuming 0\n");
+      ttl = 0;
+    }
+  if (r_ttl)
+    *r_ttl = ttl;
+
+  /* Here is the place to parse flags if we need them.  */  
 
   return 0; /* Okay:  found it.  */
 }
@@ -773,17 +795,19 @@ add_control_entry (ctrl_t ctrl, const char *hexgrip, int ttl)
   FILE *fp;
   int disabled;
 
+  (void)ctrl;
+
   err = open_control_file (&fp, 1);
   if (err)
     return err;
 
-  err = search_control_file (fp, hexgrip, &disabled);
+  err = search_control_file (fp, hexgrip, &disabled, 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);
       fprintf (fp, "# Key added on %04d-%02d-%02d %02d:%02d:%02d\n%s %d\n",
@@ -797,6 +821,29 @@ add_control_entry (ctrl_t ctrl, const char *hexgrip, int ttl)
 }
 
 
+/* Scan the sshcontrol file and return the TTL.  */
+static int
+ttl_from_sshcontrol (const char *hexgrip)
+{
+  FILE *fp;
+  int disabled, ttl;
+
+  if (!hexgrip || strlen (hexgrip) != 40)
+    return 0;  /* Wrong input: Use global default.  */
+
+  if (open_control_file (&fp, 0))
+    return 0; /* Error: Use the global default TTL.  */
+
+  if (search_control_file (fp, hexgrip, &disabled, &ttl)
+      || disabled)
+    ttl = 0;  /* Use the global default if not found or disabled.  */
+
+  fclose (fp); 
+
+  return ttl;
+}
+
+
 
 \f
 
@@ -849,14 +896,12 @@ ssh_receive_mpint_list (estream_t stream, int secret,
   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;
     }
-  
-  memset (mpis, 0, sizeof (*mpis) * (elems_n + 1));
 
   elem_is_secret = 0;
   for (i = 0; i < elems_n; i++)
@@ -1029,7 +1074,7 @@ sexp_key_construct (gcry_sexp_t *sexp,
   sexp_template = xtrymalloc (sexp_template_n);
   if (! sexp_template)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
@@ -1037,7 +1082,7 @@ sexp_key_construct (gcry_sexp_t *sexp,
   arg_list = xtrymalloc (sizeof (*arg_list) * (2 + elems_n + 1));
   if (! arg_list)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
@@ -1139,13 +1184,12 @@ sexp_key_extract (gcry_sexp_t sexp,
     }
 
   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;
     }
-  memset (mpis_new, 0, sizeof (*mpis_new) * (elems_n + 1));
 
   value_list = gcry_sexp_find_token (sexp, key_spec.identifier, 0);
   if (! value_list)
@@ -1194,7 +1238,7 @@ sexp_key_extract (gcry_sexp_t sexp,
   comment_new = make_cstring (data, data_n);
   if (! comment_new)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
@@ -1381,6 +1425,9 @@ ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
   gpg_error_t err;
   unsigned int i;
 
+  *blob = NULL;
+  *blob_size = 0;
+
   blob_new = NULL;
   stream = NULL;
   err = 0;
@@ -1388,7 +1435,7 @@ ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
   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;
     }
 
@@ -1404,7 +1451,7 @@ ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
   blob_size_new = es_ftell (stream);
   if (blob_size_new == -1)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
   
@@ -1415,7 +1462,7 @@ ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
   blob_new = xtrymalloc (blob_size_new);
   if (! blob_new)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
@@ -1507,7 +1554,7 @@ ssh_read_key_public_from_blob (unsigned char *blob, size_t blob_size,
   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;
     }
 
@@ -1535,20 +1582,12 @@ ssh_read_key_public_from_blob (unsigned char *blob, size_t blob_size,
    S-Expression KEY and writes it to BUFFER, which must be large
    enough to hold it.  Returns usual error code.  */
 static gpg_error_t
-ssh_key_grip (gcry_sexp_t key, char *buffer)
+ssh_key_grip (gcry_sexp_t key, unsigned char *buffer)
 {
-  gpg_error_t err;
-  char *p;
+  if (!gcry_pk_get_keygrip (key, buffer))
+    return gpg_error (GPG_ERR_INTERNAL);
 
-  /* FIXME: unsigned vs. signed.  */
-  
-  p = gcry_pk_get_keygrip (key, buffer);
-  if (! p)
-    err = gpg_error (GPG_ERR_INTERNAL);        /* FIXME?  */
-  else
-    err = 0;
-
-  return err;
+  return 0;
 }
 
 /* Converts the secret key KEY_SECRET into a public key, storing it in
@@ -1584,13 +1623,13 @@ key_secret_to_public (gcry_sexp_t *key_public,
 /* Check whether a smartcard is available and whether it has a usable
    key.  Store a copy of that key at R_PK and return 0.  If no key is
    available store NULL at R_PK and return an error code.  If CARDSN
-   is no NULL, a string with the serial number of the card will be
+   is not NULL, a string with the serial number of the card will be
    a malloced and stored there. */
 static gpg_error_t
 card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn)
 {
   gpg_error_t err;
-  char *appname;
+  char *authkeyid;
   char *serialno = NULL;
   unsigned char *pkbuf;
   size_t pkbuflen;
@@ -1603,7 +1642,7 @@ card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn)
 
   /* First see whether a card is available and whether the application
      is supported.  */
-  err = agent_card_getattr (ctrl, "APPTYPE", &appname);
+  err = agent_card_getattr (ctrl, "$AUTHKEYID", &authkeyid);
   if ( gpg_err_code (err) == GPG_ERR_CARD_REMOVED )
     {
       /* Ask for the serial number to reset the card.  */
@@ -1616,51 +1655,45 @@ card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn)
           return err;
         }
       log_info (_("detected card with S/N: %s\n"), serialno);
-      err = agent_card_getattr (ctrl, "APPTYPE", &appname);
+      err = agent_card_getattr (ctrl, "$AUTHKEYID", &authkeyid);
     }
   if (err)
     {
-      log_error (_("error getting application type of card: %s\n"),
+      log_error (_("error getting default authentication keyID of card: %s\n"),
                  gpg_strerror (err));
       xfree (serialno);
       return err;
     }
-  if (strcmp (appname, "OPENPGP"))
-    {
-      log_info (_("card application `%s' is not supported\n"), appname);
-      xfree (appname);
-      xfree (serialno);
-      return gpg_error (GPG_ERR_NOT_SUPPORTED);
-    }
-  xfree (appname);
-  appname = NULL;
 
   /* Get the S/N if we don't have it yet.  Use the fast getattr method.  */
   if (!serialno && (err = agent_card_getattr (ctrl, "SERIALNO", &serialno)) )
     {
       log_error (_("error getting serial number of card: %s\n"),
                  gpg_strerror (err));
+      xfree (authkeyid);
       return err;
     }
 
   /* Read the public key.  */
-  err = agent_card_readkey (ctrl, "OPENPGP.3", &pkbuf);
+  err = agent_card_readkey (ctrl, authkeyid, &pkbuf);
   if (err)
     {
       if (opt.verbose)
         log_info (_("no suitable card key found: %s\n"), gpg_strerror (err));
       xfree (serialno);
+      xfree (authkeyid);
       return err;
     }
 
   pkbuflen = gcry_sexp_canon_len (pkbuf, 0, NULL, NULL);
-  err = gcry_sexp_sscan (&s_pk, NULL, pkbuf, pkbuflen);
+  err = gcry_sexp_sscan (&s_pk, NULL, (char*)pkbuf, pkbuflen);
   if (err)
     {
       log_error ("failed to build S-Exp from received card key: %s\n",
                  gpg_strerror (err));
       xfree (pkbuf);
       xfree (serialno);
+      xfree (authkeyid);
       return err;
     }
 
@@ -1672,6 +1705,7 @@ card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn)
       xfree (pkbuf);
       gcry_sexp_release (s_pk);
       xfree (serialno);
+      xfree (authkeyid);
       return err;
     }
 
@@ -1681,13 +1715,14 @@ card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn)
       unsigned char *shadow_info;
       unsigned char *tmp;
       
-      shadow_info = make_shadow_info (serialno, "OPENPGP.3");
+      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 (authkeyid);
           return err;
         }
       err = agent_shadow_key (pkbuf, shadow_info, &tmp);
@@ -1698,6 +1733,7 @@ card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn)
           xfree (pkbuf);
           gcry_sexp_release (s_pk);
           xfree (serialno);
+          xfree (authkeyid);
           return err;
         }
       xfree (pkbuf);
@@ -1712,31 +1748,38 @@ card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn)
           xfree (pkbuf);
           gcry_sexp_release (s_pk);
           xfree (serialno);
+          xfree (authkeyid);
           return err;
         }
     }
 
   if (cardsn)
     {
-      size_t snlen = strlen (serialno);
+      char *dispsn;
 
-      if (snlen == 32
-          && !memcmp (serialno, "D27600012401", 12)) /* OpenPGP card. */
-        *cardsn = xtryasprintf ("cardno:%.12s", serialno+16);
-      else /* Something is wrong: Print all. */
+      /* If the card handler is able to return a short serialnumber,
+         use that one, else use the complete serialno. */
+      if (!agent_card_getattr (ctrl, "$DISPSERIALNO", &dispsn))
+        {
+          *cardsn = xtryasprintf ("cardno:%s", dispsn);
+          xfree (dispsn);
+        }
+      else
         *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 (authkeyid);
           return err;
         }
     }
 
   xfree (pkbuf);
   xfree (serialno);
+  xfree (authkeyid);
   *r_pk = s_pk;
   return 0;
 }
@@ -1777,6 +1820,8 @@ ssh_handler_request_identities (ctrl_t ctrl,
   char *cardsn;
   gpg_error_t ret_err;
 
+  (void)request;
+
   /* Prepare buffer stream.  */
 
   key_directory = NULL;
@@ -1792,7 +1837,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
   key_blobs = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
   if (! key_blobs)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
@@ -1866,7 +1911,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
           hexgrip[40] = 0;
           if ( strlen (hexgrip) != 40 )
             continue;
-          if (search_control_file (ctrl_fp, hexgrip, &disabled)
+          if (search_control_file (ctrl_fp, hexgrip, &disabled, NULL)
               || disabled)
             continue;
 
@@ -1877,7 +1922,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
           if (err)
             goto out;
              
-          err = gcry_sexp_sscan (&key_secret, NULL, buffer, buffer_n);
+          err = gcry_sexp_sscan (&key_secret, NULL, (char*)buffer, buffer_n);
           if (err)
             goto out;
 
@@ -1916,7 +1961,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
   ret = es_fseek (key_blobs, 0, SEEK_SET);
   if (ret)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
@@ -1955,7 +2000,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
   if (ctrl_fp)
     fclose (ctrl_fp);
 
-  free (key_directory);
+  xfree (key_directory);
   xfree (key_path);
   xfree (buffer);
   xfree (key_type);
@@ -1963,6 +2008,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
   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
@@ -1984,14 +2030,14 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
           unsigned char **sig, size_t *sig_n)
 {
   gpg_error_t err;
-  gcry_sexp_t signature_sexp;
-  estream_t stream;
-  gcry_sexp_t valuelist;
-  gcry_sexp_t sublist;
-  gcry_mpi_t sig_value;
-  unsigned char *sig_blob;
-  size_t sig_blob_n;
-  char *identifier;
+  gcry_sexp_t signature_sexp = NULL;
+  estream_t stream = NULL;
+  gcry_sexp_t valuelist = NULL;
+  gcry_sexp_t sublist = NULL;
+  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;
@@ -1999,22 +2045,16 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
   unsigned int i;
   const char *elems;
   size_t elems_n;
-  gcry_mpi_t *mpis;
+  gcry_mpi_t *mpis = NULL;
 
-  signature_sexp = NULL;
-  identifier = NULL;
-  valuelist = NULL;
-  sublist = NULL;
-  sig_blob = NULL;
-  sig_blob_n = 0;
-  stream = NULL;
-  sig_value = NULL;
-  mpis = NULL;
+  *sig = NULL;
+  *sig_n = 0;
 
   ctrl->use_auth_call = 1;
   err = agent_pksign_do (ctrl,
                          _("Please enter the passphrase "
-                           "for the ssh key%0A  %c"), &signature_sexp, 0);
+                           "for the ssh key%0A  %c"), &signature_sexp,
+                         CACHE_MODE_SSH, ttl_from_sshcontrol);
   ctrl->use_auth_call = 0;
   if (err)
     goto out;
@@ -2029,7 +2069,7 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
   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;
     }
 
@@ -2043,7 +2083,7 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
   identifier = make_cstring (identifier_raw, identifier_n);
   if (! identifier)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
@@ -2058,13 +2098,12 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
   elems = spec.elems_signature;
   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;
     }
-  memset (mpis, 0, sizeof (*mpis) * (elems_n + 1));
 
   for (i = 0; i < elems_n; i++)
     {
@@ -2096,21 +2135,21 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
   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)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
   ret = es_fseek (stream, 0, SEEK_SET);
   if (ret)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }    
 
@@ -2118,7 +2157,7 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
   if (err)
     goto out;
   
-  *sig = (char *) sig_blob;
+  *sig = sig_blob;
   *sig_n = sig_blob_n;
   
  out:
@@ -2267,7 +2306,7 @@ ssh_key_extract_comment (gcry_sexp_t key, char **comment)
   comment_new = make_cstring (data, data_n);
   if (! comment_new)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
@@ -2297,7 +2336,7 @@ ssh_key_to_protected_buffer (gcry_sexp_t key, const char *passphrase,
   buffer_new = xtrymalloc_secure (buffer_new_n);
   if (! buffer_new)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
   
@@ -2315,6 +2354,18 @@ 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
@@ -2324,21 +2375,21 @@ static gpg_error_t
 ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
 {
   gpg_error_t err;
-  unsigned char key_grip_raw[21];
+  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;
+  const char *description2 = _("Please re-enter this passphrase");
   char *comment = NULL;
+  const char *initial_errtext = NULL;
   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)
     goto out;
 
-  key_grip_raw[sizeof (key_grip_raw) - 1] = 0; /* FIXME:  Why?? */
-
   /* Check whether the key is already in our key storage.  Don't do
      anything then.  */
   if ( !agent_key_available (key_grip_raw) )
@@ -2356,23 +2407,43 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
                    "within gpg-agent's key storage"),
                  comment ? comment : "?") < 0)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
 
-  pi = gcry_calloc_secure (1, sizeof (*pi) + 100 + 1);
+  pi = gcry_calloc_secure (2, sizeof (*pi) + 100 + 1);
   if (!pi)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto out;
     }
+  pi2 = pi + (sizeof *pi + 100 + 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;
 
+  /* 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;
@@ -2386,7 +2457,7 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
   for (i = 0; i < 20; i++)
     sprintf (key_grip + 2 * i, "%02X", key_grip_raw[i]);
 
-  err = agent_put_cache (key_grip, pi->pin, ttl);
+  err = agent_put_cache (key_grip, CACHE_MODE_SSH, pi->pin, ttl);
   if (err)
     goto out;
 
@@ -2400,7 +2471,7 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
   xfree (pi);
   xfree (buffer);
   xfree (comment);
-  free (description); /* (asprintf allocated, thus regular free.)  */
+  xfree (description); 
 
   return err;
 }
@@ -2511,6 +2582,8 @@ ssh_handler_remove_identity (ctrl_t ctrl,
   gpg_error_t ret_err;
   gpg_error_t err;
 
+  (void)ctrl;
+
   /* Receive key.  */
 
   key_blob = NULL;
@@ -2560,6 +2633,9 @@ ssh_handler_remove_all_identities (ctrl_t ctrl,
 {
   gpg_error_t ret_err;
   gpg_error_t err;
+
+  (void)ctrl;
+  (void)request;
   
   err = ssh_identities_remove_all ();
 
@@ -2602,6 +2678,9 @@ ssh_handler_lock (ctrl_t ctrl, estream_t request, estream_t response)
 {
   gpg_error_t ret_err;
   gpg_error_t err;
+
+  (void)ctrl;
+  (void)request;
   
   err = ssh_lock ();
 
@@ -2620,6 +2699,9 @@ ssh_handler_unlock (ctrl_t ctrl, estream_t request, estream_t response)
   gpg_error_t ret_err;
   gpg_error_t err;
   
+  (void)ctrl;
+  (void)request;
+
   err = ssh_unlock ();
 
   if (! err)
@@ -2646,7 +2728,8 @@ request_spec_lookup (int type)
       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
@@ -2682,10 +2765,13 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
      secret key material.  The response does not have to be stored in
      secure memory, since we never give out secret keys. 
 
-     FIXME: This is a pretty good DoS.  We only have a limited amount
-     of secure memory, we can't trhow hin everything we get from a
-     client -wk */
-      
+     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 */
+
   /* Retrieve request.  */
   err = stream_read_string (stream_sock, 1, &request_data, &request_data_size);
   if (err)
@@ -2717,13 +2803,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)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       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);
@@ -2734,7 +2820,7 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
   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;
     }
 
@@ -2818,40 +2904,36 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
 
 /* 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;
-  int bad;
   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.  */
-  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);
+  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);
+  if (!ctrl->xauthority && opt.startup_xauthority)
+    ctrl->xauthority = strdup (opt.startup_xauthority);
+  if (!ctrl->pinentry_user_data && opt.startup_pinentry_user_data)
+    ctrl->pinentry_user_data = strdup (opt.startup_pinentry_user_data);
 
 
   /* Create stream from socket.  */
-  stream_sock = es_fdopen (sock_client, "r+");
+  stream_sock = es_fdopen (FD2INT(sock_client), "r+");
   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;
@@ -2861,27 +2943,30 @@ start_command_handler_ssh (int sock_client)
   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;
     }
 
-  while (1)
+  /* Main processing loop. */
+  while ( !ssh_request_process (ctrl, stream_sock) )
     {
-      bad = ssh_request_process (&ctrl, stream_sock);
-      if (bad)
-       break;
-    };
+      /* Check wether we have reached EOF before trying to read
+        another request.  */
+      int c;
 
- out:
+      c = es_fgetc (stream_sock);
+      if (c == EOF)
+        break;
+      es_ungetc (c, stream_sock);
+    }
 
+  /* Reset the SCD in case it has been used. */
+  agent_reset_scd (ctrl);
+
+
+ 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);
 }