Updated FSF's address.
[gnupg.git] / agent / gpg-agent.c
index 4e3f0b5..fc2a200 100644 (file)
@@ -1,5 +1,6 @@
 /* gpg-agent.c  -  The GnuPG Agent
- *     Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ *     Copyright (C) 2000, 2001, 2002, 2003, 2004,
+ *                    2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +16,8 @@
  *
  * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 #include <assert.h>
 #include <time.h>
 #include <fcntl.h>
-#include <sys/socket.h>
 #include <sys/stat.h>
+#ifndef HAVE_W32_SYSTEM
+#include <sys/socket.h>
 #include <sys/un.h>
+#endif /*HAVE_W32_SYSTEM*/
 #include <unistd.h>
 #include <signal.h>
-#ifdef USE_GNU_PTH
-# include <pth.h>
-#endif
+#include <pth.h>
 
 #define JNLIB_NEED_LOG_LOGV
 #include "agent.h"
-#include <assuan.h> /* malloc hooks */
+#include <assuan.h> /* Malloc hooks */
 
 #include "i18n.h"
 #include "sysutils.h"
+#ifdef HAVE_W32_SYSTEM
+#include "../jnlib/w32-afunix.h"
+#endif
+#include "setenv.h"
 
 
 enum cmd_and_opt_values 
@@ -77,16 +83,22 @@ enum cmd_and_opt_values
   oLCctype,
   oLCmessages,
   oScdaemonProgram,
-  oDisablePth,
   oDefCacheTTL,
+  oDefCacheTTLSSH,
   oMaxCacheTTL,
+  oMaxCacheTTLSSH,
+  oUseStandardSocket,
+  oNoUseStandardSocket,
 
   oIgnoreCacheForSigning,
   oAllowMarkTrusted,
+  oAllowPresetPassphrase,
   oKeepTTY,
   oKeepDISPLAY,
-
-aTest };
+  oSSHSupport,
+  oDisableScdaemon,
+  oWriteEnvFile
+};
 
 
 
@@ -110,12 +122,15 @@ static ARGPARSE_OPTS opts[] = {
   { oNoDetach, "no-detach" ,0, N_("do not detach from the console")},
   { oNoGrab, "no-grab"     ,0, N_("do not grab keyboard and mouse")},
   { oLogFile, "log-file"   ,2, N_("use a log file for the server")},
-  { oDisablePth, "disable-pth", 0, N_("do not allow multiple connections")},
+  { oUseStandardSocket, "use-standard-socket", 0,
+                      N_("use a standard location for the socket")},
+  { oNoUseStandardSocket, "no-use-standard-socket", 0, "@"},
 
   { oPinentryProgram, "pinentry-program", 2 ,
                                N_("|PGM|use PGM as the PIN-Entry program") },
   { oScdaemonProgram, "scdaemon-program", 2 ,
                                N_("|PGM|use PGM as the SCdaemon program") },
+  { oDisableScdaemon, "disable-scdaemon", 0, N_("do not use the SCdaemon") },
 
   { oDisplay,    "display",     2, "@" },
   { oTTYname,    "ttyname",     2, "@" },
@@ -128,19 +143,26 @@ static ARGPARSE_OPTS opts[] = {
 
   { oDefCacheTTL, "default-cache-ttl", 4,
                                N_("|N|expire cached PINs after N seconds")},
+  { oDefCacheTTLSSH, "default-cache-ttl-ssh", 4, "@" },
   { oMaxCacheTTL, "max-cache-ttl", 4, "@" },
+  { oMaxCacheTTLSSH, "max-cache-ttl-ssh", 4, "@" },
   { oIgnoreCacheForSigning, "ignore-cache-for-signing", 0,
                                N_("do not use the PIN cache when signing")},
   { oAllowMarkTrusted, "allow-mark-trusted", 0,
                              N_("allow clients to mark keys as \"trusted\"")},
+  { oAllowPresetPassphrase, "allow-preset-passphrase", 0,
+                             N_("allow presetting passphrase")},
+  { oSSHSupport, "enable-ssh-support", 0, N_("enable ssh-agent emulation") },
+  { oWriteEnvFile, "write-env-file", 2|8,
+            N_("|FILE|write environment settings also to FILE")},
   {0}
 };
 
 
-#define DEFAULT_CACHE_TTL (10*60)  /* 10 minutes */
-#define MAX_CACHE_TTL     (120*60) /* 2 hours */
+#define DEFAULT_CACHE_TTL     (10*60)  /* 10 minutes */
+#define DEFAULT_CACHE_TTL_SSH (30*60)  /* 30 minutes */
+#define MAX_CACHE_TTL         (120*60) /* 2 hours */
 
-static volatile int caught_fatal_sig = 0;
 
 /* flag to indicate that a shutdown was requested */
 static int shutdown_pending;
@@ -149,8 +171,11 @@ static int shutdown_pending;
 /* It is possible that we are currently running under setuid permissions */
 static int maybe_setuid = 1;
 
-/* Name of the communication socket */
-static char socket_name[128];
+/* Name of the communication socket used for native gpg-agent requests.  */
+static char *socket_name;
+
+/* Name of the communication socket used for ssh-agent-emulation.  */
+static char *socket_name_ssh;
 
 /* Default values for options passed to the pinentry. */
 static char *default_display;
@@ -166,21 +191,37 @@ static char *config_filename;
 static const char *debug_level;
 
 /* Keep track of the current log file so that we can avoid updating
-   the log file afte a SIGHUP if id didn't changed. Malloced. */
+   the log file after a SIGHUP if it didn't changed. Malloced. */
 static char *current_logfile;
 
-/* Local prototypes. */
+/* The handle_tick() function may test whether a parent is still
+   running.  We record the PID of the parent here or -1 if it should be
+   watched. */
+static pid_t parent_pid = (pid_t)(-1);
+
+\f
+/*
+   Local prototypes. 
+ */
+
+static char *create_socket_name (int use_standard_socket,
+                                 char *standard_name, char *template);
+static int create_server_socket (int is_standard_name, const char *name);
 static void create_directories (void);
-#ifdef USE_GNU_PTH
-static void handle_connections (int listen_fd);
+
+static void handle_connections (int listen_fd, int listen_fd_ssh);
+static int check_for_running_agent (int);
 
 /* Pth wrapper function definitions. */
 GCRY_THREAD_OPTION_PTH_IMPL;
 
-#endif /*USE_GNU_PTH*/
-static void check_for_running_agent (void);
 
 
+\f
+/*
+   Functions. 
+ */
+
 
 static const char *
 my_strusage (int level)
@@ -244,12 +285,12 @@ my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
 }
 
 
