agent: Make digest algorithms for ssh fingerprints configurable.
[gnupg.git] / agent / gpg-agent.c
index 096d057..77b811c 100644 (file)
@@ -1,7 +1,6 @@
 /* gpg-agent.c  -  The GnuPG Agent
- * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009,
- *               2010 Free Software Foundation, Inc.
- * Copyright (C) 2013 Werner Koch
+ * Copyright (C) 2000-2007, 2009-2010 Free Software Foundation, Inc.
+ * Copyright (C) 2000-2016 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -16,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>
 #endif
 #include <npth.h>
 
-#define JNLIB_NEED_LOG_LOGV
-#define JNLIB_NEED_AFLOCAL
+#define GNUPG_COMMON_NEED_AFLOCAL
 #include "agent.h"
 #include <assuan.h> /* Malloc hooks  and socket wrappers. */
 
-#include "i18n.h"
-#include "mkdtemp.h" /* Gnulib replacement. */
-#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"
 
 
@@ -81,6 +77,8 @@ enum cmd_and_opt_values
   oDebugAll,
   oDebugLevel,
   oDebugWait,
+  oDebugQuickRandom,
+  oDebugPinentry,
   oNoGreeting,
   oNoOptions,
   oHomedir,
@@ -89,10 +87,13 @@ enum cmd_and_opt_values
   oLogFile,
   oServer,
   oDaemon,
+  oSupervised,
   oBatch,
 
   oPinentryProgram,
   oPinentryTouchFile,
+  oPinentryInvisibleChar,
+  oPinentryTimeout,
   oDisplay,
   oTTYname,
   oTTYtype,
@@ -110,8 +111,11 @@ enum cmd_and_opt_values
   oCheckPassphrasePattern,
   oMaxPassphraseDays,
   oEnablePassphraseHistory,
+  oEnableExtendedKeyFormat,
   oUseStandardSocket,
   oNoUseStandardSocket,
+  oExtraSocket,
+  oBrowserSocket,
   oFakedSystemTime,
 
   oIgnoreCacheForSigning,
@@ -119,9 +123,13 @@ enum cmd_and_opt_values
   oNoAllowMarkTrusted,
   oAllowPresetPassphrase,
   oAllowLoopbackPinentry,
+  oNoAllowLoopbackPinentry,
+  oNoAllowExternalCache,
+  oAllowEmacsPinentry,
   oKeepTTY,
   oKeepDISPLAY,
   oSSHSupport,
+  oSSHFingerprintDigest,
   oPuttySupport,
   oDisableScdaemon,
   oDisableCheckOwnSocket,
@@ -129,90 +137,137 @@ enum cmd_and_opt_values
 };
 
 
