agent: Make digest algorithms for ssh fingerprints configurable.
[gnupg.git] / agent / gpg-agent.c
index 740af75..77b811c 100644 (file)
@@ -1,6 +1,6 @@
 /* gpg-agent.c  -  The GnuPG Agent
  * Copyright (C) 2000-2007, 2009-2010 Free Software Foundation, Inc.
- * Copyright (C) 2000-2014 Werner Koch
+ * Copyright (C) 2000-2016 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -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>
 #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 "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"
 
 
@@ -80,6 +78,7 @@ enum cmd_and_opt_values
   oDebugLevel,
   oDebugWait,
   oDebugQuickRandom,
+  oDebugPinentry,
   oNoGreeting,
   oNoOptions,
   oHomedir,
@@ -88,10 +87,13 @@ enum cmd_and_opt_values
   oLogFile,
   oServer,
   oDaemon,
+  oSupervised,
   oBatch,
 
   oPinentryProgram,
   oPinentryTouchFile,
+  oPinentryInvisibleChar,
+  oPinentryTimeout,
   oDisplay,
   oTTYname,
   oTTYtype,
@@ -109,9 +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,
@@ -144,17 +152,21 @@ static ARGPARSE_OPTS opts[] = {
 
   ARGPARSE_s_n (oDaemon,  "daemon", N_("run in daemon mode (background)")),
   ARGPARSE_s_n (oServer,  "server", N_("run in server mode (foreground)")),
+#ifndef HAVE_W32_SYSTEM
+  ARGPARSE_s_n (oSupervised,  "supervised", N_("run in supervised mode")),
+#endif
   ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
   ARGPARSE_s_n (oQuiet,          "quiet",     N_("be somewhat more quiet")),
   ARGPARSE_s_n (oSh,     "sh",        N_("sh-style command output")),
   ARGPARSE_s_n (oCsh,    "csh",       N_("csh-style command output")),
   ARGPARSE_s_s (oOptions, "options", N_("|FILE|read options from FILE")),
 
-  ARGPARSE_s_u (oDebug,             "debug",       "@"),
+  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")),
@@ -162,11 +174,19 @@ static ARGPARSE_OPTS opts[] = {
   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",        "@"),
@@ -199,14 +219,22 @@ static ARGPARSE_OPTS opts[] = {
 
   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 (oAllowLoopbackPinentry, "allow-loopback-pinentry",
-                                   N_("allow caller to override the pinentry")),
+  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")
@@ -214,7 +242,7 @@ static ARGPARSE_OPTS opts[] = {
                 /* */           "@"
 #endif
                 ),
-  ARGPARSE_s_s (oExtraSocket, "extra-socket", "@"),
+  ARGPARSE_s_n (oEnableExtendedKeyFormat, "enable-extended-key-format", "@"),
 
   /* Dummy options for backward compatibility.  */
   ARGPARSE_o_s (oWriteEnvFile, "write-env-file", "@"),
@@ -225,6 +253,21 @@ static ARGPARSE_OPTS opts[] = {
 };
 
 
+/* 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 */
@@ -250,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;
@@ -262,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
@@ -280,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;
 
@@ -293,7 +347,11 @@ static char *redir_socket_name;
 static char *socket_name_extra;
 static char *redir_socket_name_extra;
 
