2005-04-20 Moritz Schulte <moritz@g10code.com>
[gnupg.git] / agent / command-ssh.c
index 8ea042e..133dd01 100644 (file)
@@ -107,6 +107,7 @@ typedef struct ssh_request_spec
   unsigned char type;
   ssh_request_handler_t handler;
   const char *identifier;
+  unsigned int secret_input;
 } ssh_request_spec_t;
 
 /* Type for "key modifier functions", which are necessary since
@@ -160,26 +161,26 @@ typedef struct ssh_key_type_spec
 
 /* Prototypes.  */
 static gpg_error_t ssh_handler_request_identities (ctrl_t ctrl,
-                                                   estream_t request,
-                                                   estream_t response);
+                                                  estream_t request,
+                                                  estream_t response);
 static gpg_error_t ssh_handler_sign_request (ctrl_t ctrl,
-                                             estream_t request,
-                                             estream_t response);
+                                            estream_t request,
+                                            estream_t response);
 static gpg_error_t ssh_handler_add_identity (ctrl_t ctrl,
-                                             estream_t request,
-                                             estream_t response);
+                                            estream_t request,
+                                            estream_t response);
 static gpg_error_t ssh_handler_remove_identity (ctrl_t ctrl,
-                                                estream_t request,
-                                                estream_t response);
+                                               estream_t request,
+                                               estream_t response);
 static gpg_error_t ssh_handler_remove_all_identities (ctrl_t ctrl,
-                                                      estream_t request,
-                                                      estream_t response);
+                                                     estream_t request,
+                                                     estream_t response);
 static gpg_error_t ssh_handler_lock (ctrl_t ctrl,
-                                     estream_t request,
-                                     estream_t response);
+                                    estream_t request,
+                                    estream_t response);
 static gpg_error_t ssh_handler_unlock (ctrl_t ctrl,
-                                     estream_t request,
-                                     estream_t response);
+                                      estream_t request,
+                                      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,
@@ -195,19 +196,19 @@ static gpg_error_t ssh_signature_encoder_dsa (estream_t signature_blob,
 /* Associating request types with the corresponding request
    handlers.  */
 
-#define REQUEST_SPEC_DEFINE(id, name) \
-  { SSH_REQUEST_##id, ssh_handler_##name, #name }
+#define REQUEST_SPEC_DEFINE(id, name, secret_input) \
+  { SSH_REQUEST_##id, ssh_handler_##name, #name, secret_input }
 
 static ssh_request_spec_t request_specs[] =
   {
-    REQUEST_SPEC_DEFINE (REQUEST_IDENTITIES,    request_identities),
-    REQUEST_SPEC_DEFINE (SIGN_REQUEST,          sign_request),
-    REQUEST_SPEC_DEFINE (ADD_IDENTITY,          add_identity),
-    REQUEST_SPEC_DEFINE (ADD_ID_CONSTRAINED,    add_identity),
-    REQUEST_SPEC_DEFINE (REMOVE_IDENTITY,       remove_identity),
-    REQUEST_SPEC_DEFINE (REMOVE_ALL_IDENTITIES, remove_all_identities),
-    REQUEST_SPEC_DEFINE (LOCK,                  lock),
-    REQUEST_SPEC_DEFINE (UNLOCK,                unlock)
+    REQUEST_SPEC_DEFINE (REQUEST_IDENTITIES,    request_identities,    1),
+    REQUEST_SPEC_DEFINE (SIGN_REQUEST,          sign_request,          0),
+    REQUEST_SPEC_DEFINE (ADD_IDENTITY,          add_identity,          1),
+    REQUEST_SPEC_DEFINE (ADD_ID_CONSTRAINED,    add_identity,          1),
+    REQUEST_SPEC_DEFINE (REMOVE_IDENTITY,       remove_identity,       0),
+    REQUEST_SPEC_DEFINE (REMOVE_ALL_IDENTITIES, remove_all_identities, 0),
+    REQUEST_SPEC_DEFINE (LOCK,                  lock,                  0),
+    REQUEST_SPEC_DEFINE (UNLOCK,                unlock,                0)
   };
 #undef REQUEST_SPEC_DEFINE
 
@@ -659,7 +660,9 @@ open_control_file (FILE **r_fp, int append)
      (i.e. where Pth might switch threads) we need to employ a
      mutex.  */
   *r_fp = NULL;
-  fname = make_filename (opt.homedir, "sshcontrol.txt", NULL);
+  fname = make_filename (opt.homedir, "sshcontrol", NULL);
+  /* 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)
     {
@@ -1073,7 +1076,7 @@ sexp_key_construct (gcry_sexp_t *sexp,
 static gpg_error_t
 sexp_key_extract (gcry_sexp_t sexp,
                  ssh_key_type_spec_t key_spec, int *secret,
-                 gcry_mpi_t **mpis, const char **comment)
+                 gcry_mpi_t **mpis, char **comment)
 {
   gpg_error_t err;
   gcry_sexp_t value_list;
@@ -1125,7 +1128,7 @@ sexp_key_extract (gcry_sexp_t sexp,
   mpis_new = xtrymalloc (sizeof (*mpis_new) * (elems_n + 1));
   if (! mpis_new)
     {
-      err = gpg_error_from_errno (errno); /* FIXME, xtrymalloc+errno.  */
+      err = gpg_error_from_errno (errno);
       goto out;
     }
   memset (mpis_new, 0, sizeof (*mpis_new) * (elems_n + 1));
@@ -1146,7 +1149,9 @@ sexp_key_extract (gcry_sexp_t sexp,
          break;
        }
 
-      mpi = gcry_sexp_nth_mpi (value_pair, 1, GCRYMPI_FMT_USG);
+      /* Note that we need to use STD format; i.e. prepend a 0x00 to
+         indicate a positive number if the high bit is set. */
+      mpi = gcry_sexp_nth_mpi (value_pair, 1, GCRYMPI_FMT_STD);
       if (! mpi)
        {
          err = gpg_error (GPG_ERR_INV_SEXP);
@@ -1172,14 +1177,12 @@ sexp_key_extract (gcry_sexp_t sexp,
       data_n = 6;
     }
 
-  comment_new = xtrymalloc (data_n + 1);
+  comment_new = make_cstring (data, data_n);
   if (! comment_new)
     {
       err = gpg_error_from_errno (errno);
       goto out;
     }
-  strncpy (comment_new, data, data_n);
-  comment_new[data_n] = 0;
 
   if (secret)
     *secret = is_secret;
@@ -1201,10 +1204,10 @@ sexp_key_extract (gcry_sexp_t sexp,
   return err;
 }
 
-/* Extract the car from SEXP, and create a newly created C-string it,
+/* 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, const char **identifier)
+sexp_extract_identifier (gcry_sexp_t sexp, char **identifier)
 {
   char *identifier_new;
   gcry_sexp_t sublist;
@@ -1247,8 +1250,16 @@ sexp_extract_identifier (gcry_sexp_t sexp, const char **identifier)
 
 \f
 
-/* Key I/O.  */
+/*
+
+  Key I/O.
 
+*/
+
+/* Search for a key specification entry.  If SSH_NAME is not NULL,
+   search for an entry whose "ssh_name" is equal to SSH_NAME;
+   otherwise, search for an entry whose "name" is equal to NAME.
+   Store found entry in SPEC on success, return error otherwise.  */
 static gpg_error_t
 ssh_key_type_lookup (const char *ssh_name, const char *name,
                     ssh_key_type_spec_t *spec)
@@ -1272,6 +1283,11 @@ ssh_key_type_lookup (const char *ssh_name, const char *name,
   return err;
 }
 
+/* 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.
+   Constructs a new S-Expression from received data and stores it in
+   KEY_NEW.  Returns zero on success or an error code.  */
 static gpg_error_t
 ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
                  int read_comment, ssh_key_type_spec_t *key_spec)
@@ -1338,6 +1354,9 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
   return err;
 }
 
+/* Converts a key of type TYPE, whose key material is given in MPIS,
+   into a newly created binary blob, which is to be stored in
+   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)
@@ -1404,13 +1423,17 @@ ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
 }
                              
 
+/* 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;
   gcry_mpi_t *mpi_list;
-  const char *key_type;
-  const char *comment;
+  char *key_type;
+  char *comment;
   unsigned char *blob;
   size_t blob_n;
   gpg_error_t err;
@@ -1441,18 +1464,22 @@ ssh_send_key_public (estream_t stream, gcry_sexp_t key_public)
   if (err)
     goto out;
 
-  err = stream_write_cstring (stream, comment);
+  err = stream_write_cstring (stream,
+                              override_comment? override_comment : comment);
   
  out:
 
   mpint_list_free (mpi_list);
-  xfree ((void *) key_type);
-  xfree ((void *) comment);
+  xfree (key_type);
+  xfree (comment);
   xfree (blob);
 
   return err;
 }
 
+/* Read a public key out of BLOB/BLOB_SIZE according to the key
+   specification given as KEY_SPEC, storing the new key in KEY_PUBLIC.
+   Returns zero on success or an error code.  */
 static gpg_error_t
 ssh_read_key_public_from_blob (unsigned char *blob, size_t blob_size,
                               gcry_sexp_t *key_public,
@@ -1490,11 +1517,14 @@ ssh_read_key_public_from_blob (unsigned char *blob, size_t blob_size,
 
 \f
 
+/* 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.  */
 static gpg_error_t
 key_secret_to_public (gcry_sexp_t *key_public,
                      ssh_key_type_spec_t spec, gcry_sexp_t key_secret)
 {
-  const char *comment;
+  char *comment;
   gcry_mpi_t *mpis;
   gpg_error_t err;
   int is_secret;
@@ -1511,22 +1541,186 @@ key_secret_to_public (gcry_sexp_t *key_public,
  out:
 
   mpint_list_free (mpis);
-  xfree ((char *) comment);
+  xfree (comment);
 
   return err;
 }
 
+
+/* 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
+   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 *serialno = NULL;
+  unsigned char *pkbuf;
+  size_t pkbuflen;
+  gcry_sexp_t s_pk;
+  unsigned char grip[20];
+
+  *r_pk = NULL;
+  if (cardsn)
+    *cardsn = NULL;
+
+  /* First see whether a card is available and whether the application
+     is supported.  */
+  err = agent_card_getattr (ctrl, "APPTYPE", &appname);
+  if ( gpg_err_code (err) == GPG_ERR_CARD_REMOVED )
+    {
+      /* Ask for the serial number to reset the card.  */
+      err = agent_card_serialno (ctrl, &serialno);
+      if (err)
+        {
+          if (opt.verbose)
+            log_info (_("error getting serial number of card: %s\n"),
+                      gpg_strerror (err));
+          return err;
+        }
+      log_info (_("detected card with S/N: %s\n"), serialno);
+      err = agent_card_getattr (ctrl, "APPTYPE", &appname);
+    }
+  if (err)
+    {
+      log_error (_("error getting application type 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));
+      return err;
+    }
+
+  /* Read the public key.  */
+  err = agent_card_readkey (ctrl, "OPENPGP.3", &pkbuf);
+  if (err)
+    {
+      if (opt.verbose)
+        log_info (_("no suitable card key found: %s\n"), gpg_strerror (err));
+      xfree (serialno);
+      return err;
+    }
+
+  pkbuflen = gcry_sexp_canon_len (pkbuf, 0, NULL, NULL);
+  err = gcry_sexp_sscan (&s_pk, NULL, pkbuf, pkbuflen);
+  if (err)
+    {
+      log_error ("failed to build S-Exp from received card key: %s\n",
+                 gpg_strerror (err));
+      xfree (pkbuf);
+      xfree (serialno);
+      return err;
+    }
+  
+  if ( !gcry_pk_get_keygrip (s_pk, grip) )
+    {
+      log_debug ("error computing keygrip from received card key\n");
+      xfree (pkbuf);
+      gcry_sexp_release (s_pk);
+      xfree (serialno);
+      return gpg_error (GPG_ERR_INTERNAL);
+    }
+
+  if ( agent_key_available (grip) )
+    {
+      /* (Shadow)-key is not available in our key storage.  */
+      unsigned char *shadow_info;
+      unsigned char *tmp;
+      
+      shadow_info = make_shadow_info (serialno, "OPENPGP.3");
+      if (!shadow_info)
+        {
+          err = gpg_error_from_errno (errno);
+          xfree (pkbuf);
+          gcry_sexp_release (s_pk);
+          xfree (serialno);
+          return err;
+        }
+      err = agent_shadow_key (pkbuf, shadow_info, &tmp);
+      xfree (shadow_info);
+      if (err)
+        {
+          log_error (_("shadowing the key failed: %s\n"), gpg_strerror (err));
+          xfree (pkbuf);
+          gcry_sexp_release (s_pk);
+          xfree (serialno);
+          return err;
+        }
+      xfree (pkbuf);
+      pkbuf = tmp;
+      pkbuflen = gcry_sexp_canon_len (pkbuf, 0, NULL, NULL);
+      assert (pkbuflen);
+
+      err = agent_write_private_key (grip, pkbuf, pkbuflen, 0);
+      if (err)
+        {
+          log_error (_("error writing key: %s\n"), gpg_strerror (err));
+          xfree (pkbuf);
+          gcry_sexp_release (s_pk);
+          xfree (serialno);
+          return err;
+        }
+    }
+
+  if (cardsn)
+    {
+      size_t snlen = strlen (serialno);
+
+      if (snlen == 32
+          && !memcmp (serialno, "D27600012401", 12)) /* OpenPGP card. */
+        *cardsn = xtryasprintf ("cardno:%.12s", serialno+16);
+      else /* Something is wrong: Print all. */
+        *cardsn = xtryasprintf ("cardno:%s", serialno);
+      if (!*cardsn)
+        {
+          err = gpg_error_from_errno (errno);
+          xfree (pkbuf);
+          gcry_sexp_release (s_pk);
+          xfree (serialno);
+          return err;
+        }
+    }
+
+  xfree (pkbuf);
+  xfree (serialno);
+  *r_pk = s_pk;
+  return 0;
+}
+
+
 \f
 
 /*
+
   Request handler.  
- */
 
+*/
+
+
+/* Handler for the "request_identities" command.  */
 static gpg_error_t
 ssh_handler_request_identities (ctrl_t ctrl,
                                 estream_t request, estream_t response)
 {
-  const char *key_type;
+  char *key_type;
   ssh_key_type_spec_t spec;
   struct dirent *dir_entry;
   char *key_directory;
@@ -1540,9 +1734,10 @@ ssh_handler_request_identities (ctrl_t ctrl,
   gcry_sexp_t key_public;
   DIR *dir;
   gpg_error_t err;
-  gpg_error_t ret_err;
   int ret;
   FILE *ctrl_fp = NULL;
+  char *cardsn;
+  gpg_error_t ret_err;
 
   /* Prepare buffer stream.  */
 
@@ -1589,91 +1784,96 @@ ssh_handler_request_identities (ctrl_t ctrl,
       goto out;
     }
 
-  /* Iterate over key files.  */
 
-  /* FIXME: make sure that buffer gets deallocated properly.  */
+
+  /* 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))
+    {
+      err = ssh_send_key_public (key_blobs, key_public, cardsn);
+      gcry_sexp_release (key_public);
+      key_public = NULL;
+      xfree (cardsn);
+      if (err)
+        goto out;
+      
+      key_counter++;
+    }
+
+
+  /* Then look at all the registered an allowed keys. */
+
 
   /* 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);
   if (err)
     goto out;
 
-#warning Really need to fix this fixme.
-  /*
- FIXME:  First check whether a key is currently available in the card reader - this should be allowed even without being listed in sshcontrol.txt.
-  */
-
-  while (1)
+  while ( (dir_entry = readdir (dir)) )
     {
-      dir_entry = readdir (dir);
-      if (dir_entry)
-       {
-         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;
-
-             strncpy (key_path + key_directory_n + 1, dir_entry->d_name, 40);
-
-             /* Read file content.  */
-             err = file_to_buffer (key_path, &buffer, &buffer_n);
-             if (err)
-               break;
+      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;
+
+          strncpy (key_path + key_directory_n + 1, dir_entry->d_name, 40);
+
+          /* Read file content.  */
+          err = file_to_buffer (key_path, &buffer, &buffer_n);
+          if (err)
+            goto out;
              
-             err = gcry_sexp_sscan (&key_secret, NULL, buffer, buffer_n);
-             if (err)
-               break;
+          err = gcry_sexp_sscan (&key_secret, NULL, buffer, buffer_n);
+          if (err)
+            goto out;
 
-             xfree (buffer);
-             buffer = NULL;
+          xfree (buffer);
+          buffer = NULL;
 
-             err = sexp_extract_identifier (key_secret, &key_type);
-             if (err)
-               break;
+          err = sexp_extract_identifier (key_secret, &key_type);
+          if (err)
+            goto out;
 
-             err = ssh_key_type_lookup (NULL, key_type, &spec);
-             if (err)
-               break;
+          err = ssh_key_type_lookup (NULL, key_type, &spec);
+          if (err)
+            goto out;
 
-             xfree ((void *) key_type);
-             key_type = NULL;
+          xfree (key_type);
+          key_type = NULL;
 
-             err = key_secret_to_public (&key_public, spec, key_secret);
-             if (err)
-               break;
+          err = key_secret_to_public (&key_public, spec, key_secret);
+          if (err)
+            goto out;
 
-             gcry_sexp_release (key_secret);
-             key_secret = NULL;
+          gcry_sexp_release (key_secret);
+          key_secret = NULL;
              
-             err = ssh_send_key_public (key_blobs, key_public);
-             if (err)
-               break;
+          err = ssh_send_key_public (key_blobs, key_public, NULL);
+          if (err)
+            goto out;
 
-             gcry_sexp_release (key_public);
-             key_public = NULL;
+          gcry_sexp_release (key_public);
+          key_public = NULL;
 
-             key_counter++;
-           }
-       }
-      else
-       break;
+          key_counter++;
+        }
     }
-  if (err)
-    goto out;
   
   ret = es_fseek (key_blobs, 0, SEEK_SET);
   if (ret)
@@ -1720,13 +1920,12 @@ ssh_handler_request_identities (ctrl_t ctrl,
   free (key_directory);
   xfree (key_path);
   xfree (buffer);
-  /* FIXME: Ist is for sure is a Bad Thing to use the const qualifier
-     and later cast it away.  You can't do that!!! */
-  xfree ((void *) key_type);           /* FIXME? */
+  xfree (key_type);
 
   return ret_err;
 }
 
+/*  */
 static gpg_error_t
 data_hash (unsigned char *data, size_t data_n,
           int md_algorithm, unsigned char *hash)
@@ -1749,7 +1948,7 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
   gcry_mpi_t sig_value;
   unsigned char *sig_blob;
   size_t sig_blob_n;
-  const char *identifier;
+  char *identifier;
   const char *identifier_raw;
   size_t identifier_n;
   ssh_key_type_spec_t spec;
@@ -1769,9 +1968,11 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
   sig_value = NULL;
   mpis = NULL;
 
+  ctrl->use_auth_call = 1;
   err = agent_pksign_do (ctrl,
-                         _("Please provide the passphrase "
-                           "for the ssh key `%c':"), &signature_sexp, 0);
+                         _("Please enter the passphrase "
+                           "for the ssh key%0A  %c"), &signature_sexp, 0);
+  ctrl->use_auth_call = 0;
   if (err)
     goto out;
 
@@ -1888,7 +2089,7 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
   gcry_sexp_release (signature_sexp);
   gcry_sexp_release (sublist);
   mpint_list_free (mpis);
-  xfree ((void *) identifier);
+  xfree (identifier);
 
   return err;
 }
@@ -1908,7 +2109,7 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
   size_t sig_n;
   u32 data_size;
   u32 flags;
-  const void *p;
+  void *p;
   gpg_error_t err;
   gpg_error_t ret_err;
 
@@ -2021,15 +2222,13 @@ ssh_key_extract_comment (gcry_sexp_t key, char **comment)
       goto out;
     }
 
-  comment_new = xtrymalloc (data_n + 1);
+  comment_new = make_cstring (data, data_n);
   if (! comment_new)
     {
       err = gpg_error_from_errno (errno);
       goto out;
     }
 
-  strncpy (comment_new, data, data_n);
-  comment_new[data_n] = 0;
   *comment = comment_new;
   err = 0;
 
@@ -2067,8 +2266,7 @@ ssh_key_to_buffer (gcry_sexp_t key, const char *passphrase,
 
   err = 0;
   buffer_new_n = gcry_sexp_sprint (key, GCRYSEXP_FMT_CANON, NULL, 0);
-  buffer_new = xtrymalloc (buffer_new_n);
-  /* FIXME: secmem? */
+  buffer_new = xtrymalloc_secure (buffer_new_n);
   if (! buffer_new)
     {
       err = gpg_error_from_errno (errno);
@@ -2113,7 +2311,7 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
 
   key_grip_raw[sizeof (key_grip_raw) - 1] = 0; /* FIXME:  Why?? */
 
-  /* Check whether the key is alread in our key storage.  Don't do
+  /* Check whether the key is already in our key storage.  Don't do
      anything then.  */
   if ( !agent_key_available (key_grip_raw) )
     goto out; /* Yes, key is available.  */
@@ -2124,8 +2322,8 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
     goto out;
 
   if ( asprintf (&description,
-                 _("Please enter a passphrase to protect%%0A"
-                   "the received secret key%%0A"
+                 _("Please enter a passphrase to protect"
+                   " the received secret key%%0A"
                    "   %s%%0A"
                    "within gpg-agent's key storage"),
                  comment ? comment : "?") < 0)
@@ -2263,8 +2461,10 @@ ssh_handler_add_identity (ctrl_t ctrl, estream_t request, estream_t response)
 
   gcry_sexp_release (key);
 
-  ret_err = stream_write_byte (response,
-                          err ? SSH_RESPONSE_FAILURE : SSH_RESPONSE_SUCCESS);
+  if (! err)
+    ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS);
+  else
+    ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
 
   return ret_err;
 }
@@ -2299,8 +2499,10 @@ ssh_handler_remove_identity (ctrl_t ctrl, estream_t request,
   xfree (key_blob);
   gcry_sexp_release (key);
 
-  ret_err = stream_write_byte (response,
-                          err ? SSH_RESPONSE_FAILURE : SSH_RESPONSE_SUCCESS);
+  if (! err)
+    ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS);
+  else
+    ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
 
   return ret_err;
 }
@@ -2326,8 +2528,11 @@ ssh_handler_remove_all_identities (ctrl_t ctrl, estream_t request,
   gpg_error_t err;
   
   err = ssh_identities_remove_all ();
-  ret_err = stream_write_byte (response,
-                          err ? SSH_RESPONSE_FAILURE : SSH_RESPONSE_SUCCESS);
+
+  if (! err)
+    ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS);
+  else
+    ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
 
   return ret_err;
 }
@@ -2362,8 +2567,11 @@ ssh_handler_lock (ctrl_t ctrl, estream_t request, estream_t response)
   gpg_error_t err;
   
   err = ssh_lock ();
-  ret_err = stream_write_byte (response,
-                          err ? SSH_RESPONSE_FAILURE : SSH_RESPONSE_SUCCESS);
+
+  if (! err)
+    ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS);
+  else
+    ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
 
   return ret_err;
 }