+#ifndef ENAMETOOLONG
+# define ENAMETOOLONG EINVAL
+#endif
+
 
 static ARGPARSE_OPTS opts[] = {
 
-  { aGPGConfList, "gpgconf-list", 256, "@" },
-  { aGPGConfTest, "gpgconf-test", 256, "@" },
-  { aUseStandardSocketP, "use-standard-socket-p", 256, "@" },
-
-  { 301, NULL, 0, N_("@Options:\n ") },
-
-  { oDaemon,   "daemon",     0, N_("run in daemon mode (background)") },
-  { oServer,   "server",     0, N_("run in server mode (foreground)") },
-  { oVerbose, "verbose",     0, N_("verbose") },
-  { oQuiet,    "quiet",     0, N_("be somewhat more quiet") },
-  { oSh,       "sh",        0, N_("sh-style command output") },
-  { oCsh,      "csh",       0, N_("csh-style command output") },
-  { oOptions, "options"  , 2, N_("|FILE|read options from FILE")},
-  { oDebug,    "debug"     ,4|16, "@"},
-  { oDebugAll, "debug-all"     ,0, "@"},
-  { oDebugLevel, "debug-level" ,2, "@"},
-  { oDebugWait,"debug-wait",1, "@"},
-  { 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")},
-  { 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") },
-  { oPinentryTouchFile, "pinentry-touch-file", 2 , "@" },
-  { oScdaemonProgram, "scdaemon-program", 2 ,
-                               N_("|PGM|use PGM as the SCdaemon program") },
-  { oDisableScdaemon, "disable-scdaemon", 0, N_("do not use the SCdaemon") },
-  { oDisableCheckOwnSocket, "disable-check-own-socket", 0, "@" },
-  { oFakedSystemTime, "faked-system-time", 2, "@" }, /* (epoch time) */
-
-  { oBatch,      "batch",       0, "@" },
-  { oHomedir,    "homedir",     2, "@"},
-
-  { oDisplay,    "display",     2, "@" },
-  { oTTYname,    "ttyname",     2, "@" },
-  { oTTYtype,    "ttytype",     2, "@" },
-  { oLCctype,    "lc-ctype",    2, "@" },
-  { oLCmessages, "lc-messages", 2, "@" },
-  { oXauthority, "xauthority",  2, "@" },
-  { oKeepTTY,    "keep-tty",    0,  N_("ignore requests to change the TTY")},
-  { oKeepDISPLAY, "keep-display",
-                          0, N_("ignore requests to change the X display")},
-
-  { 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, "@" },
-
-  { oEnforcePassphraseConstraints, "enforce-passphrase-constraints", 0, "@"},
-  { oMinPassphraseLen, "min-passphrase-len", 4, "@" },
-  { oMinPassphraseNonalpha, "min-passphrase-nonalpha", 4, "@" },
-  { oCheckPassphrasePattern, "check-passphrase-pattern", 2, "@" },
-  { oMaxPassphraseDays, "max-passphrase-days", 4, "@" },
-  { oEnablePassphraseHistory, "enable-passphrase-history", 0, "@" },
-
-  { oIgnoreCacheForSigning, "ignore-cache-for-signing", 0,
-                               N_("do not use the PIN cache when signing")},
-  { oNoAllowMarkTrusted, "no-allow-mark-trusted", 0,
-                            N_("disallow clients to mark keys as \"trusted\"")},
-  { oAllowMarkTrusted, "allow-mark-trusted", 0, "@"},
-  { oAllowPresetPassphrase, "allow-preset-passphrase", 0,
-                             N_("allow presetting passphrase")},
-  { oAllowLoopbackPinentry, "allow-loopback-pinentry", 0,
-                             N_("allow presetting passphrase")},
-  { oSSHSupport, "enable-ssh-support", 0, N_("enable ssh support") },
-  { oPuttySupport, "enable-putty-support", 0,
+  ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
+  ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"),
+  ARGPARSE_c (aUseStandardSocketP, "use-standard-socket-p", "@"),
+
+  ARGPARSE_group (301, N_("@Options:\n ")),
+
+  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")),
+  ARGPARSE_s_n (oCsh,    "csh",       N_("csh-style command output")),
+  ARGPARSE_s_s (oOptions, "options", N_("|FILE|read options from FILE")),
+
+  ARGPARSE_s_s (oDebug,             "debug",       "@"),
+  ARGPARSE_s_n (oDebugAll,   "debug-all",   "@"),
+  ARGPARSE_s_s (oDebugLevel, "debug-level", "@"),
+  ARGPARSE_s_i (oDebugWait,"  debug-wait",  "@"),
+  ARGPARSE_s_n (oDebugQuickRandom, "debug-quick-random", "@"),
+  ARGPARSE_s_n (oDebugPinentry, "debug-pinentry", "@"),
+
+  ARGPARSE_s_n (oNoDetach,  "no-detach", N_("do not detach from the console")),
+  ARGPARSE_s_n (oNoGrab,    "no-grab",   N_("do not grab keyboard and mouse")),
+  ARGPARSE_s_s (oLogFile,   "log-file",  N_("use a log file for the server")),
+  ARGPARSE_s_s (oPinentryProgram, "pinentry-program",
+                /* */             N_("|PGM|use PGM as the PIN-Entry program")),
+  ARGPARSE_s_s (oPinentryTouchFile, "pinentry-touch-file", "@"),
+  ARGPARSE_s_s (oPinentryInvisibleChar, "pinentry-invisible-char", "@"),
+  ARGPARSE_s_u (oPinentryTimeout, "pinentry-timeout", "@"),
+  ARGPARSE_s_s (oScdaemonProgram, "scdaemon-program",
+                /* */             N_("|PGM|use PGM as the SCdaemon program") ),
+  ARGPARSE_s_n (oDisableScdaemon, "disable-scdaemon",
+                /* */             N_("do not use the SCdaemon") ),
+  ARGPARSE_s_n (oDisableCheckOwnSocket, "disable-check-own-socket", "@"),
+
+  ARGPARSE_s_s (oExtraSocket, "extra-socket",
+                /* */       N_("|NAME|accept some commands via NAME")),
+
+  ARGPARSE_s_s (oBrowserSocket, "browser-socket", "@"),
+
+  ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"),
+
+  ARGPARSE_s_n (oBatch,      "batch",        "@"),
+  ARGPARSE_s_s (oHomedir,    "homedir",      "@"),
+
+  ARGPARSE_s_s (oDisplay,    "display",     "@"),
+  ARGPARSE_s_s (oTTYname,    "ttyname",     "@"),
+  ARGPARSE_s_s (oTTYtype,    "ttytype",     "@"),
+  ARGPARSE_s_s (oLCctype,    "lc-ctype",    "@"),
+  ARGPARSE_s_s (oLCmessages, "lc-messages", "@"),
+  ARGPARSE_s_s (oXauthority, "xauthority",  "@"),
+  ARGPARSE_s_n (oKeepTTY,    "keep-tty",
+                /* */        N_("ignore requests to change the TTY")),
+  ARGPARSE_s_n (oKeepDISPLAY, "keep-display",
+                /* */        N_("ignore requests to change the X display")),
+
+  ARGPARSE_s_u (oDefCacheTTL,    "default-cache-ttl",
+                                 N_("|N|expire cached PINs after N seconds")),
+  ARGPARSE_s_u (oDefCacheTTLSSH, "default-cache-ttl-ssh", "@" ),
+  ARGPARSE_s_u (oMaxCacheTTL,    "max-cache-ttl",         "@" ),
+  ARGPARSE_s_u (oMaxCacheTTLSSH, "max-cache-ttl-ssh",     "@" ),
+
+  ARGPARSE_s_n (oEnforcePassphraseConstraints, "enforce-passphrase-constraints",
+                /* */                          "@"),
+  ARGPARSE_s_u (oMinPassphraseLen,        "min-passphrase-len", "@"),
+  ARGPARSE_s_u (oMinPassphraseNonalpha,   "min-passphrase-nonalpha", "@"),
+  ARGPARSE_s_s (oCheckPassphrasePattern,  "check-passphrase-pattern", "@"),
+  ARGPARSE_s_u (oMaxPassphraseDays,       "max-passphrase-days", "@"),
+  ARGPARSE_s_n (oEnablePassphraseHistory, "enable-passphrase-history", "@"),
+
+  ARGPARSE_s_n (oIgnoreCacheForSigning, "ignore-cache-for-signing",
+                /* */    N_("do not use the PIN cache when signing")),
+  ARGPARSE_s_n (oNoAllowExternalCache,  "no-allow-external-cache",
+                /* */    N_("disallow the use of an external password cache")),
+  ARGPARSE_s_n (oNoAllowMarkTrusted, "no-allow-mark-trusted",
+                /* */    N_("disallow clients to mark keys as \"trusted\"")),
+  ARGPARSE_s_n (oAllowMarkTrusted,   "allow-mark-trusted", "@"),
+  ARGPARSE_s_n (oAllowPresetPassphrase, "allow-preset-passphrase",
+                /* */                    N_("allow presetting passphrase")),
+  ARGPARSE_s_n (oNoAllowLoopbackPinentry, "no-allow-loopback-pinentry",
+                                N_("disallow caller to override the pinentry")),
+  ARGPARSE_s_n (oAllowLoopbackPinentry, "allow-loopback-pinentry", "@"),
+  ARGPARSE_s_n (oAllowEmacsPinentry,  "allow-emacs-pinentry",
+                /* */    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")
+                /* */           N_("enable putty support")
 #else
-      "@"
+                /* */           "@"
 #endif
-  },
-  { oWriteEnvFile, "write-env-file", 2|8,
-            N_("|FILE|write environment settings also to FILE")},
-  {0}
+                ),
+  ARGPARSE_s_n (oEnableExtendedKeyFormat, "enable-extended-key-format", "@"),
+
+  /* Dummy options for backward compatibility.  */
+  ARGPARSE_o_s (oWriteEnvFile, "write-env-file", "@"),
+  ARGPARSE_s_n (oUseStandardSocket, "use-standard-socket", "@"),
+  ARGPARSE_s_n (oNoUseStandardSocket, "no-use-standard-socket", "@"),
+
+  {0} /* End of list */
 };
 
 
+/* The list of supported debug flags.  */
+static struct debug_flags_s debug_flags [] =
+  {
+    { DBG_MPI_VALUE    , "mpi"     },
+    { DBG_CRYPTO_VALUE , "crypto"  },
+    { DBG_MEMORY_VALUE , "memory"  },
+    { DBG_CACHE_VALUE  , "cache"   },
+    { DBG_MEMSTAT_VALUE, "memstat" },
+    { DBG_HASHING_VALUE, "hashing" },
+    { DBG_IPC_VALUE    , "ipc"     },
+    { 77, NULL } /* 77 := Do not exit on "help" or "?".  */
+  };
+
+
+
 #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 */
@@ -238,6 +293,9 @@ static ARGPARSE_OPTS opts[] = {
 #endif
 
 
+/* Flag indicating that the ssh-agent subsystem has been enabled.  */
+static int ssh_support;
+
 #ifdef HAVE_W32_SYSTEM
 /* Flag indicating that support for Putty has been enabled.  */
 static int putty_support;
@@ -250,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
@@ -268,18 +328,38 @@ 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;
 
-/* Name of the communication socket used for native gpg-agent requests.  */
+/* Name of the communication socket used for native gpg-agent
+   requests. The second variable is either NULL or a malloced string
+   with the real socket name in case it has been redirected.  */
 static char *socket_name;
+static char *redir_socket_name;
 
-/* Name of the communication socket used for ssh-agent-emulation.  */
+/* Name of the optional extra socket used for native gpg-agent requests.  */
+static char *socket_name_extra;
+static char *redir_socket_name_extra;
+
+/* Name of the optional browser socket used for native gpg-agent requests.  */
+static char *socket_name_browser;
+static char *redir_socket_name_browser;
+
+/* Name of the communication socket used for ssh-agent protocol.  */
 static char *socket_name_ssh;
+static char *redir_socket_name_ssh;
 
 /* We need to keep track of the server's nonces (these are dummies for
    POSIX systems). */
 static assuan_sock_nonce_t socket_nonce;
+static assuan_sock_nonce_t socket_nonce_extra;
+static assuan_sock_nonce_t socket_nonce_browser;
 static assuan_sock_nonce_t socket_nonce_ssh;
 
 
@@ -309,23 +389,55 @@ static pid_t parent_pid = (pid_t)(-1);
 /* Number of active connections.  */
 static int active_connections;
 
+/* This object is used to dispatch progress messages from Libgcrypt to
+ * 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;
+  /* The control object of the connection.  If this is NULL no
+   * connection is associated with this item and it is free for reuse
+   * by new connections.  */
+  ctrl_t ctrl;
+
+  /* The thread id of (npth_self) of the connection.  */
+  npth_t tid;
+
+  /* The callback set by the connection.  This is similar to the
+   * Libgcrypt callback but with the control object passed as the
+   * first argument.  */
+  void (*cb)(ctrl_t ctrl,
+             const char *what, int printchar,
+             int current, int total);
+};
+struct progress_dispatch_s *progress_dispatch_list;
+
+
+
 \f
 /*
    Local prototypes.
  */
 
-static char *create_socket_name (char *standard_name, char *template);
-static gnupg_fd_t create_server_socket (char *name, int is_ssh,
+static char *create_socket_name (char *standard_name, int with_homedir);
+static gnupg_fd_t create_server_socket (char *name, int primary, int cygwin,
+                                        char **r_redir_name,
                                         assuan_sock_nonce_t *nonce);
 static void create_directories (void);
 
+static void agent_libgcrypt_progress_cb (void *data, const char *what,
+                                         int printchar,
+                                         int current, int total);
 static void agent_init_default_ctrl (ctrl_t ctrl);
 static void agent_deinit_default_ctrl (ctrl_t ctrl);
 
 static void handle_connections (gnupg_fd_t listen_fd,
+                                gnupg_fd_t listen_fd_extra,
+                                gnupg_fd_t listen_fd_browser,
                                 gnupg_fd_t listen_fd_ssh);
 static void check_own_socket (void);
-static int check_for_running_agent (int silent, int mode);
+static int check_for_running_agent (int silent);
 
 /* Pth wrapper function definitions. */
 ASSUAN_SYSTEM_NPTH_IMPL;
@@ -412,12 +524,11 @@ set_debug (void)
   else if (!strcmp (debug_level, "none") || (numok && numlvl < 1))
     opt.debug = 0;
   else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2))
-    opt.debug = DBG_ASSUAN_VALUE;
+    opt.debug = DBG_IPC_VALUE;
   else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5))