-/* Setup the debugging.  With a LEVEL of NULL only the active debug
-   flags are propagated to the subsystems.  With LEVEL set, a specific
-   set of debug flags is set; thus overriding all flags already
-   set. Note that we don't fail here, because it is important to keep
-   gpg-agent running even after re-reading the options due to a
-   SIGHUP. */
+/* Setup the debugging.  With the global variable DEBUG_LEVEL set to NULL
+   only the active debug flags are propagated to the subsystems.  With
+   DEBUG_LEVEL set, a specific set of debug flags is set; thus overriding
+   all flags already set. Note that we don't fail here, because it is
+   important to keep gpg-agent running even after re-reading the
+   options due to a SIGHUP. */
 static void
 set_debug (void)
 {
@@ -286,49 +327,35 @@ set_debug (void)
 }
  
 
+/* Helper for cleanup to remove one socket with NAME.  */
 static void
-cleanup (void)
+remove_socket (char *name)
 {
-  if (*socket_name)
+  if (name && *name)
     {
       char *p;
 
-      remove (socket_name);
-      p = strrchr (socket_name, '/');
+      remove (name);
+      p = strrchr (name, '/');
       if (p)
-        {
-          *p = 0;
-          rmdir (socket_name);
-          *p = '/';
-        }
-      *socket_name = 0;
+       {
+         *p = 0;
+         rmdir (name);
+         *p = '/';
+       }
+      *name = 0;
     }
-}
+}  
 
-
-static RETSIGTYPE
-cleanup_sh (int sig)
+static void
+cleanup (void)
 {
-  if (caught_fatal_sig)
-    raise (sig);
-  caught_fatal_sig = 1;
-
-  /* gcry_control( GCRYCTL_TERM_SECMEM );*/
-  cleanup ();
-
-#ifndef HAVE_DOSISH_SYSTEM
-  {    /* reset action to default action and raise signal again */
-    struct sigaction nact;
-    nact.sa_handler = SIG_DFL;
-    sigemptyset( &nact.sa_mask );
-    nact.sa_flags = 0;
-    sigaction( sig, &nact, NULL);
-  }
-#endif
-  raise( sig );
+  remove_socket (socket_name);
+  remove_socket (socket_name_ssh);
 }
 
 
+
 /* Handle options which are allowed to be reset after program start.
    Return true when the current option in PARGS could be handled and
    false if not.  As a special feature, passing a value of NULL for
@@ -346,9 +373,12 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
       opt.pinentry_program = NULL;
       opt.scdaemon_program = NULL;
       opt.def_cache_ttl = DEFAULT_CACHE_TTL;
+      opt.def_cache_ttl_ssh = DEFAULT_CACHE_TTL_SSH;
       opt.max_cache_ttl = MAX_CACHE_TTL;
+      opt.max_cache_ttl_ssh = MAX_CACHE_TTL;
       opt.ignore_cache_for_signing = 0;
       opt.allow_mark_trusted = 0;
+      opt.disable_scdaemon = 0;
       return 1;
     }
 
@@ -362,9 +392,10 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
     case oDebugLevel: debug_level = pargs->r.ret_str; break;
 
     case oLogFile:
-      if (reread 
-          && (!current_logfile || !pargs->r.ret_str
-              || strcmp (current_logfile, pargs->r.ret_str)))
+      if (!reread)
+        return 0; /* not handeld */
+      if (!current_logfile || !pargs->r.ret_str
+          || strcmp (current_logfile, pargs->r.ret_str))
         {
           log_set_file (pargs->r.ret_str);
           xfree (current_logfile);
@@ -376,17 +407,23 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
       
     case oPinentryProgram: opt.pinentry_program = pargs->r.ret_str; break;
     case oScdaemonProgram: opt.scdaemon_program = pargs->r.ret_str; break;
+    case oDisableScdaemon: opt.disable_scdaemon = 1; break;
 
     case oDefCacheTTL: opt.def_cache_ttl = pargs->r.ret_ulong; break;
+    case oDefCacheTTLSSH: opt.def_cache_ttl_ssh = pargs->r.ret_ulong; break;
     case oMaxCacheTTL: opt.max_cache_ttl = pargs->r.ret_ulong; break;
+    case oMaxCacheTTLSSH: opt.max_cache_ttl_ssh = pargs->r.ret_ulong; break;
       
     case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break;
 
     case oAllowMarkTrusted: opt.allow_mark_trusted = 1; break;
 
+    case oAllowPresetPassphrase: opt.allow_preset_passphrase = 1; break;
+
     default:
       return 0; /* not handled */
     }
+
   return 1; /* handled */
 }
 
@@ -412,9 +449,10 @@ main (int argc, char **argv )
   int csh_style = 0;
   char *logfile = NULL;
   int debug_wait = 0;
-  int disable_pth = 0;
   int gpgconf_list = 0;
+  int standard_socket = 0;
   gpg_error_t err;
+  const char *env_file_name = NULL;
 
   set_strusage (my_strusage);
   gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
