agent/
[gnupg.git] / agent / command-ssh.c
index f48df69..23f083c 100644 (file)
@@ -1,5 +1,5 @@
 /* command-ssh.c - gpg-agent's ssh-agent emulation layer
- * Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -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
@@ -147,10 +148,14 @@ typedef struct ssh_key_type_spec
      is required by gpg-agent's key access layer.  */
   const char *elems_sexp_order;
 
-  /* Key modifier function.  */
+  /* Key modifier function.  Key modifier functions are necessary in
+     order to fix any inconsistencies between the representation of
+     keys on the SSH and on the GnuPG side.  */
   ssh_key_modifier_t key_modifier;
 
-  /* Signature encoder function.  */
+  /* Signature encoder function.  Signature encoder functions are
+     necessary since the encoding of signatures depends on the used
+     algorithm.  */
   ssh_signature_encoder_t signature_encoder;
 
   /* Misc flags.  */
@@ -160,26 +165,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,21 +200,21 @@ 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 }
-
 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)
-  };
+#define REQUEST_SPEC_DEFINE(id, name, secret_input) \
+  { SSH_REQUEST_##id, ssh_handler_##name, #name, secret_input }
+
+    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
+  };
 
 
 /* Table holding key type specifications.  */
@@ -292,6 +297,7 @@ stream_read_byte (estream_t stream, unsigned char *b)
        err = gpg_error_from_errno (errno);
       else
        err = gpg_error (GPG_ERR_EOF);
+      *b = 0;
     }
   else
     {
@@ -599,6 +605,9 @@ 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;
   
@@ -815,7 +824,10 @@ mpint_list_free (gcry_mpi_t *mpi_list)
     }
 }
 
-
+/* Receive key material MPIs from STREAM according to KEY_SPEC;
+   depending on SECRET expect a public key or secret key.  The newly
+   allocated list of MPIs is stored in MPI_LIST.  Returns usual error
+   code.  */
 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)
@@ -981,7 +993,9 @@ ssh_signature_encoder_dsa (estream_t signature_blob, gcry_mpi_t *mpis)
  */
 
 
-/*  */
+/* 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.  */
 static gpg_error_t
 sexp_key_construct (gcry_sexp_t *sexp,
                    ssh_key_type_spec_t key_spec, int secret,
@@ -1071,7 +1085,12 @@ sexp_key_construct (gcry_sexp_t *sexp,
   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
+   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.  */
 static gpg_error_t
 sexp_key_extract (gcry_sexp_t sexp,
                  ssh_key_type_spec_t key_spec, int *secret,
@@ -1366,6 +1385,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;
@@ -1516,6 +1538,18 @@ ssh_read_key_public_from_blob (unsigned char *blob, size_t blob_size,
 
 \f
 
+/* This function calculates the key grip for the key contained in the
+   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, unsigned char *buffer)
+{
+  if (!gcry_pk_get_keygrip (key, buffer))
+    return gpg_error (GPG_ERR_INTERNAL);
+
+  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.  */
@@ -1549,13 +1583,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;
@@ -1568,7 +1602,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.  */
@@ -1581,61 +1615,58 @@ 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;
     }
-  
-  if ( !gcry_pk_get_keygrip (s_pk, grip) )
+
+  err = ssh_key_grip (s_pk, grip);
+  if (err)
     {
-      log_debug ("error computing keygrip from received card key\n");
+      log_debug ("error computing keygrip from received card key: %s\n",
+                gcry_strerror (err));
       xfree (pkbuf);
       gcry_sexp_release (s_pk);
       xfree (serialno);
-      return gpg_error (GPG_ERR_INTERNAL);
+      xfree (authkeyid);
+      return err;
     }
 
   if ( agent_key_available (grip) )
@@ -1644,13 +1675,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);
           xfree (pkbuf);
           gcry_sexp_release (s_pk);
           xfree (serialno);
+          xfree (authkeyid);
           return err;
         }
       err = agent_shadow_key (pkbuf, shadow_info, &tmp);
@@ -1661,6 +1693,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);
@@ -1675,18 +1708,23 @@ 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)
         {
@@ -1694,12 +1732,14 @@ 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);
   xfree (serialno);