-    opt.debug = DBG_ASSUAN_VALUE|DBG_COMMAND_VALUE;
+    opt.debug = DBG_IPC_VALUE;
   else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8))
-    opt.debug = (DBG_ASSUAN_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;
@@ -447,39 +558,188 @@ set_debug (void)
   gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
 
   if (opt.debug)
-    log_info ("enabled debug flags:%s%s%s%s%s%s%s%s\n",
-              (opt.debug & DBG_COMMAND_VALUE)? " command":"",
-              (opt.debug & DBG_MPI_VALUE    )? " mpi":"",
-              (opt.debug & DBG_CRYPTO_VALUE )? " crypto":"",
-              (opt.debug & DBG_MEMORY_VALUE )? " memory":"",
-              (opt.debug & DBG_CACHE_VALUE  )? " cache":"",
-              (opt.debug & DBG_MEMSTAT_VALUE)? " memstat":"",
-              (opt.debug & DBG_HASHING_VALUE)? " hashing":"",
-              (opt.debug & DBG_ASSUAN_VALUE )? " assuan":"");
+    parse_debug_flag (NULL, &opt.debug, debug_flags);
 }
 
 
-/* Helper for cleanup to remove one socket with NAME.  */
+/* Helper for cleanup to remove one socket with NAME.  REDIR_NAME is
+   the corresponding real name if the socket has been redirected.  */
 static void
-remove_socket (char *name)
+remove_socket (char *name, char *redir_name)
 {
   if (name && *name)
     {
-      char *p;
+      if (redir_name)
+        name = redir_name;
 
       gnupg_remove (name);
-      p = strrchr (name, '/');
-      if (p)
-       {
-         *p = 0;
-         rmdir (name);
-         *p = '/';
-       }
       *name = 0;
     }
 }
 
 
+/* 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
@@ -491,8 +751,15 @@ cleanup (void)
     return;
   done = 1;
   deinitialize_module_cache ();
-  remove_socket (socket_name);
-  remove_socket (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);
+    }
 }
 
 
@@ -511,8 +778,12 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
       opt.verbose = 0;
       opt.debug = 0;
       opt.no_grab = 0;
+      opt.debug_pinentry = 0;
       opt.pinentry_program = NULL;
       opt.pinentry_touch_file = NULL;
+      xfree (opt.pinentry_invisible_char);
+      opt.pinentry_invisible_char = NULL;
+      opt.pinentry_timeout = 0;
       opt.scdaemon_program = NULL;
       opt.def_cache_ttl = DEFAULT_CACHE_TTL;
       opt.def_cache_ttl_ssh = DEFAULT_CACHE_TTL_SSH;
@@ -523,11 +794,16 @@ 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;
+      opt.allow_loopback_pinentry = 1;
+      opt.allow_emacs_pinentry = 0;
       opt.disable_scdaemon = 0;
       disable_check_own_socket = 0;
+      opt.ssh_fingerprint_digest = GCRY_MD_MD5;
       return 1;
     }
 
@@ -536,9 +812,12 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
     case oQuiet: opt.quiet = 1; break;
     case oVerbose: opt.verbose++; break;
 
-    case oDebug: opt.debug |= pargs->r.ret_ulong; break;
+    case oDebug:
+      parse_debug_flag (pargs->r.ret_str, &opt.debug, debug_flags);
+      break;
     case oDebugAll: opt.debug = ~0; break;
     case oDebugLevel: debug_level = pargs->r.ret_str; break;
+    case oDebugPinentry: opt.debug_pinentry = 1; break;
 
     case oLogFile:
       if (!reread)
@@ -556,6 +835,11 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
 
     case oPinentryProgram: opt.pinentry_program = pargs->r.ret_str; break;
     case oPinentryTouchFile: opt.pinentry_touch_file = pargs->r.ret_str; break;
+    case oPinentryInvisibleChar:
+      xfree (opt.pinentry_invisible_char);
+      opt.pinentry_invisible_char = xtrystrdup (pargs->r.ret_str); break;
+      break;
+    case oPinentryTimeout: opt.pinentry_timeout = pargs->r.ret_ulong; break;
     case oScdaemonProgram: opt.scdaemon_program = pargs->r.ret_str; break;
     case oDisableScdaemon: opt.disable_scdaemon = 1; break;
     case oDisableCheckOwnSocket: disable_check_own_socket = 1; break;
@@ -579,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;
@@ -590,6 +878,13 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
     case oAllowPresetPassphrase: opt.allow_preset_passphrase = 1; break;
 
     case oAllowLoopbackPinentry: opt.allow_loopback_pinentry = 1; break;
+    case oNoAllowLoopbackPinentry: opt.allow_loopback_pinentry = 0; break;
+
+    case oNoAllowExternalCache: opt.allow_external_cache = 0;
+      break;
+
+    case oAllowEmacsPinentry: opt.allow_emacs_pinentry = 1;
+      break;
 
     default:
       return 0; /* not handled */
@@ -599,6 +894,47 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
 }
 
 
+/* Fixup some options after all have been processed.  */
+static void
+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
 main (int argc, char **argv )
@@ -620,13 +956,16 @@ main (int argc, char **argv )
   int debug_wait = 0;
   int gpgconf_list = 0;
   gpg_error_t err;
-  const char *env_file_name = NULL;
   struct assuan_malloc_hooks malloc_hooks;
 
+  early_system_init ();
+
   /* 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;
@@ -638,48 +977,33 @@ main (int argc, char **argv )
   /* Please note that we may running SUID(ROOT), so be very CAREFUL
      when adding any stuff between here and the call to INIT_SECMEM()
      somewhere after the option parsing */
-  log_set_prefix (GPG_AGENT_NAME, JNLIB_LOG_WITH_PREFIX|JNLIB_LOG_WITH_PID);
+  log_set_prefix (GPG_AGENT_NAME, GPGRT_LOG_WITH_PREFIX|GPGRT_LOG_WITH_PID);
 
   /* Make sure that our subsystems are ready.  */
   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);
+  gcry_set_progress_handler (agent_libgcrypt_progress_cb, NULL);
 
   disable_core_dumps ();
 
   /* Set default options.  */
   parse_rereadable_options (NULL, 0); /* Reset them to default values. */
-#ifdef USE_STANDARD_SOCKET
-  opt.use_standard_socket = 1;
-#endif
 
   shell = getenv ("SHELL");
   if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") )
     csh_style = 1;
 
-  opt.homedir = default_homedir ();
-
   /* Record some of the original environment strings. */
   {
     const char *s;
@@ -735,11 +1059,16 @@ main (int argc, char **argv )
        else if (pargs.r_opt == oNoOptions)
           default_config = 0; /* --no-options */
        else if (pargs.r_opt == oHomedir)
-          opt.homedir = pargs.r.ret_str;
+          gnupg_set_homedir (pargs.r.ret_str);
+       else if (pargs.r_opt == oDebugQuickRandom)
+          {
+            gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+          }
+
     }
 
   /* Initialize the secure memory. */
-  gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
+  gcry_control (GCRYCTL_INIT_SECMEM, SECMEM_BUFFER_SIZE, 0);
   maybe_setuid = 0;
 
   /*
@@ -747,8 +1076,8 @@ main (int argc, char **argv )
   */
 
   if (default_config)
-    configname = make_filename (opt.homedir, GPG_AGENT_NAME EXTSEP_S "conf",
-                                NULL );
+    configname = make_filename (gnupg_homedir (),
+                                GPG_AGENT_NAME EXTSEP_S "conf", NULL);
 
   argc = orig_argc;
   argv = orig_argv;