@@ -430,14 +468,13 @@ main (int argc, char **argv )
 
   /* Libgcrypt requires us to register the threading model first.
      Note that this will also do the pth_init. */
-#ifdef USE_GNU_PTH
   err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth);
   if (err)
     {
       log_fatal ("can't register GNU Pth with Libgcrypt: %s\n",
                  gpg_strerror (err));
     }
-#endif /*USE_GNU_PTH*/
+
 
   /* Check that the libraries are suitable.  Do it here because
      the option parsing may need services of the library. */
@@ -456,18 +493,38 @@ main (int argc, char **argv )
 
   may_coredump = disable_core_dumps ();
 
+  /* Set default options.  */
   parse_rereadable_options (NULL, 0); /* Reset them to default values. */
-
+#ifdef HAVE_W32_SYSTEM
+  standard_socket = 1;  /* Under Windows we always use a standard
+                           socket.  */
+#endif
+  
   shell = getenv ("SHELL");
   if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") )
     csh_style = 1;
-  
-  opt.homedir = getenv("GNUPGHOME");
-  if (!opt.homedir || !*opt.homedir)
-    opt.homedir = GNUPG_DEFAULT_HOMEDIR;
 
-
-  /* check whether we have a config file on the commandline */
+  opt.homedir = default_homedir ();
+
+  /* Record some of the original environment strings. */
+  opt.startup_display = getenv ("DISPLAY");
+  if (opt.startup_display)
+    opt.startup_display = xstrdup (opt.startup_display);
+  opt.startup_ttyname = ttyname (0);
+  if (opt.startup_ttyname)
+    opt.startup_ttyname = xstrdup (opt.startup_ttyname);
+  opt.startup_ttytype = getenv ("TERM");
+  if (opt.startup_ttytype)
+    opt.startup_ttytype = xstrdup (opt.startup_ttytype);
+  /* Fixme: Better use the locale function here.  */
+  opt.startup_lc_ctype = getenv ("LC_CTYPE");
+  if (opt.startup_lc_ctype) 
+    opt.startup_lc_ctype = xstrdup (opt.startup_lc_ctype);
+  opt.startup_lc_messages = getenv ("LC_MESSAGES");
+  if (opt.startup_lc_messages)
+    opt.startup_lc_messages = xstrdup (opt.startup_lc_messages);
+
+  /* Check whether we have a config file on the commandline */
   orig_argc = argc;
   orig_argv = argv;
   pargs.argc = &argc;
@@ -497,7 +554,6 @@ main (int argc, char **argv )
      Now we are now working under our real uid 
   */
 
-
   if (default_config)
     configname = make_filename (opt.homedir, "gpg-agent.conf", NULL );
   
@@ -563,7 +619,6 @@ main (int argc, char **argv )
         case oSh: csh_style = 0; break;
         case oServer: pipe_server = 1; break;
         case oDaemon: is_daemon = 1; break;
-        case oDisablePth: disable_pth = 1; break;
 
         case oDisplay: default_display = xstrdup (pargs.r.ret_str); break;
         case oTTYname: default_ttyname = xstrdup (pargs.r.ret_str); break;
@@ -572,9 +627,20 @@ main (int argc, char **argv )
         case oLCmessages: default_lc_messages = xstrdup (pargs.r.ret_str);
           break;
 
+        case oUseStandardSocket: standard_socket = 1; break;
+        case oNoUseStandardSocket: standard_socket = 0; break;
+
         case oKeepTTY: opt.keep_tty = 1; break;
         case oKeepDISPLAY: opt.keep_display = 1; break;
 
+       case oSSHSupport:  opt.ssh_support = 1; break;
+        case oWriteEnvFile:
+          if (pargs.r_type)
+            env_file_name = pargs.r.ret_str;
+          else
+            env_file_name = make_filename ("~/.gpg-agent-info", NULL);
+          break;
+
         default : pargs.err = configfp? 1:2; break;
        }
     }
@@ -615,6 +681,10 @@ main (int argc, char **argv )
       exit (1);
     }
 
+  initialize_module_query ();
+  initialize_module_call_scd ();
+  
+  /* Try to create missing directories. */
   create_directories ();
 
   if (debug_wait && pipe_server)