-/* Name of the communication socket used for ssh-agent-emulation.  */
+/* 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;
 
@@ -301,6 +359,7 @@ static char *redir_socket_name_ssh;
    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;
 
 
@@ -330,22 +389,52 @@ 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, int with_homedir);
-static gnupg_fd_t create_server_socket (char *name, int primary,
+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);
@@ -437,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;
@@ -470,15 +558,7 @@ 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_IPC_VALUE    )? " ipc":"");
+    parse_debug_flag (NULL, &opt.debug, debug_flags);
 }
 
 
@@ -489,24 +569,177 @@ 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
@@ -518,10 +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);
-  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);
+    }
 }
 
 
@@ -540,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;
@@ -552,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;
     }
 
@@ -565,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)
@@ -585,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;
@@ -608,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;
@@ -619,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 */
@@ -632,15 +898,42 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
 static void
 finalize_rereadable_options (void)
 {
-  /* It would be too surprising if the max-cache-ttl is lower than the
-     default-cache-ttl - thus we silently correct that.  */
-  if (opt.def_cache_ttl > opt.max_cache_ttl)
-    opt.max_cache_ttl = opt.def_cache_ttl;
-  if (opt.def_cache_ttl_ssh > opt.max_cache_ttl_ssh)
-    opt.max_cache_ttl_ssh = opt.def_cache_ttl_ssh;
 }
 
 
+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
@@ -669,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;
@@ -682,33 +977,23 @@ 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 ();
 
@@ -719,8 +1004,6 @@ main (int argc, char **argv )
   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;
@@ -776,7 +1059,7 @@ 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);
@@ -785,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;
 
   /*
@@ -793,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;
@@ -859,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;
@@ -876,8 +1160,10 @@ main (int argc, char **argv )
         case oXauthority: default_xauthority = xstrdup (pargs.r.ret_str);
           break;
 
-        case oUseStandardSocket:   /* dummy */ break;
-        case oNoUseStandardSocket: /* dummy */ break;
+        case oUseStandardSocket:
+        case oNoUseStandardSocket:
+          obsolete_option (configname, configlineno, "use-standard-socket");
+          break;
 
         case oFakedSystemTime:
           {
@@ -891,11 +1177,17 @@ 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;
 
@@ -904,11 +1196,18 @@ main (int argc, char **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: /* dummy */ break;
+        case oWriteEnvFile:
+          obsolete_option (configname, configlineno, "write-env-file");
+          break;
 
         default : pargs.err = configfp? 1:2; break;
        }
@@ -934,9 +1233,6 @@ main (int argc, char **argv )
 
   finalize_rereadable_options ();
 
-  /* Turn the homedir into an absolute one. */
-  opt.homedir = make_absfilename (opt.homedir, NULL);
-
   /* Print a warning if an argument looks like an option.  */
   if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
     {
@@ -958,9 +1254,9 @@ main (int argc, char **argv )
     bind_textdomain_codeset (PACKAGE_GT, "UTF-8");
 #endif
 
-  if (!pipe_server && !is_daemon && !gpgconf_list)
+  if (!pipe_server && !is_daemon && !gpgconf_list && !is_supervised)
     {
-     /* We have been called without any options and thus we merely
+     /* We have been called without any command and thus we merely
         check whether an agent is already running.  We do this right
         here so that we don't clobber a logfile with this check but
         print the status directly to stderr. */
@@ -970,6 +1266,32 @@ main (int argc, char **argv )
       agent_exit (0);
     }
 
+  if (is_supervised)
+    ;
+  else if (!opt.extra_socket)
+    opt.extra_socket = 1;
+  else if (socket_name_extra
+           && (!strcmp (socket_name_extra, "none")
+               || !strcmp (socket_name_extra, "/dev/null")))
+    {
+      /* User requested not to create this socket.  */
+      opt.extra_socket = 0;
+      socket_name_extra = NULL;
+    }
+
+  if (is_supervised)
+    ;
+  else if (!opt.browser_socket)
+    opt.browser_socket = 1;
+  else if (socket_name_browser
+           && (!strcmp (socket_name_browser, "none")
+               || !strcmp (socket_name_browser, "/dev/null")))
+    {
+      /* User requested not to create this socket.  */
+      opt.browser_socket = 0;
+      socket_name_browser = NULL;
+    }
+
   set_debug ();
 
   if (atexit (cleanup))
@@ -979,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);
@@ -1009,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",
@@ -1053,16 +1371,23 @@ 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 ("allow-loopback-pinentry:%lu:\n",
+      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);
@@ -1072,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);
     }
 