@@ -765,7 +1094,7 @@ main (int argc, char **argv )
           if (default_config)
             {
               if( parse_debug )
-                log_info (_("NOTE: no default option file '%s'\n"),
+                log_info (_("Note: no default option file '%s'\n"),
                           configname );
               /* Save the default conf file name so that
                  reread_configuration is able to test whether the
@@ -813,13 +1142,14 @@ main (int argc, char **argv )
         case oNoGreeting: /* Dummy option.  */ break;
         case oNoVerbose: opt.verbose = 0; break;
         case oNoOptions: break; /* no-options */
-        case oHomedir: opt.homedir = pargs.r.ret_str; break;
+        case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
         case oNoDetach: nodetach = 1; break;
         case oLogFile: logfile = pargs.r.ret_str; break;
         case oCsh: csh_style = 1; break;
         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;
@@ -830,8 +1160,10 @@ main (int argc, char **argv )
         case oXauthority: default_xauthority = xstrdup (pargs.r.ret_str);
           break;
 
-        case oUseStandardSocket:   opt.use_standard_socket = 1; break;
-        case oNoUseStandardSocket: opt.use_standard_socket = 0; break;
+        case oUseStandardSocket:
+        case oNoUseStandardSocket:
+          obsolete_option (configname, configlineno, "use-standard-socket");
+          break;
 
         case oFakedSystemTime:
           {
@@ -845,19 +1177,36 @@ main (int argc, char **argv )
         case oKeepTTY: opt.keep_tty = 1; break;
         case oKeepDISPLAY: opt.keep_display = 1; break;
 
-       case oSSHSupport:  opt.ssh_support = 1; break;
+       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;
-          opt.ssh_support = 1;
 #        endif
           break;
 
+        case oExtraSocket:
+          opt.extra_socket = 1;  /* (1 = points into argv)  */
+          socket_name_extra = pargs.r.ret_str;
+          break;
+
+        case oBrowserSocket:
+          opt.browser_socket = 1;  /* (1 = points into argv)  */
+          socket_name_browser = pargs.r.ret_str;
+          break;
+
+        case oDebugQuickRandom:
+          /* Only used by the first stage command line parser.  */
+          break;
+
         case oWriteEnvFile:
-          if (pargs.r_type)
-            env_file_name = pargs.r.ret_str;
-          else
-            env_file_name = make_filename ("~/.gpg-agent-info", NULL);
+          obsolete_option (configname, configlineno, "write-env-file");
           break;
 
         default : pargs.err = configfp? 1:2; break;
@@ -882,8 +1231,7 @@ main (int argc, char **argv )
   if (log_get_errorcount(0))
     exit(2);
 
-  /* Turn the homedir into an absolute one. */
-  opt.homedir = make_absfilename (opt.homedir, NULL);
+  finalize_rereadable_options ();
 
   /* Print a warning if an argument looks like an option.  */
   if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
@@ -892,7 +1240,7 @@ main (int argc, char **argv )
 
       for (i=0; i < argc; i++)
         if (argv[i][0] == '-' && argv[i][1] == '-')
-          log_info (_("NOTE: '%s' is not considered an option\n"), argv[i]);
+          log_info (_("Note: '%s' is not considered an option\n"), argv[i]);
     }
 
 #ifdef ENABLE_NLS
@@ -906,18 +1254,44 @@ 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. */
       opt.debug = 0;
       set_debug ();
-      check_for_running_agent (0, 0);
+      check_for_running_agent (0);
       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))
@@ -927,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);
@@ -945,9 +1315,9 @@ main (int argc, char **argv )
 
   if (gpgconf_list == 3)
     {
-      if (opt.use_standard_socket && !opt.quiet)
-        log_info ("configured to use the standard socket\n");
-      agent_exit (!opt.use_standard_socket);
+      /* We now use the standard socket always - return true for
+         backward compatibility.  */
+      agent_exit (0);
     }
   else if (gpgconf_list == 2)
     agent_exit (0);
@@ -957,8 +1327,8 @@ main (int argc, char **argv )
       char *filename_esc;
 
       /* List options and default values in the GPG Conf format.  */