@@ -669,6 +739,8 @@ main (int argc, char **argv )
               GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
       printf ("allow-mark-trusted:%lu:\n",
               GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
+      printf ("disable-scdaemon:%lu:\n",
+              GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
 
       agent_exit (0);
     }
@@ -679,7 +751,7 @@ main (int argc, char **argv )
   if (!pipe_server && !is_daemon)
     {
       log_set_prefix (NULL, JNLIB_LOG_WITH_PREFIX); 
-      check_for_running_agent ();
+      check_for_running_agent (0);
       agent_exit (0);
     }
   
@@ -710,6 +782,7 @@ main (int argc, char **argv )
   if (!default_ttytype && getenv ("TERM"))
     default_ttytype = xstrdup (getenv ("TERM"));
 
+
   if (pipe_server)
     { /* this is the simple pipe based server */
       start_command_handler (-1, -1);
@@ -719,79 +792,47 @@ main (int argc, char **argv )
   else
     { /* Regular server mode */
       int fd;
+      int fd_ssh;
       pid_t pid;
-      int len;
-      struct sockaddr_un serv_addr;
-      char *p;
 
       /* Remove the DISPLAY variable so that a pinentry does not
          default to a specific display.  There is still a default
-         display when gpg-agent weas started using --display or a
-         client requested this using an OPTION command. */
-      if (!opt.keep_display)
+         display when gpg-agent was started using --display or a
+         client requested this using an OPTION command.  Note, that we
+         don't do this when running in reverse daemon mode (i.e. when
+         exec the program given as arguments). */
+#ifndef HAVE_W32_SYSTEM
+      if (!opt.keep_display && !argc)
         unsetenv ("DISPLAY");
+#endif
 
-      *socket_name = 0;
-      snprintf (socket_name, DIM(socket_name)-1,
-                "/tmp/gpg-XXXXXX/S.gpg-agent");
-      socket_name[DIM(socket_name)-1] = 0;
-      p = strrchr (socket_name, '/');
-      if (!p)
-        BUG ();
-      *p = 0;;
-      if (!mkdtemp(socket_name))
-        {
-          log_error ("can't create directory `%s': %s\n",
-                    socket_name, strerror(errno) );
-          exit (1);
-        }
-      *p = '/';
-
-      if (strchr (socket_name, ':') )
-        {
-          log_error ("colons are not allowed in the socket name\n");
-          exit (1);
-        }
-      if (strlen (socket_name)+1 >= sizeof serv_addr.sun_path ) 
-        {
-          log_error ("name of socket too long\n");
-          exit (1);
-        }
-   
-
-      fd = socket (AF_UNIX, SOCK_STREAM, 0);
-      if (fd == -1)
-        {
-          log_error ("can't create socket: %s\n", strerror(errno) );
-          exit (1);
-        }
 
-      memset (&serv_addr, 0, sizeof serv_addr);
-      serv_addr.sun_family = AF_UNIX;
-      strcpy (serv_addr.sun_path, socket_name);
-      len = (offsetof (struct sockaddr_un, sun_path)
-             + strlen(serv_addr.sun_path) + 1);
+      /* Create the sockets.  */
+      socket_name = create_socket_name (standard_socket,
+                                        "S.gpg-agent",
+                                        "/tmp/gpg-XXXXXX/S.gpg-agent");
+      if (opt.ssh_support)
+       socket_name_ssh = create_socket_name (standard_socket, 
+                                            "S.gpg-agent.ssh",
+                                            "/tmp/gpg-XXXXXX/S.gpg-agent.ssh");
 
-      if (bind (fd, (struct sockaddr*)&serv_addr, len) == -1)
-        {
-          log_error ("error binding socket to `%s': %s\n",
-                     serv_addr.sun_path, strerror (errno) );
-          close (fd);
-          exit (1);
-        }
-  
-      if (listen (fd, 5 ) == -1)
-        {
-          log_error ("listen() failed: %s\n", strerror (errno));
-          close (fd);
-          exit (1);
-        }
-
-      if (opt.verbose)
-        log_info ("listening on socket `%s'\n", socket_name );
+      fd = create_server_socket (standard_socket, socket_name);
+      if (opt.ssh_support)
+       fd_ssh = create_server_socket (standard_socket, socket_name_ssh);
+      else
+       fd_ssh = -1;
 
+      /* If we are going to exec a program in the parent, we record
+         the PID, so that the child may check whether the program is
+         still alive. */
+      if (argc)
+        parent_pid = getpid ();
 
       fflush (NULL);
+#ifdef HAVE_W32_SYSTEM
+      pid = getpid ();
+      printf ("set GPG_AGENT_INFO=%s;%lu;1\n", socket_name, (ulong)pid);
+#else /*!HAVE_W32_SYSTEM*/
       pid = fork ();
       if (pid == (pid_t)-1) 
         {
@@ -800,11 +841,11 @@ main (int argc, char **argv )
         }
       else if (pid) 
         { /* We are the parent */
-          char *infostr;
+          char *infostr, *infostr_ssh_sock, *infostr_ssh_pid;
           
           close (fd);
           
-          /* create the info string: <name>:<pid>:<protocol_version> */
+          /* Create the info string: <name>:<pid>:<protocol_version> */
           if (asprintf (&infostr, "GPG_AGENT_INFO=%s:%lu:1",
                         socket_name, (ulong)pid ) < 0)
             {
@@ -812,10 +853,55 @@ main (int argc, char **argv )
               kill (pid, SIGTERM);
               exit (1);
             }
-          *socket_name = 0; /* don't let cleanup() remove the socket -
+         if (opt.ssh_support)
+           {
+             if (asprintf (&infostr_ssh_sock, "SSH_AUTH_SOCK=%s",
+                           socket_name_ssh) < 0)
+               {
+                 log_error ("out of core\n");
+                 kill (pid, SIGTERM);
+                 exit (1);
+               }
+             if (asprintf (&infostr_ssh_pid, "SSH_AGENT_PID=%u",
+                           pid) < 0)
+               {
+                 log_error ("out of core\n");
+                 kill (pid, SIGTERM);
+                 exit (1);
+               }
+           }
+
+          *socket_name = 0; /* Don't let cleanup() remove the socket -
                                the child should do this from now on */
+         if (opt.ssh_support)
+           *socket_name_ssh = 0;
+
+          if (env_file_name)
+            {
+              FILE *fp;
+              
+              fp = fopen (env_file_name, "w");
+              if (!fp)
+                log_error (_("error creating `%s': %s\n"),
+                             env_file_name, strerror (errno));
+              else
+                {
+                  fputs (infostr, fp);
+                  putc ('\n', fp);
+                  if (opt.ssh_support)
+                    {
+                      fputs (infostr_ssh_sock, fp);
+                      putc ('\n', fp);
+                      fputs (infostr_ssh_pid, fp);
+                      putc ('\n', fp);
+                    }
+                  fclose (fp);
+                }
+            }
+
+
           if (argc) 
-            { /* run the program given on the commandline */
+            { /* Run the program given on the commandline.  */
               if (putenv (infostr))
                 {
                   log_error ("failed to set environment: %s\n",
@@ -823,6 +909,20 @@ main (int argc, char **argv )
                   kill (pid, SIGTERM );
                   exit (1);
                 }
+              if (opt.ssh_support && putenv (infostr_ssh_sock))
+                {
+                  log_error ("failed to set environment: %s\n",
+                             strerror (errno) );
+                  kill (pid, SIGTERM );
+                  exit (1);
+                }
+              if (opt.ssh_support && putenv (infostr_ssh_pid))
+                {
+                  log_error ("failed to set environment: %s\n",
+                             strerror (errno) );
+                  kill (pid, SIGTERM );
+                  exit (1);
+                }
               execvp (argv[0], argv);
               log_error ("failed to run the command: %s\n", strerror (errno));
               kill (pid, SIGTERM);
@@ -830,23 +930,39 @@ main (int argc, char **argv )
             }
           else
             {
-              /* print the environment string, so that the caller can use
+              /* Print the environment string, so that the caller can use
                  shell's eval to set it */
               if (csh_style)
                 {
                   *strchr (infostr, '=') = ' ';
-                  printf ( "setenv %s\n", infostr);
+                  printf ("setenv %s\n", infostr);
+                 if (opt.ssh_support)
+                   {
+                     *strchr (infostr_ssh_sock, '=') = ' ';
+                     printf ("setenv %s\n", infostr_ssh_sock);
+                     *strchr (infostr_ssh_pid, '=') = ' ';
+                     printf ("setenv %s\n", infostr_ssh_pid);
+                   }
                 }
               else
                 {
                   printf ( "%s; export GPG_AGENT_INFO;\n", infostr);
+                 if (opt.ssh_support)
+                   {
+                     printf ("%s; export SSH_AUTH_SOCK;\n", infostr_ssh_sock);
+                     printf ("%s; export SSH_AGENT_PID;\n", infostr_ssh_pid);
+                   }
                 }
-              free (infostr);
+              free (infostr); /* (Note that a vanilla free is here correct.) */
+             if (opt.ssh_support)
+               {
+                 free (infostr_ssh_sock);
+                 free (infostr_ssh_pid);
+               }
               exit (0); 
             }
-          /*NEVER REACHED*/
-        } /* end parent */
-      
+          /*NOTREACHED*/
+        } /* End parent */
 
       /* 
          This is the child
@@ -861,7 +977,7 @@ main (int argc, char **argv )
           /* Close stdin, stdout and stderr unless it is the log stream */
           for (i=0; i <= 2; i++) 
             {
-              if (!log_test_fd (i) )
+              if (!log_test_fd (i) && i != fd )
                 close (i);
             }
           if (setsid() == -1)
@@ -882,40 +998,17 @@ main (int argc, char **argv )
           exit (1);
         }
 
-
-#ifdef USE_GNU_PTH
-      if (!disable_pth)
-        {
-         struct sigaction sa;
-
-         sa.sa_handler = SIG_IGN;
-         sigemptyset (&sa.sa_mask);
-         sa.sa_flags = 0;
-         sigaction (SIGPIPE, &sa, NULL);
-          handle_connections (fd);
-        }
-      else
-#endif /*!USE_GNU_PTH*/
-      /* setup signals */
-        {
-          struct sigaction oact, nact;
-          
-          nact.sa_handler = cleanup_sh;
-          sigemptyset (&nact.sa_mask);
-          nact.sa_flags = 0;
-          
-          sigaction (SIGHUP, NULL, &oact);
-          if (oact.sa_handler != SIG_IGN)
-            sigaction (SIGHUP, &nact, NULL);
-          sigaction( SIGTERM, NULL, &oact );
-          if (oact.sa_handler != SIG_IGN)
-            sigaction (SIGTERM, &nact, NULL);
-          nact.sa_handler = SIG_IGN;
-          sigaction (SIGPIPE, &nact, NULL);
-          sigaction (SIGINT, &nact, NULL);
-
-          start_command_handler (fd, -1);
-        }
+      {
+        struct sigaction sa;
+        
+        sa.sa_handler = SIG_IGN;
+        sigemptyset (&sa.sa_mask);
+        sa.sa_flags = 0;
+        sigaction (SIGPIPE, &sa, NULL);
+      }
+#endif /*!HAVE_W32_SYSTEM*/
+
+      handle_connections (fd, opt.ssh_support ? fd_ssh : -1);
       close (fd);
     }
   
@@ -1018,6 +1111,125 @@ reread_configuration (void)
 }
 
 
