Nuked almost all trailing white space.
[gnupg.git] / agent / command-ssh.c
index b44dc21..8603a53 100644 (file)
@@ -1,11 +1,11 @@
 /* command-ssh.c - gpg-agent's ssh-agent emulation layer
- * Copyright (C) 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 /* Only v2 of the ssh-agent protocol is implemented.  */
@@ -34,7 +32,6 @@
 
 #include "agent.h"
 
-#include "estream.h"
 #include "i18n.h"
 
 \f
@@ -71,7 +68,7 @@ static const char sshcontrolblurb[] =
 "# in the SSH protocol.  The ssh-add tool may add new entries to this\n"
 "# file to enable them; you may also add them manually.  Comment\n"
 "# lines, like this one, as well as empty lines are ignored.  Lines do\n"
-"# have a certain length limit but this is not serious limitation as\n" 
+"# have a certain length limit but this is not serious limitation as\n"
 "# the format of the entries is fixed and checked by gpg-agent. A\n"
 "# non-comment line starts with optional white spaces, followed by the\n"
 "# keygrip of the key given as 40 hex digits, optionally followed by a\n"
@@ -195,7 +192,7 @@ static gpg_error_t ssh_signature_encoder_dsa (estream_t signature_blob,
 
 
 /* Global variables.  */
-   
+
 
 /* Associating request types with the corresponding request
    handlers.  */
@@ -237,7 +234,7 @@ static ssh_key_type_spec_t ssh_key_types[] =
 
 
 /*
-   General utility functions. 
+   General utility functions.
  */
 
 /* A secure realloc, i.e. it makes sure to allocate secure memory if A
@@ -248,7 +245,7 @@ static void *
 realloc_secure (void *a, size_t n)
 {
   void *p;
-  
+
   if (a)
     p = gcry_realloc (a, n);
   else
@@ -278,8 +275,8 @@ make_cstring (const char *data, size_t data_n)
 
 
 
-/* 
-   Primitive I/O functions.  
+/*
+   Primitive I/O functions.
  */
 
 
@@ -421,10 +418,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);
@@ -471,7 +466,7 @@ stream_read_cstring (estream_t stream, char **string)
   err = stream_read_string (stream, 0, &buffer, NULL);
   if (err)
     goto out;
-  
+
   *string = (char *) buffer;
 
  out:
@@ -508,7 +503,7 @@ stream_write_cstring (estream_t stream, const char *string)
                             (const unsigned char *) string, strlen (string));
 
   return err;
-}                        
+}
 
 /* Read an MPI from STREAM, store it in MPINT.  Depending on SECURE
    use secure memory.  */
@@ -619,7 +614,7 @@ file_to_buffer (const char *filename, unsigned char **buffer, size_t *buffer_n)
 
   buffer_new = NULL;
   err = 0;
-  
+
   stream = es_fopen (filename, "r");
   if (! stream)
     {
@@ -683,18 +678,16 @@ open_control_file (FILE **r_fp, int append)
   fp = fopen (fname, append? "a+":"r");
   if (!fp && errno == ENOENT)
     {
-      /* Fixme: "x" is a GNU extension.  We might want to use the es_
-         functions here.  */
-      fp = fopen (fname, "wx");  
-      if (!fp)
+      estream_t stream = es_fopen (fname, "wx,mode=-rw-r");
+      if (!stream)
         {
-          err = gpg_error (gpg_err_code_from_errno (errno));
+          err = gpg_error_from_syserror ();
           log_error (_("can't create `%s': %s\n"), fname, gpg_strerror (err));
           xfree (fname);
           return err;
         }
-      fputs (sshcontrolblurb, fp);
-      fclose (fp);
+      es_fputs (sshcontrolblurb, stream);
+      es_fclose (stream);
       fp = fopen (fname, append? "a+":"r");
     }
 
@@ -705,8 +698,8 @@ open_control_file (FILE **r_fp, int append)
       xfree (fname);
       return err;
     }
-  
-  *r_fp = fp;  
+
+  *r_fp = fp;
 
   return 0;
 }
@@ -714,17 +707,21 @@ 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;
+  fseek (fp, 0, SEEK_SET);
+  clearerr (fp);
+  *r_disabled = 0;
  next_line:
   do
     {
@@ -734,7 +731,7 @@ search_control_file (FILE *fp, const char *hexgrip, int *disabled)
             return gpg_error (GPG_ERR_EOF);
           return gpg_error (gpg_err_code_from_errno (errno));
         }