-      filename = make_filename (opt.homedir, GPG_AGENT_NAME EXTSEP_S "conf",
-                                NULL );
+      filename = make_filename (gnupg_homedir (),
+                                GPG_AGENT_NAME EXTSEP_S "conf", NULL);
       filename_esc = percent_escape (filename, NULL);
 
       es_printf ("%s-%s.conf:%lu:\"%s\n",
@@ -1001,15 +1371,24 @@ main (int argc, char **argv )
               GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
       es_printf ("ignore-cache-for-signing:%lu:\n",
               GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
+      es_printf ("no-allow-external-cache:%lu:\n",
+              GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
       es_printf ("no-allow-mark-trusted:%lu:\n",
               GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
       es_printf ("disable-scdaemon:%lu:\n",
               GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
+      es_printf ("enable-ssh-support:%lu:\n", GC_OPT_FLAG_NONE);
 #ifdef HAVE_W32_SYSTEM
       es_printf ("enable-putty-support:%lu:\n", GC_OPT_FLAG_NONE);
-#else
-      es_printf ("enable-ssh-support:%lu:\n", GC_OPT_FLAG_NONE);
 #endif
+      es_printf ("no-allow-loopback-pinentry:%lu:\n",
+              GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
+      es_printf ("allow-emacs-pinentry:%lu:\n",
+                 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);
     }
@@ -1018,9 +1397,9 @@ main (int argc, char **argv )
   if (logfile)
     {
       log_set_file (logfile);
-      log_set_prefix (NULL, (JNLIB_LOG_WITH_PREFIX
-                             |JNLIB_LOG_WITH_TIME
-                             |JNLIB_LOG_WITH_PID));
+      log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX
+                             | GPGRT_LOG_WITH_TIME
+                             | GPGRT_LOG_WITH_PID));
       current_logfile = xstrdup (logfile);
     }
 
@@ -1036,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)
         {
@@ -1056,13 +1437,61 @@ 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
     { /* Regular server mode */
       gnupg_fd_t fd;
-      gnupg_fd_t fd_ssh;
+      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
       pid_t pid;
+#endif
 
       /* Remove the DISPLAY variable so that a pinentry does not
          default to a specific display.  There is still a default
@@ -1075,20 +1504,46 @@ main (int argc, char **argv )
         gnupg_unsetenv ("DISPLAY");
 #endif
 
+      /* Remove the INSIDE_EMACS variable so that a pinentry does not
+         always try to interact with Emacs.  The variable is set when
+         a client requested this using an OPTION command.  */
+      gnupg_unsetenv ("INSIDE_EMACS");
 
       /* Create the sockets.  */
-      socket_name = create_socket_name
-        (GPG_AGENT_SOCK_NAME, "gpg-XXXXXX/"GPG_AGENT_SOCK_NAME);
+      socket_name = create_socket_name (GPG_AGENT_SOCK_NAME, 1);
+      fd = create_server_socket (socket_name, 1, 0,
+                                 &redir_socket_name, &socket_nonce);
 
-      fd = create_server_socket (socket_name, 0, &socket_nonce);
-      if (opt.ssh_support)
+      if (opt.extra_socket)
         {
-          socket_name_ssh = create_socket_name
-            (GPG_AGENT_SSH_SOCK_NAME, "gpg-XXXXXX/"GPG_AGENT_SSH_SOCK_NAME);
-          fd_ssh = create_server_socket (socket_name_ssh, 1, &socket_nonce_ssh);
+          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,
+                                           &socket_nonce_extra);
         }
-      else
-       fd_ssh = GNUPG_INVALID_FD;
+
+      if (opt.browser_socket)
+        {
+          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);
+        }
+
+      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
@@ -1097,14 +1552,15 @@ main (int argc, char **argv )
         parent_pid = getpid ();
 
       fflush (NULL);
+
 #ifdef HAVE_W32_SYSTEM
+
       (void)csh_style;
       (void)nodetach;
-      (void)env_file_name;
-      pid = getpid ();
-      es_printf ("set %s=%s;%lu;1\n",
-                 GPG_AGENT_INFO_NAME, socket_name, (ulong)pid);
+      initialize_modules ();
+
 #else /*!HAVE_W32_SYSTEM*/
+
       pid = fork ();
       if (pid == (pid_t)-1)
         {
@@ -1113,7 +1569,7 @@ main (int argc, char **argv )
         }
       else if (pid)
         { /* We are the parent */
-          char *infostr, *infostr_ssh_sock, *infostr_ssh_valid;
+          char *infostr_ssh_sock, *infostr_ssh_valid;
 
           /* Close the socket FD. */
           close (fd);
@@ -1133,15 +1589,8 @@ main (int argc, char **argv )
             log_info ("no saved signal mask\n");
 #endif /*HAVE_SIGPROCMASK*/
 
-          /* Create the info string: <name>:<pid>:<protocol_version> */
-          if (asprintf (&infostr, "%s=%s:%lu:1",
-                        GPG_AGENT_INFO_NAME, socket_name, (ulong)pid ) < 0)
-            {
-              log_error ("out of core\n");
-              kill (pid, SIGTERM);
-              exit (1);
-            }
-         if (opt.ssh_support)
+          /* Create the SSH info string if enabled. */
+         if (ssh_support)
            {
              if (asprintf (&infostr_ssh_sock, "SSH_AUTH_SOCK=%s",
                            socket_name_ssh) < 0)
@@ -1161,42 +1610,16 @@ main (int argc, char **argv )
 
           *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)
-            {
-              estream_t fp;
-
-              fp = es_fopen (env_file_name, "w,mode=-rw");
-              if (!fp)
-                log_error (_("error creating '%s': %s\n"),
-                             env_file_name, strerror (errno));
-              else
-                {
-                  es_fputs (infostr, fp);
-                  es_putc ('\n', fp);
-                  if (opt.ssh_support)
-                    {
-                      es_fputs (infostr_ssh_sock, fp);
-                      es_putc ('\n', fp);
-                    }
-                  es_fclose (fp);
-                }
-            }
-
+         if (opt.extra_socket)
+           *socket_name_extra = 0;
+         if (opt.browser_socket)
+           *socket_name_browser = 0;
+          *socket_name_ssh = 0;
 
           if (argc)
             { /* Run the program given on the commandline.  */
-              if (putenv (infostr))
-                {
-                  log_error ("failed to set environment: %s\n",
-                             strerror (errno) );
-                  kill (pid, SIGTERM );
-                  exit (1);
-                }
-              if (opt.ssh_support && (putenv (infostr_ssh_sock)
-                                      || putenv (infostr_ssh_valid)))
+              if (ssh_support && (putenv (infostr_ssh_sock)
+                                  || putenv (infostr_ssh_valid)))
                 {
                   log_error ("failed to set environment: %s\n",
                              strerror (errno) );
@@ -1222,9 +1645,7 @@ main (int argc, char **argv )
                  shell's eval to set it */
               if (csh_style)
                 {
-                  *strchr (infostr, '=') = ' ';
-                  es_printf ("setenv %s;\n", infostr);
-                 if (opt.ssh_support)
+                 if (ssh_support)
                    {
                      *strchr (infostr_ssh_sock, '=') = ' ';
                      es_printf ("setenv %s;\n", infostr_ssh_sock);
@@ -1232,15 +1653,13 @@ main (int argc, char **argv )
                 }
               else
                 {
-                  es_printf ( "%s; export %s;\n", infostr, GPG_AGENT_INFO_NAME);
-                 if (opt.ssh_support)
+                 if (ssh_support)
                    {
                      es_printf ("%s; export SSH_AUTH_SOCK;\n",
                                  infostr_ssh_sock);
                    }
                 }
-              xfree (infostr);
-             if (opt.ssh_support)
+             if (ssh_support)
                {
                  xfree (infostr_ssh_sock);
                  xfree (infostr_ssh_valid);
@@ -1254,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 )
         {
@@ -1283,7 +1704,7 @@ main (int argc, char **argv )
             }
 
           log_get_prefix (&oldflags);
-          log_set_prefix (NULL, oldflags | JNLIB_LOG_RUN_DETACHED);
+          log_set_prefix (NULL, oldflags | GPGRT_LOG_RUN_DETACHED);
           opt.running_detached = 1;
         }
 
@@ -1304,7 +1725,7 @@ main (int argc, char **argv )
 #endif /*!HAVE_W32_SYSTEM*/
 
       log_info ("%s %s started\n", strusage(11), strusage(13) );
-      handle_connections (fd, opt.ssh_support ? fd_ssh : GNUPG_INVALID_FD);
+      handle_connections (fd, fd_extra, fd_browser, fd_ssh);
       assuan_sock_close (fd);
     }
 
@@ -1340,11 +1761,110 @@ agent_exit (int rc)
 }
 
 
+/* This is our callback function for gcrypt progress messages.  It is
+   set once at startup and dispatches progress messages to the
+   corresponding threads of the agent.  */
+static void
+agent_libgcrypt_progress_cb (void *data, const char *what, int printchar,
+                             int current, int total)
+{
+  struct progress_dispatch_s *dispatch;
+  npth_t mytid = npth_self ();
+
+  (void)data;
+
+  for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next)
+    if (dispatch->ctrl && dispatch->tid == mytid)
+      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
+}
+
+
+/* If a progress dispatcher callback has been associated with the
+ * current connection unregister it.  */
+static void
+unregister_progress_cb (void)
+{
+  struct progress_dispatch_s *dispatch;
+  npth_t mytid = npth_self ();
+
+  for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next)
+    if (dispatch->ctrl && dispatch->tid == mytid)
+      break;
+  if (dispatch)
+    {
+      dispatch->ctrl = NULL;
+      dispatch->cb = NULL;
+    }
+}
+
+
+/* Setup a progress callback CB for the current connection.  Using a
+ * CB of NULL disables the callback.  */
+void
+agent_set_progress_cb (void (*cb)(ctrl_t ctrl, const char *what,
+                                  int printchar, int current, int total),
+                       ctrl_t ctrl)
+{
+  struct progress_dispatch_s *dispatch, *firstfree;
+  npth_t mytid = npth_self ();
+
+  firstfree = NULL;
+  for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next)
+    {
+      if (dispatch->ctrl && dispatch->tid == mytid)
+        break;
+      if (!dispatch->ctrl && !firstfree)
+        firstfree = dispatch;
+    }
+  if (!dispatch) /* None allocated: Reuse or allocate a new one.  */
+    {
+      if (firstfree)
+        {
+          dispatch = firstfree;
+        }
+      else if ((dispatch = xtrycalloc (1, sizeof *dispatch)))
+        {
+          dispatch->next = progress_dispatch_list;
+          progress_dispatch_list = dispatch;
+        }
+      else
+        {
+          log_error ("error allocating new progress dispatcher slot: %s\n",
+                     gpg_strerror (gpg_error_from_syserror ()));
+          return;
+        }
+      dispatch->ctrl = ctrl;
+      dispatch->tid = mytid;
+    }
+
+  dispatch->cb = cb;
+}
+
+
 /* Each thread has its own local variables conveyed by a control
    structure usually identified by an argument named CTRL.  This
    function is called immediately after allocating the control
    structure.  Its purpose is to setup the default values for that
-   structure.  */
+   structure.  Note that some values may have already been set.  */
 static void
 agent_init_default_ctrl (ctrl_t ctrl)
 {
@@ -1376,6 +1896,7 @@ agent_init_default_ctrl (ctrl_t ctrl)
 static void
 agent_deinit_default_ctrl (ctrl_t ctrl)
 {
+  unregister_progress_cb ();
   session_env_release (ctrl->session_env);
 
   if (ctrl->lc_ctype)
@@ -1385,6 +1906,39 @@ agent_deinit_default_ctrl (ctrl_t ctrl)
 }
 
 
+/* Because the ssh protocol does not send us information about the
+   current TTY setting, we use this function to use those from startup
+   or those explicitly set.  This is also used for the restricted mode
+   where we ignore requests to change the environment.  */
+gpg_error_t
+agent_copy_startup_env (ctrl_t ctrl)
+{
+  static const char *names[] =
+    {"GPG_TTY", "DISPLAY", "TERM", "XAUTHORITY", "PINENTRY_USER_DATA", NULL};
+  gpg_error_t err = 0;
+  int idx;
+  const char *value;
+
+  for (idx=0; !err && names[idx]; 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)
+    if (!(ctrl->lc_ctype = xtrystrdup (opt.startup_lc_ctype)))
+      err = gpg_error_from_syserror ();
+
+  if (!err && !ctrl->lc_messages && opt.startup_lc_messages)
+    if (!(ctrl->lc_messages = xtrystrdup (opt.startup_lc_messages)))
+      err = gpg_error_from_syserror ();
+
+  if (err)
+    log_error ("error setting default session environment: %s\n",
+               gpg_strerror (err));
+
+  return err;
+}
+
+
 /* Reread parts of the configuration.  Note, that this function is
    obviously not thread-safe and should only be called from the PTH
    signal handler.
@@ -1427,6 +1981,7 @@ reread_configuration (void)
         parse_rereadable_options (&pargs, 1);
     }
   fclose (fp);
+  finalize_rereadable_options ();
   set_debug ();
 }
 
@@ -1452,6 +2007,14 @@ get_agent_ssh_socket_name (void)
 }
 
 
+/* Return the number of active connections. */
+int
+get_agent_active_connection_count (void)
+{
+  return active_connections;
+}
+
+
 /* Under W32, this function returns the handle of the scdaemon
    notification event.  Calling it the first time creates that
    event.  */
@@ -1496,111 +2059,108 @@ get_agent_scd_notify_event (void)
 
 
 
-/* Create a name for the socket.  With USE_STANDARD_SOCKET given as
-   true using STANDARD_NAME in the home directory or if given as
-   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.  Returns: Pointer to an
-   allocated string with the absolute name of the socket used.  */
+/* Create a name for the socket in the home directory as using
+   STANDARD_NAME.  We also 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.  Returns:
+   Pointer to an allocated string with the absolute name of the socket
+   used.  */
 static char *
-create_socket_name (char *standard_name, char *template)
+create_socket_name (char *standard_name, int with_homedir)
 {
-  char *name, *p;
+  char *name;
 
-  if (opt.use_standard_socket)
-    name = make_filename (opt.homedir, standard_name, NULL);
+  if (with_homedir)
+    name = make_filename (gnupg_socketdir (), standard_name, NULL);
   else
-    {
-      /* Prepend the tmp directory to the template.  */
-      p = getenv ("TMPDIR");
-      if (!p || !*p)
-        p = "/tmp";
-      if (p[strlen (p) - 1] == '/')
-        name = xstrconcat (p, template, NULL);
-      else
-        name = xstrconcat (p, "/", template, NULL);
-
-      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 = '/';
-    }
-
+    name = make_filename (standard_name, NULL);
   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.  Returns the file descriptor
-   or terminates the process in case of an error.  Not that this
-   function needs to be used for the regular socket first and only
-   then for the ssh socket.  */
+   or terminates the process in case of an error.  Note that this
+   function needs to be used for the regular socket first (indicated
+   by PRIMARY) and only then for the extra and the ssh sockets.  If
+   the socket has been redirected the name of the real socket is
+   stored as a malloced string at R_REDIR_NAME.  If CYGWIN is set a
+   Cygwin compatible socket is created (Windows only). */
 static gnupg_fd_t
-create_server_socket (char *name, int is_ssh, assuan_sock_nonce_t *nonce)
+create_server_socket (char *name, int primary, int cygwin,
+                      char **r_redir_name, assuan_sock_nonce_t *nonce)
 {
-  struct sockaddr_un *serv_addr;
+  struct sockaddr *addr;
+  struct sockaddr_un *unaddr;
   socklen_t len;
   gnupg_fd_t fd;
   int rc;
 
+  xfree (*r_redir_name);
+  *r_redir_name = NULL;
+
   fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0);
   if (fd == ASSUAN_INVALID_FD)
     {
       log_error (_("can't create socket: %s\n"), strerror (errno));
+      *name = 0; /* Inhibit removal of the socket by cleanup(). */
       agent_exit (2);
     }
 
-  serv_addr = xmalloc (sizeof (*serv_addr));
-  memset (serv_addr, 0, sizeof *serv_addr);
-  serv_addr->sun_family = AF_UNIX;
-  if (strlen (name) + 1 >= sizeof (serv_addr->sun_path))
-    {
-      log_error (_("socket name '%s' is too long\n"), name);
-      agent_exit (2);
-    }
-  strcpy (serv_addr->sun_path, name);
-  len = SUN_LEN (serv_addr);
-  rc = assuan_sock_bind (fd, (struct sockaddr*) serv_addr, len);
+  if (cygwin)
+    assuan_sock_set_flag (fd, "cygwin", 1);
+
+  unaddr = xmalloc (sizeof *unaddr);
+  addr = (struct sockaddr*)unaddr;
+
+  {
+    int redirected;
+
+    if (assuan_sock_set_sockaddr_un (name, addr, &redirected))
+      {
+        if (errno == ENAMETOOLONG)
+          log_error (_("socket name '%s' is too long\n"), name);
+        else
+          log_error ("error preparing socket '%s': %s\n",
+                     name, gpg_strerror (gpg_error_from_syserror ()));
+        *name = 0; /* Inhibit removal of the socket by cleanup(). */
+        agent_exit (2);
+      }
+    if (redirected)
+      {
+        *r_redir_name = xstrdup (unaddr->sun_path);
+        if (opt.verbose)
+          log_info ("redirecting socket '%s' to '%s'\n", name, *r_redir_name);
+      }
+  }
+
+  len = SUN_LEN (unaddr);
+  rc = assuan_sock_bind (fd, addr, len);
 
   /* Our error code mapping on W32CE returns EEXIST thus we also test
      for this. */
-  if (opt.use_standard_socket && rc == -1
+  if (rc == -1
       && (errno == EADDRINUSE
 #ifdef HAVE_W32_SYSTEM
           || errno == EEXIST
 #endif
           ))
     {
-      /* Check whether a gpg-agent is already running on the standard
-         socket.  We do this test only if this is not the ssh socket.
-         For ssh we assume that a test for gpg-agent has already been
-         done and reuse the requested ssh socket.  Testing the
-         ssh-socket is not possible because at this point, though we
-         know the new Assuan socket, the Assuan server and thus the
-         ssh-agent server is not yet operational.  This would lead to
-         a hang.  */
-      if (!is_ssh && !check_for_running_agent (1, 1))
+      /* Check whether a gpg-agent is already running.  We do this
+         test only if this is the primary socket.  For secondary
+         sockets we assume that a test for gpg-agent has already been
+         done and reuse the requested socket.  Testing the ssh-socket
+         is not possible because at this point, though we know the new
+         Assuan socket, the Assuan server and thus the ssh-agent
+         server is not yet operational; this would lead to a hang.  */
+      if (primary && !check_for_running_agent (1))
         {
-          log_set_prefix (NULL, JNLIB_LOG_WITH_PREFIX);
+          log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX);
           log_set_file (NULL);
           log_error (_("a gpg-agent is already running - "
                        "not starting a new one\n"));
@@ -1608,35 +2168,38 @@ create_server_socket (char *name, int is_ssh, assuan_sock_nonce_t *nonce)
           assuan_sock_close (fd);
           agent_exit (2);
         }
-      gnupg_remove (name);
-      rc = assuan_sock_bind (fd, (struct sockaddr*) serv_addr, len);
+      gnupg_remove (unaddr->sun_path);
+      rc = assuan_sock_bind (fd, addr, len);
     }
-  if (rc != -1
-      && (rc=assuan_sock_get_nonce ((struct sockaddr*)serv_addr, len, nonce)))
+  if (rc != -1 && (rc=assuan_sock_get_nonce (addr, len, nonce)))
     log_error (_("error getting nonce for the socket\n"));
   if (rc == -1)
     {
       /* We use gpg_strerror here because it allows us to get strings
          for some W32 socket error codes.  */
       log_error (_("error binding socket to '%s': %s\n"),
-                serv_addr->sun_path,
-                 gpg_strerror (gpg_error_from_errno (errno)));
+                unaddr->sun_path,
+                 gpg_strerror (gpg_error_from_syserror ()));
 
       assuan_sock_close (fd);
-      if (opt.use_standard_socket)
-        *name = 0; /* Inhibit removal of the socket by cleanup(). */
+      *name = 0; /* Inhibit removal of the socket by cleanup(). */
       agent_exit (2);
     }
 
+  if (gnupg_chmod (unaddr->sun_path, "-rwx"))
+    log_error (_("can't set permissions of '%s': %s\n"),
+               unaddr->sun_path, strerror (errno));
+
   if (listen (FD2INT(fd), 5 ) == -1)
     {
       log_error (_("listen() failed: %s\n"), strerror (errno));
+      *name = 0; /* Inhibit removal of the socket by cleanup(). */
       assuan_sock_close (fd);
       agent_exit (2);
     }
 
   if (opt.verbose)
-    log_info (_("listening on socket '%s'\n"), serv_addr->sun_path);
+    log_info (_("listening on socket '%s'\n"), unaddr->sun_path);
 
   return fd;
 }