+
+
+/* Create a name for the socket.  With USE_STANDARD_SOCKET given as
+   true using STANDARD_NAME in the home directory or if given has
+   false from the mkdir type name TEMPLATE.  In the latter case a
+   unique name in a unique new directory will be created.  In both
+   cases check for valid characters as well as against a maximum
+   allowed length for a unix domain socket is done.  The function
+   terminates the process in case of an error.  Retunrs: Pointer to an
+   allcoated string with the absolute name of the socket used.  */
+static char *
+create_socket_name (int use_standard_socket,
+                   char *standard_name, char *template)
+{
+  char *name, *p;
+
+  if (use_standard_socket)
+    name = make_filename (opt.homedir, standard_name, NULL);
+  else
+    {
+      name = xstrdup (template);
+      p = strrchr (name, '/');
+      if (!p)
+       BUG ();
+      *p = 0;
+      if (!mkdtemp (name))
+       {
+         log_error (_("can't create directory `%s': %s\n"),
+                    name, strerror (errno));
+         agent_exit (2);
+       }
+      *p = '/';
+    }
+
+  if (strchr (name, PATHSEP_C))
+    {
+      log_error (("`%s' are not allowed in the socket name\n"), PATHSEP_S);
+      agent_exit (2);
+    }
+  if (strlen (name) + 1 >= DIMof (struct sockaddr_un, sun_path) )
+    {
+      log_error (_("name of socket too long\n"));
+      agent_exit (2);
+    }
+  return name;
+}
+
+
+
+/* Create a Unix domain socket with NAME.  IS_STANDARD_NAME indicates
+   whether a non-random socket is used.  Returns the filedescriptor or
+   terminates the process in case of an error. */
+static int
+create_server_socket (int is_standard_name, const char *name)
+{
+  struct sockaddr_un *serv_addr;
+  socklen_t len;
+  int fd;
+  int rc;
+
+#ifdef HAVE_W32_SYSTEM
+  fd = _w32_sock_new (AF_UNIX, SOCK_STREAM, 0);
+#else
+  fd = socket (AF_UNIX, SOCK_STREAM, 0);
+#endif
+  if (fd == -1)
+    {
+      log_error (_("can't create socket: %s\n"), strerror (errno));
+      agent_exit (2);
+    }
+
+  serv_addr = xmalloc (sizeof (*serv_addr)); 
+  memset (serv_addr, 0, sizeof *serv_addr);
+  serv_addr->sun_family = AF_UNIX;
+  assert (strlen (name) + 1 < sizeof (serv_addr->sun_path));
+  strcpy (serv_addr->sun_path, name);
+  len = (offsetof (struct sockaddr_un, sun_path)
+        + strlen (serv_addr->sun_path) + 1);
+
+#ifdef HAVE_W32_SYSTEM
+  rc = _w32_sock_bind (fd, (struct sockaddr*) serv_addr, len);
+  if (is_standard_name && rc == -1 )
+    {
+      remove (name);
+      rc = bind (fd, (struct sockaddr*) serv_addr, len);
+    }
+#else
+  rc = bind (fd, (struct sockaddr*) serv_addr, len);
+  if (is_standard_name && rc == -1 && errno == EADDRINUSE)
+    {
+      remove (name);
+      rc = bind (fd, (struct sockaddr*) serv_addr, len);
+    }
+#endif
+  if (rc == -1)
+    {
+      log_error (_("error binding socket to `%s': %s\n"),
+                serv_addr->sun_path, strerror (errno));
+      close (fd);
+      agent_exit (2);
+    }
+
+  if (listen (fd, 5 ) == -1)
+    {
+      log_error (_("listen() failed: %s\n"), strerror (errno));
+      close (fd);
+      agent_exit (2);
+    }
+          
+  if (opt.verbose)
+    log_info (_("listening on socket `%s'\n"), serv_addr->sun_path);
+
+  return fd;
+}
+
+
+/* Check that the directory for storing the private keys exists and
+   create it if not.  This function won't fail as it is only a
+   convenience function and not strictly necessary.  */
 static void
 create_private_keys_directory (const char *home)
 {
@@ -1027,9 +1239,15 @@ create_private_keys_directory (const char *home)
   fname = make_filename (home, GNUPG_PRIVATE_KEYS_DIR, NULL);
   if (stat (fname, &statbuf) && errno == ENOENT)
     {
+#ifdef HAVE_W32_SYSTEM  /*FIXME: Setup proper permissions.  */
+      if (!CreateDirectory (fname, NULL))
+        log_error (_("can't create directory `%s': %s\n"),
+                   fname, w32_strerror (-1) );
+#else
       if (mkdir (fname, S_IRUSR|S_IWUSR|S_IXUSR ))
         log_error (_("can't create directory `%s': %s\n"),
-                   fname,      strerror(errno) );
+                   fname, strerror (errno) );
+#endif
       else if (!opt.quiet)
         log_info (_("directory `%s' created\n"), fname);
     }
