agent: Make digest algorithms for ssh fingerprints configurable.
[gnupg.git] / agent / gpg-agent.c
index 8a957cc..77b811c 100644 (file)
@@ -15,7 +15,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, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include "agent.h"
 #include <assuan.h> /* Malloc hooks  and socket wrappers. */
 
-#include "i18n.h"
-#include "sysutils.h"
-#include "gc-opt-flags.h"
-#include "exechelp.h"
-#include "asshelp.h"
-#include "openpgpdefs.h"  /* for PUBKEY_ALGO_ECDSA, PUBKEY_ALGO_ECDH */
+#include "../common/i18n.h"
+#include "../common/sysutils.h"
+#include "../common/gc-opt-flags.h"
+#include "../common/exechelp.h"
+#include "../common/asshelp.h"
 #include "../common/init.h"
 
 
@@ -88,6 +87,7 @@ enum cmd_and_opt_values
   oLogFile,
   oServer,
   oDaemon,
+  oSupervised,
   oBatch,
 
   oPinentryProgram,
@@ -111,6 +111,7 @@ enum cmd_and_opt_values
   oCheckPassphrasePattern,
   oMaxPassphraseDays,
   oEnablePassphraseHistory,
+  oEnableExtendedKeyFormat,
   oUseStandardSocket,
   oNoUseStandardSocket,
   oExtraSocket,
@@ -128,6 +129,7 @@ enum cmd_and_opt_values
   oKeepTTY,
   oKeepDISPLAY,
   oSSHSupport,
+  oSSHFingerprintDigest,
   oPuttySupport,
   oDisableScdaemon,
   oDisableCheckOwnSocket,
@@ -150,6 +152,9 @@ static ARGPARSE_OPTS opts[] = {
 
   ARGPARSE_s_n (oDaemon,  "daemon", N_("run in daemon mode (background)")),
   ARGPARSE_s_n (oServer,  "server", N_("run in server mode (foreground)")),
+#ifndef HAVE_W32_SYSTEM
+  ARGPARSE_s_n (oSupervised,  "supervised", N_("run in supervised mode")),
+#endif
   ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
   ARGPARSE_s_n (oQuiet,          "quiet",     N_("be somewhat more quiet")),
   ARGPARSE_s_n (oSh,     "sh",        N_("sh-style command output")),
@@ -228,6 +233,8 @@ static ARGPARSE_OPTS opts[] = {
                 /* */    N_("allow passphrase to be prompted through Emacs")),
 
   ARGPARSE_s_n (oSSHSupport,   "enable-ssh-support", N_("enable ssh support")),
+  ARGPARSE_s_s (oSSHFingerprintDigest, "ssh-fingerprint-digest",
+                N_("digest to use when communicating ssh fingerprints")),
   ARGPARSE_s_n (oPuttySupport, "enable-putty-support",
 #ifdef HAVE_W32_SYSTEM
                 /* */           N_("enable putty support")
@@ -235,6 +242,7 @@ static ARGPARSE_OPTS opts[] = {
                 /* */           "@"
 #endif
                 ),
+  ARGPARSE_s_n (oEnableExtendedKeyFormat, "enable-extended-key-format", "@"),
 
   /* Dummy options for backward compatibility.  */
   ARGPARSE_o_s (oWriteEnvFile, "write-env-file", "@"),
@@ -248,7 +256,6 @@ static ARGPARSE_OPTS opts[] = {
 /* The list of supported debug flags.  */
 static struct debug_flags_s debug_flags [] =
   {
-    { DBG_COMMAND_VALUE, "command"  },
     { DBG_MPI_VALUE    , "mpi"     },
     { DBG_CRYPTO_VALUE , "crypto"  },
     { DBG_MEMORY_VALUE , "memory"  },
@@ -301,8 +308,10 @@ static int putty_support;
 #endif /*HAVE_W32_SYSTEM*/
 
 /* The list of open file descriptors at startup.  Note that this list
-   has been allocated using the standard malloc.  */
+ * has been allocated using the standard malloc.  */
+#ifndef HAVE_W32_SYSTEM
 static int *startup_fd_list;
+#endif
 
 /* The signal mask at startup and a flag telling whether it is valid.  */
 #ifdef HAVE_SIGPROCMASK
@@ -319,6 +328,12 @@ static int check_own_socket_running;
 /* Flags to indicate that check_own_socket shall not be called.  */
 static int disable_check_own_socket;
 
+/* Flag indicating that we are in supervised mode.  */
+static int is_supervised;
+
+/* Flag to inhibit socket removal in cleanup.  */
+static int inhibit_socket_removal;
+
 /* It is possible that we are currently running under setuid permissions */
 static int maybe_setuid = 1;
 
@@ -336,7 +351,7 @@ static char *redir_socket_name_extra;
 static char *socket_name_browser;
 static char *redir_socket_name_browser;
 
-/* Name of the communication socket used for ssh-agent-emulation.  */
+/* Name of the communication socket used for ssh-agent protocol.  */
 static char *socket_name_ssh;
 static char *redir_socket_name_ssh;
 
@@ -375,9 +390,9 @@ static pid_t parent_pid = (pid_t)(-1);
 static int active_connections;
 
 /* This object is used to dispatch progress messages from Libgcrypt to
- * the right thread.  Given that we won't have at max a few dozen
- * connections at the same time using a linked list is the easiest way
- * to handle this. */
+ * the right thread.  Given that we will have at max only a few dozen
+ * connections at a time, using a linked list is the easiest way to
+ * handle this. */
 struct progress_dispatch_s
 {
   struct progress_dispatch_s *next;
@@ -511,10 +526,9 @@ set_debug (void)
   else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2))
     opt.debug = DBG_IPC_VALUE;
   else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5))
-    opt.debug = DBG_IPC_VALUE|DBG_COMMAND_VALUE;
+    opt.debug = DBG_IPC_VALUE;
   else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8))
