agent: Make digest algorithms for ssh fingerprints configurable.
[gnupg.git] / agent / gpg-agent.c
index 9221dc3..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>
 #ifdef HAVE_SIGNAL_H
 # include <signal.h>
 #endif
-#ifdef HAVE_INOTIFY_INIT
-# include <sys/inotify.h>
-#endif /*HAVE_INOTIFY_INIT*/
 #include <npth.h>
 
 #define GNUPG_COMMON_NEED_AFLOCAL
 #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 "../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"
 
 
@@ -114,6 +111,7 @@ enum cmd_and_opt_values
   oCheckPassphrasePattern,
   oMaxPassphraseDays,
   oEnablePassphraseHistory,
+  oEnableExtendedKeyFormat,
   oUseStandardSocket,
   oNoUseStandardSocket,
   oExtraSocket,
@@ -131,6 +129,7 @@ enum cmd_and_opt_values
   oKeepTTY,
   oKeepDISPLAY,
   oSSHSupport,
+  oSSHFingerprintDigest,
   oPuttySupport,
   oDisableScdaemon,
   oDisableCheckOwnSocket,
@@ -153,7 +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)")),
-  ARGPARSE_s_n (oSupervised,  "supervised", N_("run supervised (e.g., systemd)")),
+#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")),
@@ -232,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")
@@ -239,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", "@"),
@@ -252,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"  },
@@ -305,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
@@ -323,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;
 
@@ -340,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;
 
@@ -379,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;
@@ -515,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;
@@ -568,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
@@ -579,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);
+    }
 }
 
 
@@ -619,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;
@@ -627,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;
     }
 
@@ -686,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;
@@ -730,6 +911,15 @@ thread_init_once (void)
       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
 }
 
 
@@ -745,180 +935,6 @@ initialize_modules (void)
 }
 
 