@@ -1049,7 +1267,7 @@ create_directories (void)
   const char *defhome = GNUPG_DEFAULT_HOMEDIR;
   char *home;
 
-  home  = make_filename (opt.homedir, NULL);
+  home = make_filename (opt.homedir, NULL);
   if ( stat (home, &statbuf) )
     {
       if (errno == ENOENT)
@@ -1061,9 +1279,15 @@ create_directories (void)
                || (*defhome != '~' && !strcmp (home, defhome) )
                )
             {
+#ifdef HAVE_W32_SYSTEM
+              if (!CreateDirectory (home, NULL))
+                log_error (_("can't create directory `%s': %s\n"),
+                           home, w32_strerror (-1) );
+#else
               if (mkdir (home, S_IRUSR|S_IWUSR|S_IXUSR ))
                 log_error (_("can't create directory `%s': %s\n"),
-                           home, strerror(errno) );
+                           home, strerror (errno) );
+#endif
               else 
                 {
                   if (!opt.quiet)
@@ -1073,11 +1297,11 @@ create_directories (void)
             }
         }
       else
-        log_error ("error stat-ing `%s': %s\n", home, strerror (errno));
+        log_error (_("stat() failed for `%s': %s\n"), home, strerror (errno));
     }
   else if ( !S_ISDIR(statbuf.st_mode))
     {
-      log_error ("can't use `%s' as home directory\n", home);
+      log_error (_("can't use `%s' as home directory\n"), home);
     }
   else /* exists and is a directory. */
     {
@@ -1088,12 +1312,38 @@ create_directories (void)
 
 
 
-#ifdef USE_GNU_PTH
+/* This is the worker for the ticker.  It is called every few seconds
+   and may only do fast operations. */
+static void
+handle_tick (void)
+{
+  /* Check whether the scdaemon has died and cleanup in this case. */
+  agent_scd_check_aliveness ();
+
+  /* If we are running as a child of another process, check whether
+     the parent is still alive and shutdown if not. */
+#ifndef HAVE_W32_SYSTEM
+  if (parent_pid != (pid_t)(-1))
+    {
+      if (kill (parent_pid, 0))
+        {
+          shutdown_pending = 2;
+          log_info ("parent process died - shutting down\n");
+          log_info ("%s %s stopped\n", strusage(11), strusage(13) );
+          cleanup ();
+          agent_exit (0);
+        }
+    }
+#endif /*HAVE_W32_SYSTEM*/
+}
+
+
 static void
 handle_signal (int signo)
 {
   switch (signo)
     {
+#ifndef HAVE_W32_SYSTEM
     case SIGHUP:
       log_info ("SIGHUP received - "
                 "re-reading configuration and flushing cache\n");
@@ -1103,7 +1353,10 @@ handle_signal (int signo)
       break;
       
     case SIGUSR1:
-      log_info ("SIGUSR1 received - no action defined\n");
+      log_info ("SIGUSR1 received - printing internal information:\n");
+      pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ());
+      agent_query_dump_state ();
+      agent_scd_dump_state ();
       break;
       
     case SIGUSR2:
@@ -1132,60 +1385,102 @@ handle_signal (int signo)
       cleanup ();
       agent_exit (0);
       break;
-
+#endif
     default:
       log_info ("signal %d received - no action defined\n", signo);
     }
 }
 
 