-    opt.debug = (DBG_IPC_VALUE|DBG_COMMAND_VALUE
-                 |DBG_CACHE_VALUE);
+    opt.debug = (DBG_IPC_VALUE | DBG_CACHE_VALUE);
   else if (!strcmp (debug_level, "guru") || numok)
     {
       opt.debug = ~0;
@@ -564,6 +578,168 @@ remove_socket (char *name, char *redir_name)
 }
 
 
+/* Discover which inherited file descriptors correspond to which
+ * services/sockets offered by gpg-agent, using the LISTEN_FDS and
+ * LISTEN_FDNAMES convention.  The understood labels are "ssh",
+ * "extra", and "browser".  "std" or other labels will be interpreted
+ * as the standard socket.
+ *
+ * This function is designed to log errors when the expected file
+ * descriptors don't make sense, but to do its best to continue to
+ * work even in the face of minor misconfigurations.
+ *
+ * For more information on the LISTEN_FDS convention, see
+ * sd_listen_fds(3) on certain Linux distributions.
+ */
+#ifndef HAVE_W32_SYSTEM
+static void
+map_supervised_sockets (gnupg_fd_t *r_fd,
+                        gnupg_fd_t *r_fd_extra,
+                        gnupg_fd_t *r_fd_browser,
+                        gnupg_fd_t *r_fd_ssh)
+{
+  struct {
+    const char *label;
+    int **fdaddr;
+    char **nameaddr;
+  } tbl[] = {
+    { "ssh",     &r_fd_ssh,     &socket_name_ssh },
+    { "browser", &r_fd_browser, &socket_name_browser },
+    { "extra",   &r_fd_extra,   &socket_name_extra },
+    { "std",     &r_fd,         &socket_name }  /* (Must be the last item.)  */
+  };
+  const char *envvar;
+  char **fdnames;
+  int nfdnames;
+  int fd_count;
+
+  *r_fd = *r_fd_extra = *r_fd_browser = *r_fd_ssh = -1;
+
+  /* Print a warning if LISTEN_PID does not match outr pid.  */
+  envvar = getenv ("LISTEN_PID");
+  if (!envvar)
+    log_error ("no LISTEN_PID environment variable found in "
+               "--supervised mode (ignoring)\n");
+  else if (strtoul (envvar, NULL, 10) != (unsigned long)getpid ())
+    log_error ("environment variable LISTEN_PID (%lu) does not match"
+               " our pid (%lu) in --supervised mode (ignoring)\n",
+               (unsigned long)strtoul (envvar, NULL, 10),
+               (unsigned long)getpid ());
+
+  /* Parse LISTEN_FDNAMES into the array FDNAMES.  */
+  envvar = getenv ("LISTEN_FDNAMES");
+  if (envvar)
+    {
+      fdnames = strtokenize (envvar, ":");
+      if (!fdnames)
+        {
+          log_error ("strtokenize failed: %s\n",
+                     gpg_strerror (gpg_error_from_syserror ()));
+          agent_exit (1);
+        }
+      for (nfdnames=0; fdnames[nfdnames]; nfdnames++)
+        ;
+    }
+  else
+    {
+      fdnames = NULL;
+      nfdnames = 0;
+    }
+
+  /* Parse LISTEN_FDS into fd_count or provide a replacement.  */
+  envvar = getenv ("LISTEN_FDS");
+  if (envvar)
+    fd_count = atoi (envvar);
+  else if (fdnames)
+    {
+      log_error ("no LISTEN_FDS environment variable found in --supervised"
+                 " mode (relying on LISTEN_FDNAMES instead)\n");
+      fd_count = nfdnames;
+    }
+  else
+    {
+      log_error ("no LISTEN_FDS or LISTEN_FDNAMES environment variables "
+                "found in --supervised mode"
+                " (assuming 1 active descriptor)\n");
+      fd_count = 1;
+    }
+
+  if (fd_count < 1)
+    {
+      log_error ("--supervised mode expects at least one file descriptor"
+                 " (was told %d, carrying on as though it were 1)\n",
+                 fd_count);
+      fd_count = 1;
+    }
+
+  /* Assign the descriptors to the return values.  */
+  if (!fdnames)
+    {
+      struct stat statbuf;
+
+      if (fd_count != 1)
+        log_error ("no LISTEN_FDNAMES and LISTEN_FDS (%d) != 1"
+                   " in --supervised mode."
+                   " (ignoring all sockets but the first one)\n",
+                   fd_count);
+      if (fstat (3, &statbuf) == -1 && errno ==EBADF)
+        log_fatal ("file descriptor 3 must be valid in --supervised mode"
+                   " if LISTEN_FDNAMES is not set\n");
+      *r_fd = 3;
+      socket_name = gnupg_get_socket_name (3);
+    }
+  else if (fd_count != nfdnames)
+    {
+      log_fatal ("number of items in LISTEN_FDNAMES (%d) does not match "
+                 "LISTEN_FDS (%d) in --supervised mode\n",
+                 nfdnames, fd_count);
+    }
+  else
+    {
+      int i, j, fd;
+      char *name;
+
+      for (i = 0; i < nfdnames; i++)
+        {
+          for (j = 0; j < DIM (tbl); j++)
+            {
+              if (!strcmp (fdnames[i], tbl[j].label) || j == DIM(tbl)-1)
+                {
+                  fd = 3 + i;
+                  if (**tbl[j].fdaddr == -1)
+                    {
+                      name = gnupg_get_socket_name (fd);
+                      if (name)
+                        {
+                          **tbl[j].fdaddr = fd;
+                          *tbl[j].nameaddr = name;
+                          log_info ("using fd %d for %s socket (%s)\n",
+                                    fd, tbl[j].label, name);
+                        }
+                      else
+                        {
+                          log_error ("cannot listen on fd %d for %s socket\n",
+                                     fd, tbl[j].label);
+                          close (fd);
+                        }
+                    }
+                  else
+                    {
+                      log_error ("cannot listen on more than one %s socket\n",
+                                 tbl[j].label);
+                      close (fd);
+                    }
+                  break;
+                }
+            }
+        }
+    }
+
+  xfree (fdnames);
+}
+#endif /*!HAVE_W32_SYSTEM*/
+
+
 /* Cleanup code for this program.  This is either called has an atexit
    handler or directly.  */
 static void