@@ -1660,9 +2223,13 @@ create_private_keys_directory (const char *home)
       else if (!opt.quiet)
         log_info (_("directory '%s' created\n"), fname);
     }
+  if (gnupg_chmod (fname, "-rwx"))
+    log_error (_("can't set permissions of '%s': %s\n"),
+               fname, strerror (errno));
   xfree (fname);
 }
 
+
 /* Create the directory only if the supplied directory name is the
    same as the default one.  This way we avoid to create arbitrary
    directories when a non-default home directory is used.  To cope
@@ -1676,7 +2243,7 @@ create_directories (void)
   const char *defhome = standard_homedir ();
   char *home;
 
-  home = make_filename (opt.homedir, NULL);
+  home = make_filename (gnupg_homedir (), NULL);
   if ( stat (home, &statbuf) )
     {
       if (errno == ENOENT)
@@ -1768,9 +2335,14 @@ agent_sighup_action (void)
 {
   log_info ("SIGHUP received - "
             "re-reading configuration and flushing cache\n");
+
   agent_flush_cache ();
   reread_configuration ();
   agent_reload_trustlist ();
+  /* We flush the module name cache so that after installing a
+     "pinentry" binary that one can be used in case the
+     "pinentry-basic" fallback was in use.  */
+  gnupg_module_name_flush_some ();
 }
 
 