-      
+
       if (!*line || line[strlen(line)-1] != '\n')
         {
           /* Eat until end of line */
@@ -743,17 +740,17 @@ search_control_file (FILE *fp, const char *hexgrip, int *disabled)
           return gpg_error (*line? GPG_ERR_LINE_TOO_LONG
                                  : GPG_ERR_INCOMPLETE_LINE);
         }
-      
+
       /* Allow for empty lines and spaces */
       for (p=line; spacep (p); p++)
         ;
     }
   while (!*p || *p == '\n' || *p == '#');
-  
-  *disabled = 0;
+
+  *r_disabled = 0;
   if (*p == '!')
     {
-      *disabled = 1;
+      *r_disabled = 1;
       for (p++; spacep (p); p++)
         ;
     }
@@ -767,7 +764,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.  */
 }
@@ -786,36 +793,61 @@ 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",
                1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
                tp->tm_hour, tp->tm_min, tp->tm_sec,
                hexgrip, ttl);
-               
+
     }
   fclose (fp);
   return 0;
 }
 
 
+/* 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
 
 /*
 
-  MPI lists. 
+  MPI lists.
 
  */
 
@@ -852,7 +884,7 @@ ssh_receive_mpint_list (estream_t stream, int secret,
 
   mpis = NULL;
   err = 0;
-  
+
   if (secret)
     elems = key_spec.elems_key_secret;
   else
@@ -974,7 +1006,7 @@ ssh_signature_encoder_dsa (estream_t signature_blob, gcry_mpi_t *mpis)
          err = gpg_error (GPG_ERR_INTERNAL); /* FIXME?  */
          break;
        }
-      
+
       memset (buffer + (i * SSH_DSA_SIGNATURE_PADDING), 0,
              SSH_DSA_SIGNATURE_PADDING - data_n);
       memcpy (buffer + (i * SSH_DSA_SIGNATURE_PADDING)
@@ -995,8 +1027,8 @@ ssh_signature_encoder_dsa (estream_t signature_blob, gcry_mpi_t *mpis)
   return err;
 }
 
-/* 
-   S-Expressions. 
+/*
+   S-Expressions.
  */
 
 