+  xfree (authkeyid);
   *r_pk = s_pk;
   return 0;
 }
@@ -1709,7 +1749,9 @@ card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn)
 
 /*
 
-  Request handler.  
+  Request handler.  Each handler is provided with a CTRL context, a
+  REQUEST object and a RESPONSE object.  The actual request is to be
+  read from REQUEST, the response needs to be written to RESPONSE.
 
 */
 
@@ -1733,10 +1775,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.  */
 
@@ -1838,7 +1880,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;
 
@@ -1924,7 +1966,10 @@ 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
+   for the digest.  */
 static gpg_error_t
 data_hash (unsigned char *data, size_t data_n,
           int md_algorithm, unsigned char *hash)
@@ -1934,20 +1979,22 @@ data_hash (unsigned char *data, size_t data_n,
   return 0;
 }
 
-
+/* This function signs the data contained in CTRL, stores the created
+   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,
           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;
@@ -1955,22 +2002,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);
   ctrl->use_auth_call = 0;
   if (err)
     goto out;
@@ -2074,7 +2115,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:
@@ -2093,6 +2134,7 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
   return err;
 }
 
+/* Handler for the "sign_request" command.  */
 static gpg_error_t
 ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
 {
@@ -2108,7 +2150,6 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
   size_t sig_n;
   u32 data_size;
   u32 flags;
-  void *p;
   gpg_error_t err;
   gpg_error_t ret_err;
 
@@ -2149,12 +2190,9 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
     goto out;
 
   /* Calculate key grip.  */
-  p = gcry_pk_get_keygrip (key, key_grip);
-  if (! p)
-    {
-      err = gpg_error (GPG_ERR_INTERNAL); /* FIXME?  */
-      goto out;
-    }
+  err = ssh_key_grip (key, key_grip);
+  if (err)
+    goto out;
 
   /* Sign data.  */
 
@@ -2197,7 +2235,9 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
   return ret_err;
 }
 
-
+/* This function extracts the comment contained in the key
+   S-Expression KEY and stores a copy in COMMENT.  Returns usual error
+   code.  */
 static gpg_error_t
 ssh_key_extract_comment (gcry_sexp_t key, char **comment)
 {
@@ -2238,26 +2278,12 @@ ssh_key_extract_comment (gcry_sexp_t key, char **comment)
   return err;
 }
 
+/* This function converts the key contained in the S-Expression KEY
+   into a buffer, which is protected by the passphrase PASSPHRASE.
+   Returns usual error code.  */
 static gpg_error_t