@@ -1840,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)
@@ -1888,7 +2460,7 @@ putty_message_proc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
   if (!cds->cbData || mapfile[cds->cbData - 1])
     return 0;  /* Ignore empty and non-properly terminated strings.  */
 
-  if (DBG_ASSUAN)
+  if (DBG_IPC)
     {
       npth_protect ();
       log_debug ("ssh map file '%s'", mapfile);
@@ -1896,7 +2468,7 @@ putty_message_proc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
     }
 
   maphd = OpenFileMapping (FILE_MAP_ALL_ACCESS, FALSE, mapfile);
-  if (DBG_ASSUAN)
+  if (DBG_IPC)
     {
       npth_protect ();
       log_debug ("ssh map handle %p\n", maphd);
@@ -1925,7 +2497,7 @@ putty_message_proc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
       goto leave;
     }
 
-  if (DBG_ASSUAN)
+  if (DBG_IPC)
     {
       char *sidstr;
 
@@ -1946,7 +2518,7 @@ putty_message_proc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
     }
 
   data = MapViewOfFile (maphd, FILE_MAP_ALL_ACCESS, 0, 0, 0);
-  if (DBG_ASSUAN)
+  if (DBG_IPC)
     log_debug ("ssh IPC buffer at %p\n", data);
   if (!data)
     goto leave;
@@ -2048,9 +2620,30 @@ putty_message_thread (void *arg)
 #endif /*HAVE_W32_SYSTEM*/
 
 
+static void *
+do_start_connection_thread (ctrl_t ctrl)
+{
+  active_connections++;
+  agent_init_default_ctrl (ctrl);
+  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 && !DBG_IPC)
+    log_info (_("handler 0x%lx for fd %d terminated\n"),
+              (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
+
+  agent_deinit_default_ctrl (ctrl);
+  xfree (ctrl);
+  active_connections--;
+  return NULL;
+}
+
+
 /* This is the standard connection thread's main function.  */
 static void *
-start_connection_thread (void *arg)
+start_connection_thread_std (void *arg)
 {
   ctrl_t ctrl = arg;
 
@@ -2061,19 +2654,43 @@ start_connection_thread (void *arg)
       return NULL;
     }
 
-  agent_init_default_ctrl (ctrl);
-  if (opt.verbose)
-    log_info (_("handler 0x%lx for fd %d started\n"),
-              (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
+  return do_start_connection_thread (ctrl);
+}
 
-  start_command_handler (ctrl, GNUPG_INVALID_FD, ctrl->thread_startup.fd);
-  if (opt.verbose)
-    log_info (_("handler 0x%lx for fd %d terminated\n"),
-              (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
 
-  agent_deinit_default_ctrl (ctrl);
-  xfree (ctrl);
-  return NULL;
+/* This is the extra socket connection thread's main function.  */
+static void *
+start_connection_thread_extra (void *arg)
+{
+  ctrl_t ctrl = arg;
+
+  if (check_nonce (ctrl, &socket_nonce_extra))
+    {
+      log_error ("handler 0x%lx nonce check FAILED\n",
+                 (unsigned long) npth_self());
+      return NULL;
+    }
+
+  ctrl->restricted = 1;
+  return do_start_connection_thread (ctrl);
+}
+
+
+/* This is the browser socket connection thread's main function.  */
+static void *
+start_connection_thread_browser (void *arg)
+{
+  ctrl_t ctrl = arg;
+
+  if (check_nonce (ctrl, &socket_nonce_browser))
+    {
+      log_error ("handler 0x%lx nonce check FAILED\n",
+                 (unsigned long) npth_self());
+      return NULL;
+    }
+
+  ctrl->restricted = 2;
+  return do_start_connection_thread (ctrl);
 }
 
 
@@ -2086,6 +2703,7 @@ start_connection_thread_ssh (void *arg)
   if (check_nonce (ctrl, &socket_nonce_ssh))
     return NULL;
 
+  active_connections++;
   agent_init_default_ctrl (ctrl);
   if (opt.verbose)
     log_info (_("ssh handler 0x%lx for fd %d started\n"),
@@ -2098,6 +2716,7 @@ start_connection_thread_ssh (void *arg)
 
   agent_deinit_default_ctrl (ctrl);
   xfree (ctrl);
+  active_connections--;
   return NULL;
 }
 
@@ -2105,8 +2724,12 @@ start_connection_thread_ssh (void *arg)
 /* Connection handler loop.  Wait for connection requests and spawn a
    thread after accepting a connection.  */
 static void
-handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_ssh)
+handle_connections (gnupg_fd_t listen_fd,
+                    gnupg_fd_t listen_fd_extra,
+                    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;
@@ -2120,8 +2743,20 @@ handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_ssh)
   struct timespec timeout;
 #ifdef HAVE_W32_SYSTEM
   HANDLE events[2];
-  int events_set;
+  unsigned int events_set;
 #endif
+  int my_inotify_fd = -1;
+  struct {
+    const char *name;
+    void *(*func) (void *arg);
+    gnupg_fd_t l_fd;
+  } listentbl[] = {
+    { "std",     start_connection_thread_std   },
+    { "extra",   start_connection_thread_extra },
+    { "browser", start_connection_thread_browser },
+    { "ssh",    start_connection_thread_ssh   }
+  };
+
 
   ret = npth_attr_init(&tattr);
   if (ret)
@@ -2148,6 +2783,15 @@ handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_ssh)
 # 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). */
@@ -2171,12 +2815,35 @@ handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_ssh)
   FD_ZERO (&fdset);
   FD_SET (FD2INT (listen_fd), &fdset);
   nfd = FD2INT (listen_fd);
+  if (listen_fd_extra != GNUPG_INVALID_FD)
+    {
+      FD_SET ( FD2INT(listen_fd_extra), &fdset);
+      if (FD2INT (listen_fd_extra) > nfd)
+        nfd = FD2INT (listen_fd_extra);
+    }
+  if (listen_fd_browser != GNUPG_INVALID_FD)
+    {
+      FD_SET ( FD2INT(listen_fd_browser), &fdset);
+      if (FD2INT (listen_fd_browser) > nfd)
+        nfd = FD2INT (listen_fd_browser);
+    }
   if (listen_fd_ssh != GNUPG_INVALID_FD)
     {
       FD_SET ( FD2INT(listen_fd_ssh), &fdset);
       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;
+  listentbl[2].l_fd = listen_fd_browser;
+  listentbl[3].l_fd = listen_fd_ssh;
 
   npth_clock_gettime (&abstime);
   abstime.tv_sec += TIMERTICK_INTERVAL;
@@ -2190,8 +2857,19 @@ handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_ssh)
             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,
@@ -2219,7 +2897,6 @@ handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_ssh)
           handle_signal (signo);
       }
 #else