@@ -575,12 +751,15 @@ cleanup (void)
     return;
   done = 1;
   deinitialize_module_cache ();
-  remove_socket (socket_name, redir_socket_name);
-  if (opt.extra_socket > 1)
-    remove_socket (socket_name_extra, redir_socket_name_extra);
-  if (opt.browser_socket > 1)
-    remove_socket (socket_name_browser, redir_socket_name_browser);
-  remove_socket (socket_name_ssh, redir_socket_name_ssh);
+  if (!is_supervised && !inhibit_socket_removal)
+    {
+      remove_socket (socket_name, redir_socket_name);
+      if (opt.extra_socket > 1)
+        remove_socket (socket_name_extra, redir_socket_name_extra);
+      if (opt.browser_socket > 1)
+        remove_socket (socket_name_browser, redir_socket_name_browser);
+      remove_socket (socket_name_ssh, redir_socket_name_ssh);
+    }
 }
 
 
@@ -615,7 +794,8 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
       opt.min_passphrase_nonalpha = MIN_PASSPHRASE_NONALPHA;
       opt.check_passphrase_pattern = NULL;
       opt.max_passphrase_days = MAX_PASSPHRASE_DAYS;
-      opt.enable_passhrase_history = 0;
+      opt.enable_passphrase_history = 0;
+      opt.enable_extended_key_format = 0;
       opt.ignore_cache_for_signing = 0;
       opt.allow_mark_trusted = 1;
       opt.allow_external_cache = 1;