+/* This is the standard connection thread's main function.  */
 static void *
 start_connection_thread (void *arg)
 {
   int fd = (int)arg;
 
   if (opt.verbose)
-    log_info ("handler for fd %d started\n", fd);
+    log_info (_("handler 0x%lx for fd %d started\n"), 
+              (long)pth_self (), fd);
 
   /* FIXME: Move this housekeeping into a ticker function.  Calling it
      for each connection should work but won't work anymore if our
-     cleints start to keep connections. */
+     clients start to keep connections. */
   agent_trustlist_housekeeping ();
 
   start_command_handler (-1, fd);
   if (opt.verbose)
-    log_info ("handler for fd %d terminated\n", fd);
+    log_info (_("handler 0x%lx for fd %d terminated\n"), 
+              (long)pth_self (), fd);
   
   return NULL;
 }
 
 
+/* This is the ssh connection thread's main function.  */
+static void *
+start_connection_thread_ssh (void *arg)
+{
+  int fd = (int)arg;
+
+  if (opt.verbose)
+    log_info (_("ssh handler 0x%lx for fd %d started\n"),
+              (long)pth_self (), fd);
+
+  agent_trustlist_housekeeping ();
+
+  start_command_handler_ssh (fd);
+  if (opt.verbose)
+    log_info (_("ssh handler 0x%lx for fd %d terminated\n"),
+              (long)pth_self (), fd);
+  
+  return NULL;
+}
+
+
+/* Connection handler loop.  Wait for coecntion requests and spawn a
+   thread after accepting a connection.  */
 static void
-handle_connections (int listen_fd)
+handle_connections (int listen_fd, int listen_fd_ssh)
 {
   pth_attr_t tattr;
-  pth_event_t ev;
+  pth_event_t ev, time_ev;
   sigset_t sigs;
   int signo;
   struct sockaddr_un paddr;
-  socklen_t plen = sizeof( paddr );
+  socklen_t plen;
+  fd_set fdset, read_fdset;
+  int ret;
   int fd;
 
   tattr = pth_attr_new();
   pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
   pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 256*1024);
-  pth_attr_set (tattr, PTH_ATTR_NAME, "gpg-agent");
 
+#ifndef HAVE_W32_SYSTEM /* fixme */
+  /* Make sure that the signals we are going to handle are not blocked
+     and create an event object for them. */
   sigemptyset (&sigs );
   sigaddset (&sigs, SIGHUP);
   sigaddset (&sigs, SIGUSR1);
   sigaddset (&sigs, SIGUSR2);
   sigaddset (&sigs, SIGINT);
   sigaddset (&sigs, SIGTERM);
+  pth_sigmask (SIG_UNBLOCK, &sigs, NULL);
   ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo);