-ssh_key_grip (gcry_sexp_t key, char *buffer)
-{
-  gpg_error_t err;
-  char *p;
-
-  /* FIXME: unsigned vs. signed.  */
-  
-  p = gcry_pk_get_keygrip (key, buffer);
-  if (! p)
-    err = gpg_error (GPG_ERR_INTERNAL);        /* FIXME?  */
-  else
-    err = 0;
-
-  return err;
-}
-
-static gpg_error_t
-ssh_key_to_buffer (gcry_sexp_t key, const char *passphrase,
-                  unsigned char **buffer, size_t *buffer_n)
+ssh_key_to_protected_buffer (gcry_sexp_t key, const char *passphrase,
+                            unsigned char **buffer, size_t *buffer_n)
 {
   unsigned char *buffer_new;
   unsigned int buffer_new_n;
@@ -2286,7 +2312,7 @@ ssh_key_to_buffer (gcry_sexp_t key, const char *passphrase,
 
 
 
-/* Store the ssh KEY into our local key storage and protect him after
+/* 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
@@ -2295,7 +2321,7 @@ 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;
@@ -2308,8 +2334,6 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
   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) )
@@ -2340,11 +2364,11 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
     }
   pi->max_length = 100;
   pi->max_tries = 1;
-  err = agent_askpin (ctrl, description, NULL, pi);
+  err = agent_askpin (ctrl, description, NULL, NULL, pi);
   if (err)
     goto out;
 
-  err = ssh_key_to_buffer (key, pi->pin, &buffer, &buffer_n);
+  err = ssh_key_to_protected_buffer (key, pi->pin, &buffer, &buffer_n);
   if (err)
     goto out;
 
@@ -2357,7 +2381,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;
 
@@ -2377,7 +2401,9 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
 }
 
 
-
+/* This function removes the key contained in the S-Expression KEY
+   from the local key storage, in case it exists there.  Returns usual
+   error code.  FIXME: this function is a stub.  */
 static gpg_error_t
 ssh_identity_drop (gcry_sexp_t key)
 {
@@ -2398,6 +2424,7 @@ ssh_identity_drop (gcry_sexp_t key)
   return err;
 }
 
+/* Handler for the "add_identity" command.  */
 static gpg_error_t
 ssh_handler_add_identity (ctrl_t ctrl, estream_t request, estream_t response)
 {
@@ -2460,15 +2487,18 @@ 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;
 }
 
+/* Handler for the "remove_identity" command.  */
 static gpg_error_t
-ssh_handler_remove_identity (ctrl_t ctrl, estream_t request,
-                             estream_t response)
+ssh_handler_remove_identity (ctrl_t ctrl,
+                            estream_t request, estream_t response)
 {
   unsigned char *key_blob;
   u32 key_blob_size;
@@ -2496,12 +2526,15 @@ 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;
 }
 
+/* FIXME: stub function.  Actually useful?  */
 static gpg_error_t
 ssh_identities_remove_all (void)
 {
@@ -2515,20 +2548,25 @@ ssh_identities_remove_all (void)
   return err;
 }
 
+/* Handler for the "remove_all_identities" command.  */
 static gpg_error_t
-ssh_handler_remove_all_identities (ctrl_t ctrl, estream_t request,
-                                   estream_t response)
+ssh_handler_remove_all_identities (ctrl_t ctrl,
+                                  estream_t request, estream_t response)
 {
   gpg_error_t ret_err;
   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;
 }
 
+/* Lock agent?  FIXME: stub function.  */
 static gpg_error_t
 ssh_lock (void)
 {
@@ -2541,6 +2579,7 @@ ssh_lock (void)
   return err;
 }
 
+/* Unock agent?  FIXME: stub function.  */
 static gpg_error_t
 ssh_unlock (void)
 {
@@ -2552,6 +2591,7 @@ ssh_unlock (void)
   return err;
 }
 
+/* Handler for the "lock" command.  */
 static gpg_error_t
 ssh_handler_lock (ctrl_t ctrl, estream_t request, estream_t response)
 {
@@ -2559,12 +2599,16 @@ 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;
 }
 
+/* Handler for the "unlock" command.  */
 static gpg_error_t
 ssh_handler_unlock (ctrl_t ctrl, estream_t request, estream_t response)
 {
@@ -2572,22 +2616,51 @@ 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
 
+/* Return the request specification for the request identified by TYPE
+   or NULL in case the requested request specification could not be
+   found.  */
+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;
+}
+
+/* Process a single request.  The request is read from and the
+   response is written to STREAM_SOCK.  Uses CTRL as context.  Returns
+   zero in case of success, non zero in case of failure.  */
 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;
@@ -2604,10 +2677,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)
@@ -2617,7 +2693,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);
@@ -2629,7 +2724,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);
@@ -2641,38 +2736,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)
@@ -2737,13 +2814,13 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
   return !!err;
 }
 
+/* Start serving client on SOCK_CLIENT.  */
 void
 start_command_handler_ssh (int sock_client)
 {
   struct server_control_s ctrl;
   estream_t stream_sock;
   gpg_error_t err;
-  int bad;
   int ret;
 
   /* Setup control structure.  */
@@ -2787,15 +2864,15 @@ start_command_handler_ssh (int sock_client)
       goto out;
     }
 
-  while (1)
-    {
-      bad = ssh_request_process (&ctrl, stream_sock);
-      if (bad)
-       break;
-    };
+  /* Main processing loop. */
+  while ( !ssh_request_process (&ctrl, stream_sock) )
+    ;
+
+  /* Reset the SCD in case it has been used. */
+  agent_reset_scd (&ctrl);
 
- out:
 
+ out:
   if (stream_sock)
     es_fclose (stream_sock);