@@ -623,6 +803,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
       opt.allow_emacs_pinentry = 0;
       opt.disable_scdaemon = 0;
       disable_check_own_socket = 0;
+      opt.ssh_fingerprint_digest = GCRY_MD_MD5;
       return 1;
     }
 
@@ -682,7 +863,11 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
       opt.max_passphrase_days = pargs->r.ret_ulong;
       break;
     case oEnablePassphraseHistory:
-      opt.enable_passhrase_history = 1;
+      opt.enable_passphrase_history = 1;
+      break;
+
+    case oEnableExtendedKeyFormat:
+      opt.enable_extended_key_format = 1;
       break;
 
     case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break;
@@ -716,6 +901,39 @@ finalize_rereadable_options (void)
 }
 
 
+static void
+thread_init_once (void)
+{
+  static int npth_initialized = 0;
+
+  if (!npth_initialized)
+    {
+      npth_initialized++;
+      npth_init ();
+    }
+  gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
+  /* Now that we have set the syscall clamp we need to tell Libgcrypt
+   * that it should get them from libgpg-error.  Note that Libgcrypt
+   * has already been initialized but at that point nPth was not
+   * initialized and thus Libgcrypt could not set its system call
+   * clamp.  */
+#if GCRYPT_VERSION_NUMBER >= 0x010800 /* 1.8.0 */
+  gcry_control (GCRYCTL_REINIT_SYSCALL_CLAMP, 0, 0);
+#endif
+}
+
+
+static void
+initialize_modules (void)
+{
+  thread_init_once ();
+  assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
+  initialize_module_cache ();
+  initialize_module_call_pinentry ();
+  initialize_module_call_scd ();
+  initialize_module_trustlist ();
+}
+
 
 /* The main entry point.  */
 int
@@ -744,8 +962,10 @@ main (int argc, char **argv )
 
   /* Before we do anything else we save the list of currently open
      file descriptors and the signal mask.  This info is required to
-     do the exec call properly. */
+     do the exec call properly.  We don't need it on Windows.  */
+#ifndef HAVE_W32_SYSTEM
   startup_fd_list = get_all_open_fds ();
+#endif /*!HAVE_W32_SYSTEM*/
 #ifdef HAVE_SIGPROCMASK
   if (!sigprocmask (SIG_UNBLOCK, NULL, &startup_signal_mask))
     startup_signal_mask_valid = 1;
@@ -763,24 +983,13 @@ main (int argc, char **argv )
   i18n_init ();
   init_common_subsystems (&argc, &argv);
 