+#else
+  ev = NULL;
+#endif
+  time_ev = NULL;
+
+  FD_ZERO (&fdset);
+  FD_SET (listen_fd, &fdset);
+  if (listen_fd_ssh != -1)
+    FD_SET (listen_fd_ssh, &fdset);
 
   for (;;)
     {
+      sigset_t oldsigs;
+
       if (shutdown_pending)
         {
           if (pth_ctrl (PTH_CTRL_GETTHREADS) == 1)
@@ -1200,86 +1495,191 @@ handle_connections (int listen_fd)
           continue;
        }
 
-      fd = pth_accept_ev (listen_fd, (struct sockaddr *)&paddr, &plen, ev);
-      if (fd == -1)
-        {
-#ifdef PTH_STATUS_OCCURRED     /* This is Pth 2 */
-          if (pth_event_status (ev) == PTH_STATUS_OCCURRED)
-#else
-          if (pth_event_occurred (ev))
-#endif
+      /* Create a timeout event if needed. */
+      if (!time_ev)
+        time_ev = pth_event (PTH_EVENT_TIME, pth_timeout (2, 0));
+
+      /* POSIX says that fd_set should be implemented as a structure,
+         thus a simple assignment is fine to copy the entire set.  */
+      read_fdset = fdset;
+
+      if (time_ev)
+        pth_event_concat (ev, time_ev, NULL);
+      ret = pth_select_ev (FD_SETSIZE, &read_fdset, NULL, NULL, NULL, ev);
+      if (time_ev)
+        pth_event_isolate (time_ev);
+
+      if (ret == -1)
+       {
+          if (pth_event_occurred (ev)
+              || (time_ev && pth_event_occurred (time_ev)))
             {
-              handle_signal (signo);
+              if (pth_event_occurred (ev))
+                handle_signal (signo);
+              if (time_ev && pth_event_occurred (time_ev))
+                {
+                  pth_event_free (time_ev, PTH_FREE_ALL);
+                  time_ev = NULL;
+                  handle_tick ();
+                }
               continue;
-           }
-          log_error ("accept failed: %s - waiting 1s\n", strerror (errno));
-          pth_sleep(1);
-          continue;
+            }
+          log_error (_("pth_select failed: %s - waiting 1s\n"),
+                     strerror (errno));
+          pth_sleep (1);
+         continue;
        }
 
-      if (!pth_spawn (tattr, start_connection_thread, (void*)fd))
+      if (pth_event_occurred (ev))
         {
-          log_error ("error spawning connection handler: %s\n",
-                     strerror (errno) );
-          close (fd);
+          handle_signal (signo);
+        }
+
+      if (time_ev && pth_event_occurred (time_ev))
+        {
+          pth_event_free (time_ev, PTH_FREE_ALL);
+          time_ev = NULL;
+          handle_tick ();
+        }
+
+      
+      /* We now might create new threads and because we don't want any
+         signals - we are handling here - to be delivered to a new
+         thread. Thus we need to block those signals. */
+      pth_sigmask (SIG_BLOCK, &sigs, &oldsigs);
+
+      if (FD_ISSET (listen_fd, &read_fdset))
+       {
+          plen = sizeof paddr;
+         fd = pth_accept (listen_fd, (struct sockaddr *)&paddr, &plen);
+         if (fd == -1)
+           {
+             log_error ("accept failed: %s\n", strerror (errno));
+           }
+          else 
+            {
+              char threadname[50];
+              snprintf (threadname, sizeof threadname-1,
+                        "conn fd=%d (gpg)", fd);
+              threadname[sizeof threadname -1] = 0;
+              pth_attr_set (tattr, PTH_ATTR_NAME, threadname);
+              if (!pth_spawn (tattr, start_connection_thread, (void*)fd))
+                {
+                  log_error ("error spawning connection handler: %s\n",
+                             strerror (errno) );
+                  close (fd);
+                }
+            }
+          fd = -1;
        }
+
+      if (listen_fd_ssh != -1 && FD_ISSET (listen_fd_ssh, &read_fdset))
+       {
+          plen = sizeof paddr;
+         fd = pth_accept (listen_fd_ssh, (struct sockaddr *)&paddr, &plen);
+         if (fd == -1)
+           {
+             log_error ("accept failed for ssh: %s\n", strerror (errno));
+           }
+          else
+            {
+              char threadname[50];
+              snprintf (threadname, sizeof threadname-1,
+                        "conn fd=%d (ssh)", fd);
+              threadname[sizeof threadname -1] = 0;
+              pth_attr_set (tattr, PTH_ATTR_NAME, threadname);
+
+              if (!pth_spawn (tattr, start_connection_thread_ssh, (void*)fd))
+                {
+                  log_error ("error spawning ssh connection handler: %s\n",
+                             strerror (errno) );
+                  close (fd);
+                }
+            }
+          fd = -1;
+       }
+
+      /* Restore the signal mask. */
+      pth_sigmask (SIG_SETMASK, &oldsigs, NULL);
+
     }
 
   pth_event_free (ev, PTH_FREE_ALL);
+  if (time_ev)
+    pth_event_free (time_ev, PTH_FREE_ALL);
   cleanup ();
-  log_info ("%s %s stopped\n", strusage(11), strusage(13));
+  log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
 }
-#endif /*USE_GNU_PTH*/
 
 
 /* Figure out whether an agent is available and running. Prints an
-   error if not.  */
-static void
-check_for_running_agent ()
+   error if not.  Usually started with MODE 0. */
+static int
+check_for_running_agent (int mode)
 {
   int rc;
   char *infostr, *p;
   assuan_context_t ctx;
   int prot, pid;
 
-  infostr = getenv ("GPG_AGENT_INFO");
-  if (!infostr || !*infostr)
+  if (!mode)
     {
-      log_error (_("no gpg-agent running in this session\n"));
-      return;
-    }
+      infostr = getenv ("GPG_AGENT_INFO");
+      if (!infostr || !*infostr)
+        {
+          if (!check_for_running_agent (1))
+            return 0; /* Okay, its running on the standard socket. */
+          log_error (_("no gpg-agent running in this session\n"));
+          return -1;
+        }
 
-  infostr = xstrdup (infostr);
-  if ( !(p = strchr (infostr, ':')) || p == infostr)
-    {
-      log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
-      xfree (infostr);
-      return;
-    }
+      infostr = xstrdup (infostr);
+      if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
+        {
+          xfree (infostr);
+          if (!check_for_running_agent (1))
+            return 0; /* Okay, its running on the standard socket. */
+          log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
+          return -1;
+        }
 
-  *p++ = 0;
-  pid = atoi (p);
-  while (*p && *p != ':')
-    p++;
-  prot = *p? atoi (p+1) : 0;
-  if (prot != 1)
+      *p++ = 0;
+      pid = atoi (p);
+      while (*p && *p != PATHSEP_C)
+        p++;
+      prot = *p? atoi (p+1) : 0;
+      if (prot != 1)
+        {
+          xfree (infostr);
+          log_error (_("gpg-agent protocol version %d is not supported\n"),
+                     prot);
+          if (!check_for_running_agent (1))
+            return 0; /* Okay, its running on the standard socket. */
+          return -1;
+        }
+    }
+  else /* MODE != 0 */
     {
-      log_error (_("gpg-agent protocol version %d is not supported\n"),
-                 prot);
-      xfree (infostr);
-      return;
+      infostr = make_filename (opt.homedir, "S.gpg-agent", NULL);
+      pid = (pid_t)(-1);
     }
 
+
   rc = assuan_socket_connect (&ctx, infostr, pid);
   xfree (infostr);
   if (rc)
     {
-      log_error ("can't connect to the agent: %s\n", assuan_strerror (rc));
-      return;
+      if (!mode && !check_for_running_agent (1))
+        return 0; /* Okay, its running on the standard socket. */
+
+      if (!mode)
+        log_error ("can't connect to the agent: %s\n", assuan_strerror (rc));
+      return -1;
     }
 
   if (!opt.quiet)
     log_info ("gpg-agent running and available\n");
 
   assuan_disconnect (ctx);
+  return 0;
 }