@@ -1218,7 +1250,7 @@ sexp_key_extract (gcry_sexp_t sexp,
   gcry_sexp_release (value_list);
   gcry_sexp_release (value_pair);
   gcry_sexp_release (comment_list);
-  
+
   if (err)
     {
       xfree (comment_new);
@@ -1228,7 +1260,7 @@ sexp_key_extract (gcry_sexp_t sexp,
   return err;
 }
 
-/* Extract the car from SEXP, and create a newly created C-string 
+/* Extract the car from SEXP, and create a newly created C-string
    which is to be stored in IDENTIFIER.  */
 static gpg_error_t
 sexp_extract_identifier (gcry_sexp_t sexp, char **identifier)
@@ -1241,7 +1273,7 @@ sexp_extract_identifier (gcry_sexp_t sexp, char **identifier)
 
   identifier_new = NULL;
   err = 0;
-  
+
   sublist = gcry_sexp_nth (sexp, 1);
   if (! sublist)
     {
@@ -1295,7 +1327,7 @@ ssh_key_type_lookup (const char *ssh_name, const char *name,
     if ((ssh_name && (! strcmp (ssh_name, ssh_key_types[i].ssh_identifier)))
        || (name && (! strcmp (name, ssh_key_types[i].identifier))))
       break;
-  
+
   if (i == DIM (ssh_key_types))
     err = gpg_error (GPG_ERR_NOT_FOUND);
   else
@@ -1328,7 +1360,7 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
   key_type = NULL;
   comment = "";
   key = NULL;
-       
+
   err = stream_read_cstring (stream, &key_type);
   if (err)
     goto out;
@@ -1367,7 +1399,7 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
   if (key_spec)
     *key_spec = spec;
   *key_new = key;
-  
+
  out:
 
   mpint_list_free (mpi_list);
@@ -1420,7 +1452,7 @@ ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
       err = gpg_error_from_syserror ();
       goto out;
     }
-  
+
   err = es_fseek (stream, 0, SEEK_SET);
   if (err)
     goto out;
@@ -1448,7 +1480,7 @@ ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
 
   return err;
 }
-                             
+
 
 /* Write the public key KEY_PUBLIC to STREAM in SSH key format.  If
    OVERRIDE_COMMENT is not NULL, it will be used instead of the
@@ -1486,14 +1518,14 @@ ssh_send_key_public (estream_t stream, gcry_sexp_t key_public,
                                  spec.ssh_identifier, mpi_list);
   if (err)
     goto out;
-  
+
   err = stream_write_string (stream, blob, blob_n);
   if (err)
     goto out;
 
   err = stream_write_cstring (stream,
                               override_comment? override_comment : comment);
-  
+
  out:
 
   mpint_list_free (mpi_list);
@@ -1516,7 +1548,7 @@ ssh_read_key_public_from_blob (unsigned char *blob, size_t blob_size,
   gpg_error_t err;
 
   err = 0;
-  
+
   blob_stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
   if (! blob_stream)
     {
@@ -1680,7 +1712,7 @@ card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn)
       /* (Shadow)-key is not available in our key storage.  */
       unsigned char *shadow_info;
       unsigned char *tmp;
-      
+
       shadow_info = make_shadow_info (serialno, authkeyid);
       if (!shadow_info)
         {
@@ -1786,6 +1818,8 @@ ssh_handler_request_identities (ctrl_t ctrl,
   char *cardsn;
   gpg_error_t ret_err;
 
+  (void)request;
+
   /* Prepare buffer stream.  */
 
   key_directory = NULL;
@@ -1813,7 +1847,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
       goto out;
     }
   key_directory_n = strlen (key_directory);
-  
+
   key_path = xtrymalloc (key_directory_n + 46);
   if (! key_path)
     {
@@ -1845,7 +1879,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
       xfree (cardsn);
       if (err)
         goto out;
-      
+
       key_counter++;
     }
 
@@ -1875,7 +1909,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;
 
@@ -1885,7 +1919,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
           err = file_to_buffer (key_path, &buffer, &buffer_n);
           if (err)
             goto out;
-             
+
           err = gcry_sexp_sscan (&key_secret, NULL, (char*)buffer, buffer_n);
           if (err)
             goto out;
@@ -1910,7 +1944,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
 
           gcry_sexp_release (key_secret);
           key_secret = NULL;
-             
+
           err = ssh_send_key_public (key_blobs, key_public, NULL);
           if (err)
             goto out;
@@ -1921,7 +1955,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
           key_counter++;
         }
     }
-  
+
   ret = es_fseek (key_blobs, 0, SEEK_SET);
   if (ret)
     {
@@ -1964,7 +1998,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);
@@ -1972,6 +2006,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
@@ -2014,10 +2049,10 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
   *sig_n = 0;
 
   ctrl->use_auth_call = 1;
-  err = agent_pksign_do (ctrl,
+  err = agent_pksign_do (ctrl, NULL,
                          _("Please enter the passphrase "
                            "for the ssh key%0A  %c"), &signature_sexp,
-                         CACHE_MODE_SSH);
+                         CACHE_MODE_SSH, ttl_from_sshcontrol);
   ctrl->use_auth_call = 0;
   if (err)
     goto out;
@@ -2114,15 +2149,15 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
     {
       err = gpg_error_from_syserror ();
       goto out;
-    }    
+    }
 
   err = stream_read_data (stream, sig_blob, sig_blob_n);
   if (err)
     goto out;
-  
+
   *sig = sig_blob;
   *sig_n = sig_blob_n;
-  
+
  out:
 
   if (err)
@@ -2164,7 +2199,7 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
   key = NULL;
 
   /* Receive key.  */
-  
+
   err = stream_read_string (request, 0, &key_blob, &key_blob_size);
   if (err)
     goto out;
@@ -2209,7 +2244,7 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
   memcpy (ctrl->keygrip, key_grip, 20);
 
   err = data_sign (ctrl, spec.signature_encoder, &sig, &sig_n);
-  
+
  out:
 
   /* Done.  */
@@ -2229,7 +2264,7 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
       if (ret_err)
        goto leave;
     }
-  
+
  leave:
 
   gcry_sexp_release (key);
@@ -2258,7 +2293,7 @@ ssh_key_extract_comment (gcry_sexp_t key, char **comment)
       err = gpg_error (GPG_ERR_INV_SEXP);
       goto out;
     }
-  
+
   data = gcry_sexp_nth_data (comment_list, 1, &data_n);
   if (! data)
     {
@@ -2302,7 +2337,7 @@ ssh_key_to_protected_buffer (gcry_sexp_t key, const char *passphrase,
       err = gpg_error_from_syserror ();
       goto out;
     }
-  
+
   gcry_sexp_sprint (key, GCRYSEXP_FMT_CANON, buffer_new, buffer_new_n);
   /* FIXME: guarantee?  */
 
@@ -2317,6 +2352,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
@@ -2331,9 +2378,11 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
   unsigned char *buffer = NULL;
   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)
@@ -2344,7 +2393,7 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
   if ( !agent_key_available (key_grip_raw) )
     goto out; /* Yes, key is available.  */
 
-  
+
   err = ssh_key_extract_comment (key, &comment);
   if (err)
     goto out;