@@ -1090,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)
         {
@@ -1110,14 +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_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
@@ -1130,28 +1504,47 @@ 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, 1);
-      fd = create_server_socket (socket_name, 1,
+      fd = create_server_socket (socket_name, 1, 0,
                                  &redir_socket_name, &socket_nonce);
 
       if (opt.extra_socket)
         {
-          socket_name_extra = create_socket_name (socket_name_extra, 0);
+          if (socket_name_extra)
+            socket_name_extra = create_socket_name (socket_name_extra, 0);
+          else
+            socket_name_extra = create_socket_name
+              /**/                (GPG_AGENT_EXTRA_SOCK_NAME, 1);
           opt.extra_socket = 2; /* Indicate that it has been malloced.  */
-          fd_extra = create_server_socket (socket_name_extra, 0,
+          fd_extra = create_server_socket (socket_name_extra, 0, 0,
                                            &redir_socket_name_extra,
                                            &socket_nonce_extra);
         }
 
-      if (opt.ssh_support)
+      if (opt.browser_socket)
         {
-          socket_name_ssh = create_socket_name (GPG_AGENT_SSH_SOCK_NAME, 1);
-          fd_ssh = create_server_socket (socket_name_ssh, 0,
-                                         &redir_socket_name_ssh,
-                                         &socket_nonce_ssh);
+          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
          still alive. */
@@ -1159,11 +1552,15 @@ main (int argc, char **argv )
         parent_pid = getpid ();
 
       fflush (NULL);
+
 #ifdef HAVE_W32_SYSTEM
+
       (void)csh_style;
       (void)nodetach;
-      pid = getpid ();
+      initialize_modules ();
+
 #else /*!HAVE_W32_SYSTEM*/
+
       pid = fork ();
       if (pid == (pid_t)-1)
         {
@@ -1193,7 +1590,7 @@ main (int argc, char **argv )
 #endif /*HAVE_SIGPROCMASK*/
 
           /* Create the SSH info string if enabled. */
-         if (opt.ssh_support)
+         if (ssh_support)
            {
              if (asprintf (&infostr_ssh_sock, "SSH_AUTH_SOCK=%s",
                            socket_name_ssh) < 0)
@@ -1215,13 +1612,14 @@ main (int argc, char **argv )
                                the child should do this from now on */
          if (opt.extra_socket)
            *socket_name_extra = 0;
-         if (opt.ssh_support)
-           *socket_name_ssh = 0;
+         if (opt.browser_socket)
+           *socket_name_browser = 0;
+          *socket_name_ssh = 0;
 
           if (argc)
             { /* Run the program given on the commandline.  */
-              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) );
@@ -1247,7 +1645,7 @@ main (int argc, char **argv )
                  shell's eval to set it */
               if (csh_style)
                 {
-                 if (opt.ssh_support)
+                 if (ssh_support)
                    {
                      *strchr (infostr_ssh_sock, '=') = ' ';
                      es_printf ("setenv %s;\n", infostr_ssh_sock);
@@ -1255,13 +1653,13 @@ main (int argc, char **argv )
                 }
               else
                 {
-                 if (opt.ssh_support)
+                 if (ssh_support)
                    {
                      es_printf ("%s; export SSH_AUTH_SOCK;\n",
                                  infostr_ssh_sock);
                    }
                 }
-             if (opt.ssh_support)
+             if (ssh_support)
                {
                  xfree (infostr_ssh_sock);
                  xfree (infostr_ssh_valid);
@@ -1275,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 )
         {
@@ -1304,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;
         }
 
@@ -1325,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, fd_extra, fd_ssh);
+      handle_connections (fd, fd_extra, fd_browser, fd_ssh);
       assuan_sock_close (fd);
     }
 
@@ -1361,6 +1761,105 @@ 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
@@ -1397,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)
@@ -1408,7 +1908,7 @@ 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 explictly set.  This is also used for the restricted mode
+   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)
@@ -1420,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)
@@ -1507,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.  */
@@ -1563,7 +2071,7 @@ create_socket_name (char *standard_name, int with_homedir)
   char *name;
 
   if (with_homedir)