-      events_set = 0;
       ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout,
                           events, &events_set);
       saved_errno = errno;
@@ -2241,94 +2918,67 @@ handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_ssh)
           next timeout.  */
        continue;
 
-      if (!shutdown_pending && FD_ISSET (FD2INT (listen_fd), &read_fdset))
-       {
+      if (!shutdown_pending)
+        {
+          int idx;
           ctrl_t ctrl;
+          npth_t thread;
 
-          plen = sizeof paddr;
-         fd = INT2FD (npth_accept (FD2INT(listen_fd),
-                                   (struct sockaddr *)&paddr, &plen));
-         if (fd == GNUPG_INVALID_FD)
-           {
-             log_error ("accept failed: %s\n", strerror (errno));
-           }
-          else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)) )
-            {
-              log_error ("error allocating connection control data: %s\n",
-                         strerror (errno) );
-              assuan_sock_close (fd);
-            }
-          else if ( !(ctrl->session_env = session_env_new ()) )
+          if (my_inotify_fd != -1
+              && FD_ISSET (my_inotify_fd, &read_fdset)
+              && gnupg_inotify_has_name (my_inotify_fd, GPG_AGENT_SOCK_NAME))
             {
-              log_error ("error allocating session environment block: %s\n",
-                         strerror (errno) );
-              xfree (ctrl);
-              assuan_sock_close (fd);
+              shutdown_pending = 1;
+              log_info ("socket file has been removed - shutting down\n");
             }
-          else
-            {
-             npth_t thread;
 
-              ctrl->thread_startup.fd = fd;
-             ret = npth_create (&thread, &tattr,
-                                 start_connection_thread, ctrl);
-              if (ret)
+          for (idx=0; idx < DIM(listentbl); idx++)
+            {
+              if (listentbl[idx].l_fd == GNUPG_INVALID_FD)
+                continue;
+              if (!FD_ISSET (FD2INT (listentbl[idx].l_fd), &read_fdset))
+                continue;
+
+              plen = sizeof paddr;
+              fd = INT2FD (npth_accept (FD2INT(listentbl[idx].l_fd),
+                                        (struct sockaddr *)&paddr, &plen));
+              if (fd == GNUPG_INVALID_FD)
                 {
-                  log_error ("error spawning connection handler: %s\n",
-                            strerror (ret));
-                  assuan_sock_close (fd);
-                  xfree (ctrl);
+                  log_error ("accept failed for %s: %s\n",
+                             listentbl[idx].name, strerror (errno));
                 }
-
-            }
-          fd = GNUPG_INVALID_FD;
-       }
-
-      if (!shutdown_pending && listen_fd_ssh != GNUPG_INVALID_FD
-          && FD_ISSET ( FD2INT (listen_fd_ssh), &read_fdset))
-       {
-          ctrl_t ctrl;
-
-          plen = sizeof paddr;
-         fd = INT2FD(npth_accept (FD2INT(listen_fd_ssh),
-                                  (struct sockaddr *)&paddr, &plen));
-         if (fd == GNUPG_INVALID_FD)
-           {
-             log_error ("accept failed for ssh: %s\n", strerror (errno));
-           }
-          else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)) )
-            {
-              log_error ("error allocating connection control data: %s\n",
-                         strerror (errno) );
-              assuan_sock_close (fd);
-            }
-          else if ( !(ctrl->session_env = session_env_new ()) )
-            {
-              log_error ("error allocating session environment block: %s\n",
-                         strerror (errno) );
-              xfree (ctrl);
-              assuan_sock_close (fd);
-            }
-          else
-            {
-             npth_t thread;
-
-              agent_init_default_ctrl (ctrl);
-              ctrl->thread_startup.fd = fd;
-              ret = npth_create (&thread, &tattr,
-                                 start_connection_thread_ssh, ctrl);
-             if (ret)
+              else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)))
                 {
-                  log_error ("error spawning ssh connection handler: %s\n",
-                            strerror (ret));
+                  log_error ("error allocating connection data for %s: %s\n",
+                             listentbl[idx].name, strerror (errno) );
                   assuan_sock_close (fd);
+                }
+              else if ( !(ctrl->session_env = session_env_new ()))
+                {
+                  log_error ("error allocating session env block for %s: %s\n",
+                             listentbl[idx].name, strerror (errno) );
                   xfree (ctrl);
+                  assuan_sock_close (fd);
+                }
+              else
+                {
+                  ctrl->thread_startup.fd = fd;
+                  ret = npth_create (&thread, &tattr,
+                                     listentbl[idx].func, ctrl);
+                  if (ret)
+                    {
+                      log_error ("error spawning connection handler for %s:"
+                                 " %s\n", listentbl[idx].name, strerror (ret));
+                      assuan_sock_close (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);
@@ -2366,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)
@@ -2402,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");
     }
@@ -2430,13 +3078,10 @@ check_own_socket (void)
   if (disable_check_own_socket)
     return;
 
-  if (!opt.use_standard_socket)
-    return; /* This check makes only sense in standard socket mode.  */
-
   if (check_own_socket_running || shutdown_pending)
     return;  /* Still running or already shutting down.  */
 
-  sockname = make_filename (opt.homedir, GPG_AGENT_SOCK_NAME, NULL);
+  sockname = make_filename_try (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL);
   if (!sockname)
     return; /* Out of memory.  */
 
@@ -2453,73 +3098,27 @@ check_own_socket (void)
 
 
 /* Figure out whether an agent is available and running. Prints an
-   error if not.  If SILENT is true, no messages are printed.  Usually
-   started with MODE 0.  Returns 0 if the agent is running. */
+   error if not.  If SILENT is true, no messages are printed.
+   Returns 0 if the agent is running. */
 static int
-check_for_running_agent (int silent, int mode)
+check_for_running_agent (int silent)
 {
-  int rc;
-  char *infostr, *p;
+  gpg_error_t err;
+  char *sockname;
   assuan_context_t ctx = NULL;
-  int prot, pid;
 
-  if (!mode)
-    {
-      infostr = getenv (GPG_AGENT_INFO_NAME);
-      if (!infostr || !*infostr)
-        {
-          if (!check_for_running_agent (silent, 1))
-            return 0; /* Okay, its running on the standard socket. */
-          if (!silent)
-            log_error (_("no gpg-agent running in this session\n"));
-          return -1;
-        }
-
-      infostr = xstrdup (infostr);
-      if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
-        {
-          xfree (infostr);
-          if (!check_for_running_agent (silent, 1))
-            return 0; /* Okay, its running on the standard socket. */
-          if (!silent)
-            log_error (_("malformed %s environment variable\n"),
-                       GPG_AGENT_INFO_NAME);
-          return -1;
-        }
-
-      *p++ = 0;
-      pid = atoi (p);
-      while (*p && *p != PATHSEP_C)
-        p++;
-      prot = *p? atoi (p+1) : 0;
-      if (prot != 1)
-        {
-          xfree (infostr);
-          if (!silent)
-            log_error (_("gpg-agent protocol version %d is not supported\n"),
-                       prot);
-          if (!check_for_running_agent (silent, 1))
-            return 0; /* Okay, its running on the standard socket. */
-          return -1;
-        }
-    }
-  else /* MODE != 0 */
-    {
-      infostr = make_filename (opt.homedir, GPG_AGENT_SOCK_NAME, NULL);
-      pid = (pid_t)(-1);
-    }
+  sockname = make_filename_try (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL);
+  if (!sockname)
+    return gpg_error_from_syserror ();
 
-  rc = assuan_new (&ctx);
-  if (! rc)
-    rc = assuan_socket_connect (ctx, infostr, pid, 0);
-  xfree (infostr);
-  if (rc)
+  err = assuan_new (&ctx);
+  if (!err)
+    err = assuan_socket_connect (ctx, sockname, (pid_t)(-1), 0);
+  xfree (sockname);
+  if (err)
     {
-      if (!mode && !check_for_running_agent (silent, 1))
-        return 0; /* Okay, its running on the standard socket. */
-
-      if (!mode && !silent)
-        log_error ("can't connect to the agent: %s\n", gpg_strerror (rc));
+      if (!silent)
+        log_error (_("no gpg-agent running in this session\n"));
 
       if (ctx)
        assuan_release (ctx);