-  npth_init ();
-
-  /* Check that the libraries are suitable.  Do it here because
-     the option parsing may need services of the library. */
-  if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
-    {
-      log_fatal( _("%s is too old (need %s, have %s)\n"), "libgcrypt",
-                 NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
-    }
-
   malloc_hooks.malloc = gcry_malloc;
   malloc_hooks.realloc = gcry_realloc;
   malloc_hooks.free = gcry_free;
   assuan_set_malloc_hooks (&malloc_hooks);
   assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
-  assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
   assuan_sock_init ();
-  setup_libassuan_logging (&opt.debug);
+  setup_libassuan_logging (&opt.debug, NULL);
 
   setup_libgcrypt_logging ();
   gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
@@ -859,7 +1068,7 @@ main (int argc, char **argv )
     }
 
   /* Initialize the secure memory. */
-  gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
+  gcry_control (GCRYCTL_INIT_SECMEM, SECMEM_BUFFER_SIZE, 0);
   maybe_setuid = 0;
 
   /*
@@ -940,6 +1149,7 @@ main (int argc, char **argv )
         case oSh: csh_style = 0; break;
         case oServer: pipe_server = 1; break;
         case oDaemon: is_daemon = 1; break;
+        case oSupervised: is_supervised = 1; break;
 
         case oDisplay: default_display = xstrdup (pargs.r.ret_str); break;
         case oTTYname: default_ttyname = xstrdup (pargs.r.ret_str); break;
@@ -970,6 +1180,11 @@ main (int argc, char **argv )
        case oSSHSupport:
           ssh_support = 1;
           break;
+       case oSSHFingerprintDigest:
+          opt.ssh_fingerprint_digest = gcry_md_map_name (pargs.r.ret_str);
+          if (opt.ssh_fingerprint_digest == 0)
+            log_error ("Unknown digest algorithm: %s\n", pargs.r.ret_str);
+          break;
         case oPuttySupport:
 #        ifdef HAVE_W32_SYSTEM
           putty_support = 1;
@@ -1039,9 +1254,9 @@ main (int argc, char **argv )
     bind_textdomain_codeset (PACKAGE_GT, "UTF-8");
 #endif
 
-  if (!pipe_server && !is_daemon && !gpgconf_list)
+  if (!pipe_server && !is_daemon && !gpgconf_list && !is_supervised)
     {
-     /* We have been called without any options and thus we merely
+     /* We have been called without any command and thus we merely
         check whether an agent is already running.  We do this right
         here so that we don't clobber a logfile with this check but
         print the status directly to stderr. */
@@ -1051,6 +1266,32 @@ main (int argc, char **argv )
       agent_exit (0);
     }
 
+  if (is_supervised)
+    ;
+  else if (!opt.extra_socket)
+    opt.extra_socket = 1;
+  else if (socket_name_extra
+           && (!strcmp (socket_name_extra, "none")
+               || !strcmp (socket_name_extra, "/dev/null")))
+    {
+      /* User requested not to create this socket.  */
+      opt.extra_socket = 0;
+      socket_name_extra = NULL;
+    }
+
+  if (is_supervised)
+    ;
+  else if (!opt.browser_socket)
+    opt.browser_socket = 1;
+  else if (socket_name_browser
+           && (!strcmp (socket_name_browser, "none")
+               || !strcmp (socket_name_browser, "/dev/null")))
+    {
+      /* User requested not to create this socket.  */
+      opt.browser_socket = 0;
+      socket_name_browser = NULL;
+    }
+
   set_debug ();
 
   if (atexit (cleanup))
@@ -1060,16 +1301,12 @@ main (int argc, char **argv )
       exit (1);
     }
 