-    name = make_filename (opt.homedir, standard_name, NULL);
+    name = make_filename (gnupg_socketdir (), standard_name, NULL);
   else
     name = make_filename (standard_name, NULL);
   if (strchr (name, PATHSEP_C))
@@ -1581,9 +2089,10 @@ create_socket_name (char *standard_name, int with_homedir)
    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.  */
+   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 primary,
+create_server_socket (char *name, int primary, int cygwin,
                       char **r_redir_name, assuan_sock_nonce_t *nonce)
 {
   struct sockaddr *addr;
@@ -1603,10 +2112,12 @@ create_server_socket (char *name, int primary,
       agent_exit (2);
     }
 
+  if (cygwin)
+    assuan_sock_set_flag (fd, "cygwin", 1);
+
   unaddr = xmalloc (sizeof *unaddr);
   addr = (struct sockaddr*)unaddr;
 
-#if ASSUAN_VERSION_NUMBER >= 0x020104 /* >= 2.1.4 */
   {
     int redirected;
 
@@ -1627,17 +2138,6 @@ create_server_socket (char *name, int primary,
           log_info ("redirecting socket '%s' to '%s'\n", name, *r_redir_name);
       }
   }
-#else /* Assuan < 2.1.4 */
-  memset (unaddr, 0, sizeof *unaddr);
-  unaddr->sun_family = AF_UNIX;
-  if (strlen (name) + 1 >= sizeof (unaddr->sun_path))
-    {
-      log_error (_("socket name '%s' is too long\n"), name);
-      *name = 0; /* Inhibit removal of the socket by cleanup(). */
-      agent_exit (2);
-    }
-  strcpy (unaddr->sun_path, name);
-#endif /* Assuan < 2.1.4 */
 
   len = SUN_LEN (unaddr);
   rc = assuan_sock_bind (fd, addr, len);
@@ -1660,7 +2160,7 @@ create_server_socket (char *name, int primary,
          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"));
@@ -1686,6 +2186,10 @@ create_server_socket (char *name, int primary,
       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));
@@ -1719,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
@@ -1735,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)
@@ -1904,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)
@@ -2113,27 +2621,22 @@ putty_message_thread (void *arg)
 
 
 static void *