-/* return a malloc'ed string that is the path to the passed unix-domain socket
-   (or return NULL if this is not a valid unix-domain socket) */
-static char *
-get_socket_path (gnupg_fd_t fd)
-{
-#ifdef HAVE_W32_SYSTEM
-  return NULL;
-#else
-  struct sockaddr_un un;
-  socklen_t len = sizeof(un);
-  char *ret = NULL;
-
-  if (fd == GNUPG_INVALID_FD)
-    return NULL;
-
-  if (getsockname (fd, (struct sockaddr*)&un, &len) != 0)
-    log_error ("could not getsockname(%d) -- error %d (%s)\n", fd,
-               errno, strerror(errno));
-  else if (un.sun_family != AF_UNIX)
-    log_error ("file descriptor %d is not a unix-domain socket\n", fd);
-  else if (len <= offsetof (struct sockaddr_un, sun_path))
-    log_error ("socket path not present for file descriptor %d\n", fd);
-  else if (len > sizeof(un))
-    log_error ("socket path for file descriptor %d was truncated "
-               "(passed %lu bytes, wanted %u)\n", fd, sizeof(un), len);
-  else
-    {
-      log_debug ("file descriptor %d has path %s (%lu octets)\n", fd,
-                 un.sun_path, len - offsetof (struct sockaddr_un, sun_path));
-      ret = malloc(len - offsetof (struct sockaddr_un, sun_path));
-      if (ret == NULL)
-        log_error ("failed to allocate memory for path to file "
-                   "descriptor %d\n", fd);
-      else
-        memcpy (ret, un.sun_path, len);
-    }
-  return ret;
-#endif /* HAVE_W32_SYSTEM */
-}
-
-
-/* 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".  Any other label 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).
- */
-static void
-map_supervised_sockets (gnupg_fd_t *fd,
-                        gnupg_fd_t *fd_extra,
-                        gnupg_fd_t *fd_browser,
-                        gnupg_fd_t *fd_ssh)
-{
-  const char *listen_pid = NULL;
-  const char *listen_fds = NULL;
-  const char *listen_fdnames = NULL;
-  int listen_fd_count = -1;
-  int listen_fdnames_colons = 0;
-  const char *fdnamep = NULL;
-
-  listen_pid = getenv ("LISTEN_PID");
-  listen_fds = getenv ("LISTEN_FDS");
-  listen_fdnames = getenv ("LISTEN_FDNAMES");
-
-  if (!listen_pid)
-    log_error ("no $LISTEN_PID environment variable found in "
-               "--supervised mode (ignoring).\n");
-  else if (atoi (listen_pid) != getpid ())
-    log_error ("$LISTEN_PID (%d) does not match process ID (%d) "
-               "in --supervised mode (ignoring).\n",
-               atoi (listen_pid), getpid ());
-  else
-    log_debug ("$LISTEN_PID matches process ID (%d)\n",
-               getpid());
-
-  if (listen_fdnames)
-    for (fdnamep = listen_fdnames; *fdnamep; fdnamep++)
-      if (*fdnamep == ':')
-        listen_fdnames_colons++;
-  log_debug ("%d colon(s) in $LISTEN_FDNAMES: (%s)\n", listen_fdnames_colons, listen_fdnames);
-
-  if (!listen_fds)
-    {
-      if (!listen_fdnames)
-        {
-          log_error ("no LISTEN_FDS or LISTEN_FDNAMES environment variables "
-                     "found in --supervised mode (assuming 1 active descriptor).\n");
-          listen_fd_count = 1;
-        }
-      else
-        {
-          log_error ("no LISTEN_FDS environment variable found in --supervised "
-                     " mode (relying on colons in LISTEN_FDNAMES instead)\n");
-          listen_fd_count = listen_fdnames_colons + 1;
-        }
-    }
-  else
-    listen_fd_count = atoi (listen_fds);
-
-  if (listen_fd_count < 1)
-    {
-      log_error ("--supervised mode expects at least one file descriptor (was told %d) "
-                 "(carrying on as though it were 1)\n", listen_fd_count);
-      listen_fd_count = 1;
-    }
-
-  if (!listen_fdnames)
-    {
-      if (listen_fd_count != 1)
-        log_error ("no LISTEN_FDNAMES and LISTEN_FDS (%d) != 1 in --supervised mode. "
-                   "(ignoring all sockets but the first one)\n", listen_fd_count);
-      *fd = 3;
-    }
-  else
-    {
-      int i;
-      if (listen_fd_count != listen_fdnames_colons + 1)
-        {
-          log_fatal ("number of items in LISTEN_FDNAMES (%d) does not match "
-                     "LISTEN_FDS (%d) in --supervised mode\n",
-                     listen_fdnames_colons + 1, listen_fd_count);
-          exit (1);
-        }
-
-      for (i = 3; i < 3 + listen_fd_count; i++)
-        {
-          int found = 0;
-          char *next = strchrnul(listen_fdnames, ':');
-          *next = '\0';
-#define match_socket(var) if (!found && strcmp (listen_fdnames, #var) == 0) \
-            {                                                           \
-              found = 1;                                                \
-              if (*fd_ ## var == GNUPG_INVALID_FD)                      \
-                {                                                       \
-                  *fd_ ## var = i;                                      \
-                  log_info (#var " socket on fd %d\n", i);              \
-                }                                                       \
-              else                                                      \
-                {                                                       \
-                  log_error ("cannot listen on more than one " #var " socket. (closing fd %d)\n", i); \
-                  close (i);                                            \
-                }                                                       \
-            }
-          match_socket(ssh);
-          match_socket(browser);
-          match_socket(extra);
-#undef match_socket
-          if (!found)
-            {
-              if (*fd == GNUPG_INVALID_FD)
-                {
-                  *fd = i;
-                  log_info ("standard socket (\"%s\") on fd %d\n",
-                            listen_fdnames, i);
-                }
-              else
-                {
-                  log_error ("cannot listen on more than one standard socket. (closing fd %d)\n", i);
-                  close (i);
-                }
-            }
-          listen_fdnames = next + 1;
-        }
-    }
-}
-
-
 /* The main entry point.  */
 int
 main (int argc, char **argv )
@@ -934,7 +950,6 @@ main (int argc, char **argv )
   int default_config =1;
   int pipe_server = 0;
   int is_daemon = 0;
-  int is_supervised = 0;
   int nodetach = 0;
   int csh_style = 0;
   char *logfile = NULL;
@@ -947,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;
@@ -1051,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;
 
   /*
@@ -1163,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;
@@ -1244,10 +1266,10 @@ main (int argc, char **argv )
       agent_exit (0);
     }
 
-  if (! opt.extra_socket)
-    {
-      opt.extra_socket = 1;
-    }
+  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")))
@@ -1257,10 +1279,10 @@ main (int argc, char **argv )
       socket_name_extra = NULL;
     }
 
-  if (! opt.browser_socket)
-    {
-      opt.browser_socket = 1;
-    }
+  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")))
@@ -1365,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);
     }
@@ -1415,10 +1439,10 @@ main (int argc, char **argv )
     }
   else if (is_supervised)
     {
-      gnupg_fd_t fd = GNUPG_INVALID_FD;
-      gnupg_fd_t fd_extra = GNUPG_INVALID_FD;
-      gnupg_fd_t fd_browser = GNUPG_INVALID_FD;
-      gnupg_fd_t fd_ssh = GNUPG_INVALID_FD;
+#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,
@@ -1429,21 +1453,17 @@ main (int argc, char **argv )
       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 == GNUPG_INVALID_FD)
-        {
-          log_fatal ("no standard socket provided\n");
-          exit (1);
-        }
-      /* record socket names where possible: */
-      socket_name = get_socket_path (fd);
-      socket_name_extra = get_socket_path (fd_extra);
-      if (socket_name_extra)
-        opt.extra_socket = 2;
-      socket_name_browser = get_socket_path (fd_browser);
-      if (socket_name_browser)
-        opt.browser_socket = 2;
-      socket_name_ssh = get_socket_path (fd_ssh);
+      if (fd == -1)
+        log_fatal ("no standard socket provided\n");
 
 #ifdef HAVE_SIGPROCMASK
       if (startup_signal_mask_valid)
@@ -1456,10 +1476,10 @@ main (int argc, char **argv )
         log_info ("no saved signal mask\n");
 #endif /*HAVE_SIGPROCMASK*/
 
-      log_debug ("FDs: std: %d extra: %d browser: %d ssh: %d\n",
-                 fd, fd_extra, fd_browser, fd_ssh);
+      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);
-      assuan_sock_close (fd);
+#endif /*!HAVE_W32_SYSTEM*/
     }
   else if (!is_daemon)
     ; /* NOTREACHED */
@@ -1473,8 +1493,6 @@ main (int argc, char **argv )
       pid_t pid;
 #endif
 
-      initialize_modules ();
-
       /* Remove the DISPLAY variable so that a pinentry does not
          default to a specific display.  There is still a default
          display when gpg-agent was started using --display or a
@@ -1534,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)
         {
@@ -1755,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
 }
 
 
@@ -1880,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)
@@ -2372,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)
@@ -2681,31 +2721,6 @@ start_connection_thread_ssh (void *arg)
 }
 
 
-#ifdef HAVE_INOTIFY_INIT
-/* Read an inotify event and return true if it matches NAME.  */
-static int
-my_inotify_is_name (int fd, const char *name)
-{
-  union {
-    struct inotify_event ev;
-    char _buf[sizeof (struct inotify_event) + 100 + 1];
-  } buf;
-  int n;
-
-  n = npth_read (fd, &buf, sizeof buf);
-  if (n < sizeof (struct inotify_event))
-    return 0;
-  if (buf.ev.len < strlen (name)+1)
-    return 0;
-  if (strcmp (buf.ev.name, name))
-    return 0; /* Not the desired file.  */
-
-  return 1; /* Found.  */
-}
-#endif /*HAVE_INOTIFY_INIT*/
-
-
-
 /* Connection handler loop.  Wait for connection requests and spawn a
    thread after accepting a connection.  */
 static void
@@ -2714,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;
@@ -2729,9 +2745,7 @@ handle_connections (gnupg_fd_t listen_fd,
   HANDLE events[2];
   unsigned int events_set;
 #endif
-#ifdef HAVE_INOTIFY_INIT
-  int my_inotify_fd;
-#endif /*HAVE_INOTIFY_INIT*/
+  int my_inotify_fd = -1;
   struct {
     const char *name;
     void *(*func) (void *arg);
@@ -2769,27 +2783,14 @@ handle_connections (gnupg_fd_t listen_fd,
 # endif
 #endif
 
-#ifdef HAVE_INOTIFY_INIT
   if (disable_check_own_socket)
     my_inotify_fd = -1;
-  else if ((my_inotify_fd = inotify_init ()) == -1)
-    log_info ("error enabling fast daemon termination: %s\n",
-              strerror (errno));
-  else
+  else if ((err = gnupg_inotify_watch_socket (&my_inotify_fd, socket_name)))
     {
-      /* We need to watch the directory for the file because there
-       * won't be an IN_DELETE_SELF for a socket file.  */
-      char *slash = strrchr (socket_name, '/');
-      log_assert (slash && slash[1]);
-      *slash = 0;
-      if (inotify_add_watch (my_inotify_fd, socket_name, IN_DELETE) == -1)
-        {
-          close (my_inotify_fd);
-          my_inotify_fd = -1;
-        }
-      *slash = '/';
+      if (gpg_err_code (err) != GPG_ERR_NOT_SUPPORTED)
+        log_info ("error enabling fast daemon termination: %s\n",
+                  gpg_strerror (err));
     }
-#endif /*HAVE_INOTIFY_INIT*/
 
   /* 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
@@ -2832,14 +2833,12 @@ handle_connections (gnupg_fd_t listen_fd,
       if (FD2INT (listen_fd_ssh) > nfd)
         nfd = FD2INT (listen_fd_ssh);
     }
-#ifdef HAVE_INOTIFY_INIT
   if (my_inotify_fd != -1)
     {
       FD_SET (my_inotify_fd, &fdset);
       if (my_inotify_fd > nfd)
         nfd = my_inotify_fd;
     }
-#endif /*HAVE_INOTIFY_INIT*/
 
   listentbl[0].l_fd = listen_fd;
   listentbl[1].l_fd = listen_fd_extra;
@@ -2858,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,
@@ -2914,14 +2924,13 @@ handle_connections (gnupg_fd_t listen_fd,
           ctrl_t ctrl;
           npth_t thread;
 
-#ifdef HAVE_INOTIFY_INIT
-          if (my_inotify_fd != -1 && FD_ISSET (my_inotify_fd, &read_fdset)
-              && my_inotify_is_name (my_inotify_fd, GPG_AGENT_SOCK_NAME))
+          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");
             }
-#endif /*HAVE_INOTIFY_INIT*/
 
           for (idx=0; idx < DIM(listentbl); idx++)
             {
@@ -2964,15 +2973,12 @@ handle_connections (gnupg_fd_t listen_fd,
                       xfree (ctrl);
                     }
                 }
-              fd = GNUPG_INVALID_FD;
             }
         }
     }
 
-#ifdef HAVE_INOTIFY_INIT
   if (my_inotify_fd != -1)
     close (my_inotify_fd);
-#endif /*HAVE_INOTIFY_INIT*/
   cleanup ();
   log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
   npth_attr_destroy (&tattr);
@@ -3047,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");
     }