@@ -2375,22 +2583,45 @@ ssh_handler_unlock (ctrl_t ctrl, estream_t request, estream_t response)
   gpg_error_t err;
   
   err = ssh_unlock ();
-  ret_err = stream_write_byte (response,
-                          err ? SSH_RESPONSE_FAILURE : SSH_RESPONSE_SUCCESS);
+
+  if (! err)
+    ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS);
+  else
+    ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
 
   return ret_err;
 }
 
 \f
 
+static ssh_request_spec_t *
+request_spec_lookup (int type)
+{
+  ssh_request_spec_t *spec;
+  unsigned int i;
+
+  for (i = 0; i < DIM (request_specs); i++)
+    if (request_specs[i].type == type)
+      break;
+  if (i == DIM (request_specs))
+    {
+      log_info ("ssh request %u is not supported\n", type);
+      spec = NULL;
+    }
+  else
+    spec = request_specs + i;
+
+  return spec;
+}
+
 static int
 ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
 {
+  ssh_request_spec_t *spec;
   estream_t response;
   estream_t request;
   unsigned char request_type;
   gpg_error_t err;
-  unsigned int i;
   int send_err;
   int ret;
   unsigned char *request_data;
@@ -2420,7 +2651,26 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
     log_info ("received ssh request of length %u\n",
               (unsigned int)request_data_size);
 
-  request = es_mopen (NULL, 0, 0, 1, realloc_secure, gcry_free, "r+");
+  if (! request_data_size)
+    {
+      send_err = 1;
+      goto out;
+      /* Broken request; FIXME.  */
+    }
+
+  request_type = request_data[0];
+  spec = request_spec_lookup (request_type);
+  if (! spec)
+    {
+      send_err = 1;
+      goto out;
+      /* Unknown request; FIXME.  */
+    }
+
+  if (spec->secret_input)
+    request = es_mopen (NULL, 0, 0, 1, realloc_secure, gcry_free, "r+");
+  else
+    request = es_mopen (NULL, 0, 0, 1, gcry_realloc, gcry_free, "r+");
   if (! request)
     {
       err = gpg_error_from_errno (errno);
@@ -2432,7 +2682,7 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
       err = gpg_error_from_errno (errno);
       goto out;
     }
-  err = stream_write_data (request, request_data, request_data_size);
+  err = stream_write_data (request, request_data + 1, request_data_size - 1);
   if (err)
     goto out;
   es_rewind (request);
@@ -2444,38 +2694,20 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
       goto out;
     }
 
-  err = stream_read_byte (request, &request_type);
-  if (err)
-    {
-      send_err = 1;
-      goto out;
-    }
-
-  for (i = 0; i < DIM (request_specs); i++)
-    if (request_specs[i].type == request_type)
-      break;
-  if (i == DIM (request_specs))
-    {
-      log_info ("ssh request %u is not supported\n", request_type);
-      send_err = 1;
-      goto out;
-    }
-
   if (opt.verbose)
     log_info ("ssh request handler for %s (%u) started\n",
-              request_specs[i].identifier, request_specs[i].type);
+              spec->identifier, spec->type);
 
-  err = (*request_specs[i].handler) (ctrl, request, response);
+  err = (*spec->handler) (ctrl, request, response);
 
   if (opt.verbose)
     {
       if (err)
         log_info ("ssh request handler for %s (%u) failed: %s\n",
-                  request_specs[i].identifier, request_specs[i].type,
-                  gpg_strerror (err));
+                  spec->identifier, spec->type, gpg_strerror (err));
       else
         log_info ("ssh request handler for %s (%u) ready\n",
-                  request_specs[i].identifier, request_specs[i].type);
+                  spec->identifier, spec->type);
     }
 
   if (err)