-  initialize_module_cache ();
-  initialize_module_call_pinentry ();
-  initialize_module_call_scd ();
-  initialize_module_trustlist ();
-
   /* Try to create missing directories. */
   create_directories ();
 
   if (debug_wait && pipe_server)
     {
+      thread_init_once ();
       log_debug ("waiting for debugger - my pid is %u .....\n",
                  (unsigned int)getpid());
       gnupg_sleep (debug_wait);
@@ -1150,6 +1387,8 @@ main (int argc, char **argv )
                  GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
       es_printf ("pinentry-timeout:%lu:0:\n",
                  GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME);
+      es_printf ("enable-extended-key-format:%lu:\n",
+                 GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
 
       agent_exit (0);
     }
@@ -1176,6 +1415,8 @@ main (int argc, char **argv )
       /* This is the simple pipe based server */
       ctrl_t ctrl;
 
+      initialize_modules ();
+
       ctrl = xtrycalloc (1, sizeof *ctrl);
       if (!ctrl)
         {
@@ -1196,6 +1437,50 @@ main (int argc, char **argv )
       agent_deinit_default_ctrl (ctrl);
       xfree (ctrl);
     }
+  else if (is_supervised)
+    {
+#ifndef HAVE_W32_SYSTEM
+      gnupg_fd_t fd, fd_extra, fd_browser, fd_ssh;
+
+      initialize_modules ();
+
+      /* when supervised and sending logs to stderr, the process
+         supervisor should handle log entry metadata (pid, name,
+         timestamp) */
+      if (!logfile)
+        log_set_prefix (NULL, 0);
+
+      log_info ("%s %s starting in supervised mode.\n",
+                strusage(11), strusage(13) );
+
+      /* See below in "regular server mode" on why we remove certain
+       * envvars.  */
+      if (!opt.keep_display)
+        gnupg_unsetenv ("DISPLAY");
+      gnupg_unsetenv ("INSIDE_EMACS");
+
+      /* Virtually create the sockets.  Note that we use -1 here
+       * because the whole thing works only on Unix. */
+      map_supervised_sockets (&fd, &fd_extra, &fd_browser, &fd_ssh);
+      if (fd == -1)
+        log_fatal ("no standard socket provided\n");
+
+#ifdef HAVE_SIGPROCMASK
+      if (startup_signal_mask_valid)
+        {
+          if (sigprocmask (SIG_SETMASK, &startup_signal_mask, NULL))
+            log_error ("error restoring signal mask: %s\n",
+                       strerror (errno));
+        }
+      else
+        log_info ("no saved signal mask\n");
+#endif /*HAVE_SIGPROCMASK*/
+
+      log_info ("listening on: std=%d extra=%d browser=%d ssh=%d\n",
+                fd, fd_extra, fd_browser, fd_ssh);
+      handle_connections (fd, fd_extra, fd_browser, fd_ssh);
+#endif /*!HAVE_W32_SYSTEM*/
+    }
   else if (!is_daemon)
     ; /* NOTREACHED */
   else
@@ -1231,7 +1516,11 @@ main (int argc, char **argv )
 
       if (opt.extra_socket)
         {
-          socket_name_extra = create_socket_name (socket_name_extra, 0);
+          if (socket_name_extra)
+            socket_name_extra = create_socket_name (socket_name_extra, 0);
+          else
+            socket_name_extra = create_socket_name
+              /**/                (GPG_AGENT_EXTRA_SOCK_NAME, 1);
           opt.extra_socket = 2; /* Indicate that it has been malloced.  */
           fd_extra = create_server_socket (socket_name_extra, 0, 0,
                                            &redir_socket_name_extra,
@@ -1240,20 +1529,21 @@ main (int argc, char **argv )
 
       if (opt.browser_socket)
         {
-          socket_name_browser = create_socket_name (socket_name_browser, 0);
+          if (socket_name_browser)
+            socket_name_browser = create_socket_name (socket_name_browser, 0);
+          else
+            socket_name_browser= create_socket_name
+              /**/                 (GPG_AGENT_BROWSER_SOCK_NAME, 1);
           opt.browser_socket = 2; /* Indicate that it has been malloced.  */
           fd_browser = create_server_socket (socket_name_browser, 0, 0,
                                              &redir_socket_name_browser,
                                              &socket_nonce_browser);
         }
 
-      if (ssh_support)
-        {
-          socket_name_ssh = create_socket_name (GPG_AGENT_SSH_SOCK_NAME, 1);
-          fd_ssh = create_server_socket (socket_name_ssh, 0, 1,
-                                         &redir_socket_name_ssh,
-                                         &socket_nonce_ssh);
-        }
+      socket_name_ssh = create_socket_name (GPG_AGENT_SSH_SOCK_NAME, 1);
+      fd_ssh = create_server_socket (socket_name_ssh, 0, 1,
+                                     &redir_socket_name_ssh,
+                                     &socket_nonce_ssh);
 
       /* 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
@@ -1262,10 +1552,15 @@ main (int argc, char **argv )
         parent_pid = getpid ();
 
       fflush (NULL);
+
 #ifdef HAVE_W32_SYSTEM
+
       (void)csh_style;
       (void)nodetach;
+      initialize_modules ();
+
 #else /*!HAVE_W32_SYSTEM*/
+
       pid = fork ();
       if (pid == (pid_t)-1)
         {
@@ -1319,8 +1614,7 @@ main (int argc, char **argv )
            *socket_name_extra = 0;
          if (opt.browser_socket)
            *socket_name_browser = 0;
-         if (ssh_support)
-           *socket_name_ssh = 0;
+          *socket_name_ssh = 0;
 
           if (argc)
             { /* Run the program given on the commandline.  */
@@ -1379,6 +1673,8 @@ main (int argc, char **argv )
          This is the child
        */
 
+      initialize_modules ();
+
       /* Detach from tty and put process into a new session */
       if (!nodetach )
         {
@@ -1482,6 +1778,23 @@ agent_libgcrypt_progress_cb (void *data, const char *what, int printchar,
       break;
   if (dispatch && dispatch->cb)
     dispatch->cb (dispatch->ctrl, what, printchar, current, total);
+
+  /* Libgcrypt < 1.8 does not know about nPth and thus when it reads
+   * from /dev/random this will block the process.  To mitigate this
+   * problem we yield the thread when Libgcrypt tells us that it needs
+   * more entropy.  This way other threads have chance to run.  */
+#if GCRYPT_VERSION_NUMBER < 0x010800 /* 1.8.0 */
+  if (what && !strcmp (what, "need_entropy"))
+    {
+#if GPGRT_VERSION_NUMBER < 0x011900 /* 1.25 */
+      /* In older gpg-error versions gpgrt_yield is buggy for use with
+       * nPth and thus we need to resort to a sleep call.  */
+      npth_usleep (1000); /* 1ms */
+#else
+      gpgrt_yield ();
+#endif
+    }
+#endif
 }
 
 
@@ -1607,7 +1920,7 @@ agent_copy_startup_env (ctrl_t ctrl)
   const char *value;
 
   for (idx=0; !err && names[idx]; idx++)
-      if ((value = session_env_getenv (opt.startup_env, names[idx])))
+    if ((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)
@@ -2099,7 +2412,7 @@ handle_signal (int signo)
 }
 #endif
 
-/* Check the nonce on a new connection.  This is a NOP unless we we
+/* Check the nonce on a new connection.  This is a NOP unless we
    are using our Unix domain socket emulation under Windows.  */
 static int
 check_nonce (ctrl_t ctrl, assuan_sock_nonce_t *nonce)
@@ -2312,12 +2625,12 @@ do_start_connection_thread (ctrl_t ctrl)
 {
   active_connections++;
   agent_init_default_ctrl (ctrl);
-  if (opt.verbose)
+  if (opt.verbose && !DBG_IPC)
     log_info (_("handler 0x%lx for fd %d started\n"),
               (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
 
   start_command_handler (ctrl, GNUPG_INVALID_FD, ctrl->thread_startup.fd);
-  if (opt.verbose)
+  if (opt.verbose && !DBG_IPC)
     log_info (_("handler 0x%lx for fd %d terminated\n"),
               (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
 
@@ -2416,6 +2729,7 @@ handle_connections (gnupg_fd_t listen_fd,
                     gnupg_fd_t listen_fd_browser,
                     gnupg_fd_t listen_fd_ssh)
 {
+  gpg_error_t err;
   npth_attr_t tattr;
   struct sockaddr_un paddr;
   socklen_t plen;
@@ -2431,6 +2745,7 @@ handle_connections (gnupg_fd_t listen_fd,
   HANDLE events[2];
   unsigned int events_set;
 #endif
+  int my_inotify_fd = -1;
   struct {
     const char *name;
     void *(*func) (void *arg);
@@ -2468,6 +2783,15 @@ handle_connections (gnupg_fd_t listen_fd,
 # endif
 #endif
 
+  if (disable_check_own_socket)
+    my_inotify_fd = -1;
+  else if ((err = gnupg_inotify_watch_socket (&my_inotify_fd, socket_name)))
+    {
+      if (gpg_err_code (err) != GPG_ERR_NOT_SUPPORTED)
+        log_info ("error enabling fast daemon termination: %s\n",
+                  gpg_strerror (err));
+    }
+
   /* On Windows we need to fire up a separate thread to listen for
      requests from Putty (an SSH client), so we can replace Putty's
      Pageant (its ssh-agent implementation). */
@@ -2509,6 +2833,12 @@ handle_connections (gnupg_fd_t listen_fd,
       if (FD2INT (listen_fd_ssh) > nfd)
         nfd = FD2INT (listen_fd_ssh);
     }
+  if (my_inotify_fd != -1)
+    {
+      FD_SET (my_inotify_fd, &fdset);
+      if (my_inotify_fd > nfd)
+        nfd = my_inotify_fd;
+    }
 
   listentbl[0].l_fd = listen_fd;
   listentbl[1].l_fd = listen_fd_extra;
@@ -2527,8 +2857,19 @@ handle_connections (gnupg_fd_t listen_fd,
             break; /* ready */
 
           /* Do not accept new connections but keep on running the
-             loop to cope with the timer events.  */
+           * loop to cope with the timer events.
+           *
+           * Note that we do not close the listening socket because a
+           * client trying to connect to that socket would instead
+           * restart a new dirmngr instance - which is unlikely the
+           * intention of a shutdown. */
           FD_ZERO (&fdset);
+          nfd = -1;
+          if (my_inotify_fd != -1)
+            {
+              FD_SET (my_inotify_fd, &fdset);
+              nfd = my_inotify_fd;
+            }
        }
 
       /* POSIX says that fd_set should be implemented as a structure,
@@ -2583,6 +2924,14 @@ handle_connections (gnupg_fd_t listen_fd,
           ctrl_t ctrl;
           npth_t thread;
 
+          if (my_inotify_fd != -1
+              && FD_ISSET (my_inotify_fd, &read_fdset)
+              && gnupg_inotify_has_name (my_inotify_fd, GPG_AGENT_SOCK_NAME))
+            {
+              shutdown_pending = 1;
+              log_info ("socket file has been removed - shutting down\n");
+            }
+
           for (idx=0; idx < DIM(listentbl); idx++)
             {
               if (listentbl[idx].l_fd == GNUPG_INVALID_FD)
@@ -2624,11 +2973,12 @@ handle_connections (gnupg_fd_t listen_fd,
                       xfree (ctrl);
                     }
                 }
-              fd = GNUPG_INVALID_FD;
             }
         }
     }
 
+  if (my_inotify_fd != -1)
+    close (my_inotify_fd);
   cleanup ();
   log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
   npth_attr_destroy (&tattr);
@@ -2666,6 +3016,7 @@ check_own_socket_thread (void *arg)
       log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc));
       goto leave;
     }
+  assuan_set_flag (ctx, ASSUAN_NO_LOGGING, 1);
 
   rc = assuan_socket_connect (ctx, sockname, (pid_t)(-1), 0);
   if (rc)
@@ -2702,11 +3053,8 @@ check_own_socket_thread (void *arg)
   if (rc)
     {
       /* We may not remove the socket as it is now in use by another
-         server.  Setting the name to empty does this.  */
-      if (socket_name)
-        *socket_name = 0;
-      if (socket_name_ssh)
-        *socket_name_ssh = 0;
+         server. */
+      inhibit_socket_removal = 1;
       shutdown_pending = 2;
       log_info ("this process is useless - shutting down\n");
     }