-start_connection_thread (ctrl_t ctrl)
+do_start_connection_thread (ctrl_t ctrl)
 {
-  if (check_nonce (ctrl, &socket_nonce))
-    {
-      log_error ("handler 0x%lx nonce check FAILED\n",
-                 (unsigned long) npth_self());
-      return NULL;
-    }
-
+  active_connections++;
   agent_init_default_ctrl (ctrl);
-  if (opt.verbose)
+  if (opt.verbose && !DBG_IPC)
     log_info (_("handler 0x%lx for fd %d started\n"),
               (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
 
   start_command_handler (ctrl, GNUPG_INVALID_FD, ctrl->thread_startup.fd);
-  if (opt.verbose)
+  if (opt.verbose && !DBG_IPC)
     log_info (_("handler 0x%lx for fd %d terminated\n"),
               (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
 
   agent_deinit_default_ctrl (ctrl);
   xfree (ctrl);
+  active_connections--;
   return NULL;
 }
 
@@ -2144,7 +2647,14 @@ start_connection_thread_std (void *arg)
 {
   ctrl_t ctrl = arg;
 
-  return start_connection_thread (ctrl);
+  if (check_nonce (ctrl, &socket_nonce))
+    {
+      log_error ("handler 0x%lx nonce check FAILED\n",
+                 (unsigned long) npth_self());
+      return NULL;
+    }
+
+  return do_start_connection_thread (ctrl);
 }
 
 
@@ -2154,8 +2664,33 @@ 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 start_connection_thread (ctrl);
+  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);
 }
 
 
@@ -2168,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"),
@@ -2180,6 +2716,7 @@ start_connection_thread_ssh (void *arg)
 
   agent_deinit_default_ctrl (ctrl);
   xfree (ctrl);
+  active_connections--;
   return NULL;
 }
 
@@ -2189,8 +2726,10 @@ start_connection_thread_ssh (void *arg)
 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)
 {
+  gpg_error_t err;
   npth_attr_t tattr;
   struct sockaddr_un paddr;
   socklen_t plen;
@@ -2206,14 +2745,16 @@ handle_connections (gnupg_fd_t listen_fd,
   HANDLE events[2];
   unsigned int events_set;
 #endif
+  int my_inotify_fd = -1;
   struct {
     const char *name;
     void *(*func) (void *arg);
     gnupg_fd_t l_fd;
   } listentbl[] = {
-    { "std",  start_connection_thread_std   },
-    { "extra",start_connection_thread_extra },
-    { "ssh",  start_connection_thread_ssh   }
+    { "std",     start_connection_thread_std   },
+    { "extra",   start_connection_thread_extra },
+    { "browser", start_connection_thread_browser },
+    { "ssh",    start_connection_thread_ssh   }
   };
 
 
@@ -2242,6 +2783,15 @@ handle_connections (gnupg_fd_t listen_fd,
 # endif
 #endif
 
+  if (disable_check_own_socket)
+    my_inotify_fd = -1;
+  else if ((err = gnupg_inotify_watch_socket (&my_inotify_fd, socket_name)))
+    {
+      if (gpg_err_code (err) != GPG_ERR_NOT_SUPPORTED)
+        log_info ("error enabling fast daemon termination: %s\n",
+                  gpg_strerror (err));
+    }
+
   /* On Windows we need to fire up a separate thread to listen for
      requests from Putty (an SSH client), so we can replace Putty's
      Pageant (its ssh-agent implementation). */
@@ -2271,16 +2821,29 @@ handle_connections (gnupg_fd_t listen_fd,
       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_ssh;
+  listentbl[2].l_fd = listen_fd_browser;
+  listentbl[3].l_fd = listen_fd_ssh;
 
   npth_clock_gettime (&abstime);
   abstime.tv_sec += TIMERTICK_INTERVAL;
@@ -2294,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,
@@ -2350,6 +2924,14 @@ handle_connections (gnupg_fd_t listen_fd,
           ctrl_t ctrl;
           npth_t thread;
 
+          if (my_inotify_fd != -1
+              && FD_ISSET (my_inotify_fd, &read_fdset)
+              && gnupg_inotify_has_name (my_inotify_fd, GPG_AGENT_SOCK_NAME))
+            {
+              shutdown_pending = 1;
+              log_info ("socket file has been removed - shutting down\n");
+            }
+
           for (idx=0; idx < DIM(listentbl); idx++)
             {
               if (listentbl[idx].l_fd == GNUPG_INVALID_FD)
@@ -2391,11 +2973,12 @@ handle_connections (gnupg_fd_t listen_fd,
                       xfree (ctrl);
                     }
                 }
-              fd = GNUPG_INVALID_FD;
             }
         }
     }
 
+  if (my_inotify_fd != -1)
+    close (my_inotify_fd);
   cleanup ();
   log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
   npth_attr_destroy (&tattr);
@@ -2433,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)
@@ -2469,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");
     }
@@ -2500,7 +3081,7 @@ check_own_socket (void)
   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.  */
 
@@ -2526,7 +3107,9 @@ check_for_running_agent (int silent)
   char *sockname;
   assuan_context_t ctx = NULL;
 
-  sockname = make_filename (opt.homedir, GPG_AGENT_SOCK_NAME, NULL);
+  sockname = make_filename_try (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL);
+  if (!sockname)
+    return gpg_error_from_syserror ();
 
   err = assuan_new (&ctx);
   if (!err)