@@ -2361,18 +2410,38 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
     }
 
 
-  pi = gcry_calloc_secure (1, sizeof (*pi) + 100 + 1);
+  pi = gcry_calloc_secure (2, sizeof (*pi) + 100 + 1);
   if (!pi)
     {
       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;
@@ -2400,7 +2469,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;
 }
@@ -2439,7 +2508,7 @@ ssh_handler_add_identity (ctrl_t ctrl, estream_t request, estream_t response)
   unsigned char b;
   int confirm;
   int ttl;
-  
+
   confirm = 0;
   key = NULL;
   ttl = 0;
@@ -2511,11 +2580,13 @@ ssh_handler_remove_identity (ctrl_t ctrl,
   gpg_error_t ret_err;
   gpg_error_t err;
 
+  (void)ctrl;
+
   /* Receive key.  */
 
   key_blob = NULL;
   key = NULL;
-  
+
   err = stream_read_string (request, 0, &key_blob, &key_blob_size);
   if (err)
     goto out;
@@ -2523,7 +2594,7 @@ ssh_handler_remove_identity (ctrl_t ctrl,
   err = ssh_read_key_public_from_blob (key_blob, key_blob_size, &key, NULL);
   if (err)
     goto out;
-  
+
   err = ssh_identity_drop (key);
 
  out:
@@ -2549,7 +2620,7 @@ ssh_identities_remove_all (void)
 
   /* FIXME: shall we remove _all_ cache entries or only those
      registered through the ssh emulation?  */
-  
+
   return err;
 }
 
@@ -2560,7 +2631,10 @@ 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 ();
 
   if (! err)
@@ -2602,7 +2676,10 @@ 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 ();
 
   if (! err)
@@ -2619,7 +2696,10 @@ 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 +2726,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
@@ -2680,7 +2761,7 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
   /* Create memory streams for request/response data.  The entire
      request will be stored in secure memory, since it might contain
      secret key material.  The response does not have to be stored in
-     secure memory, since we never give out secret keys. 
+     secure memory, since we never give out secret keys.
 
      Note: we only have little secure memory, but there is NO
      possibility of DoS here; only trusted clients are allowed to
@@ -2821,32 +2902,45 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
 
 /* Start serving client on SOCK_CLIENT.  */
 void
-start_command_handler_ssh (ctrl_t ctrl, int sock_client)
+start_command_handler_ssh (ctrl_t ctrl, gnupg_fd_t sock_client)
 {
-  estream_t stream_sock;
-  gpg_error_t err;
+  estream_t stream_sock = NULL;
+  gpg_error_t err = 0;
   int ret;
 
-  /* Setup control structure.  */
-  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);
+  {
+    static const char *names[] =
+      {"GPG_TTY", "DISPLAY", "TERM", "XAUTHORITY", "PINENTRY_USER_DATA", NULL};
+    int idx;
+    const char *value;
+
+    for (idx=0; !err && names[idx]; idx++)
+      if (!session_env_getenv (ctrl->session_env, names[idx])
+          && (value = session_env_getenv (opt.startup_env, names[idx])))
+        err = session_env_setenv (ctrl->session_env, names[idx], value);
+
+    if (!err && !ctrl->lc_ctype && opt.startup_lc_ctype)
+      if (!(ctrl->lc_ctype = xtrystrdup (opt.startup_lc_ctype)))
+        err = gpg_error_from_syserror ();
+
+    if (!err && !ctrl->lc_messages && opt.startup_lc_messages)
+      if (!(ctrl->lc_messages = xtrystrdup (opt.startup_lc_messages)))
+        err = gpg_error_from_syserror ();
+
+    if (err)
+      {
+        log_error ("error setting default session environment: %s\n",
+                   gpg_strerror (err));
+        goto out;
+      }
+  }
 
 
   /* Create stream from socket.  */
-  stream_sock = es_fdopen (sock_client, "r+");
+  stream_sock = es_fdopen (FD2INT(sock_client), "r+");
   if (!stream_sock)
     {
       err = gpg_error_from_syserror ();
@@ -2867,7 +2961,16 @@ start_command_handler_ssh (ctrl_t ctrl, int sock_client)
 
   /* Main processing loop. */
   while ( !ssh_request_process (ctrl, stream_sock) )
-    ;
+    {
+      /* Check wether we have reached EOF before trying to read
+        another request.  */
+      int c;
+
+      c = es_fgetc (stream_sock);
+      if (c == EOF)
+        break;
+      es_ungetc (c, stream_sock);
+    }
 
   /* Reset the SCD in case it has been used. */
   agent_reset_scd (ctrl);