po: Auto-update
[gnupg.git] / agent / gpg-agent.c
index 90e8a26..a1964ec 100644 (file)
@@ -39,7 +39,6 @@
 # endif
 # include <aclapi.h>
 # include <sddl.h>
-# include <direct.h>
 #else /*!HAVE_W32_SYSTEM*/
 # include <sys/socket.h>
 # include <sys/un.h>
@@ -84,6 +83,7 @@ enum cmd_and_opt_values
   oNoOptions,
   oHomedir,
   oNoDetach,
+  oGrab,
   oNoGrab,
   oLogFile,
   oServer,
@@ -134,6 +134,10 @@ enum cmd_and_opt_values
   oPuttySupport,
   oDisableScdaemon,
   oDisableCheckOwnSocket,
+  oS2KCount,
+  oAutoExpandSecmem,
+  oListenBacklog,
+
   oWriteEnvFile
 };
 
@@ -170,7 +174,10 @@ static ARGPARSE_OPTS opts[] = {
   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_n (oGrab,      "grab",      "@"),
+                /* FIXME: Add the below string for 2.3 */
+                /* N_("let PIN-Entry grab keyboard and mouse")), */
+  ARGPARSE_s_n (oNoGrab,    "no-grab",   "@"),
   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")),
@@ -235,7 +242,7 @@ static ARGPARSE_OPTS opts[] = {
 
   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")),
+                N_("|ALGO|use ALGO to show ssh fingerprints")),
   ARGPARSE_s_n (oPuttySupport, "enable-putty-support",
 #ifdef HAVE_W32_SYSTEM
                 /* */           N_("enable putty support")
@@ -245,6 +252,12 @@ static ARGPARSE_OPTS opts[] = {
                 ),
   ARGPARSE_s_n (oEnableExtendedKeyFormat, "enable-extended-key-format", "@"),
 
+  ARGPARSE_s_u (oS2KCount, "s2k-count", "@"),
+
+  ARGPARSE_op_u (oAutoExpandSecmem, "auto-expand-secmem", "@"),
+
+  ARGPARSE_s_i (oListenBacklog, "listen-backlog", "@"),
+
   /* Dummy options for backward compatibility.  */
   ARGPARSE_o_s (oWriteEnvFile, "write-env-file", "@"),
   ARGPARSE_s_n (oUseStandardSocket, "use-standard-socket", "@"),
@@ -277,19 +290,17 @@ static struct debug_flags_s debug_flags [] =
 #define MIN_PASSPHRASE_NONALPHA (1)
 #define MAX_PASSPHRASE_DAYS   (0)
 
-/* The timer tick used for housekeeping stuff.  For Windows we use a
-   longer period as the SetWaitableTimer seems to signal earlier than
-   the 2 seconds.  CHECK_OWN_SOCKET_INTERVAL defines how often we
-   check our own socket in standard socket mode.  If that value is 0
-   we don't check at all.   All values are in seconds. */
+/* The timer tick used for housekeeping stuff.  Note that on Windows
+ * we use a SetWaitableTimer seems to signal earlier than about 2
+ * seconds.  Thus we use 4 seconds on all platforms except for
+ * Windowsce.  CHECK_OWN_SOCKET_INTERVAL defines how often we check
+ * our own socket in standard socket mode.  If that value is 0 we
+ * don't check at all.  All values are in seconds. */
 #if defined(HAVE_W32CE_SYSTEM)
 # define TIMERTICK_INTERVAL         (60)
 # define CHECK_OWN_SOCKET_INTERVAL   (0)  /* Never */
-#elif defined(HAVE_W32_SYSTEM)
-# define TIMERTICK_INTERVAL          (4)
-# define CHECK_OWN_SOCKET_INTERVAL  (60)
 #else
-# define TIMERTICK_INTERVAL          (2)
+# define TIMERTICK_INTERVAL          (4)
 # define CHECK_OWN_SOCKET_INTERVAL  (60)
 #endif
 
@@ -363,6 +374,10 @@ static assuan_sock_nonce_t socket_nonce_extra;
 static assuan_sock_nonce_t socket_nonce_browser;
 static assuan_sock_nonce_t socket_nonce_ssh;
 
+/* Value for the listen() backlog argument.  We use the same value for
+ * all sockets - 64 is on current Linux half of the default maximum.
+ * Let's try this as default.  Change at runtime with --listen-backlog.  */
+static int listen_backlog = 64;
 
 /* Default values for options passed to the pinentry. */
 static char *default_display;
@@ -383,10 +398,20 @@ static const char *debug_level;
 static char *current_logfile;
 
 /* The handle_tick() function may test whether a parent is still
-   running.  We record the PID of the parent here or -1 if it should be
  watched. */
+ * running.  We record the PID of the parent here or -1 if it should
* be watched.  */
 static pid_t parent_pid = (pid_t)(-1);
 
+/* This flag is true if the inotify mechanism for detecting the
+ * removal of the homedir is active.  This flag is used to disable the
+ * alternative but portable stat based check.  */
+static int have_homedir_inotify;
+
+/* Depending on how gpg-agent was started, the homedir inotify watch
+ * may not be reliable.  This flag is set if we assume that inotify
+ * works reliable.  */
+static int reliable_homedir_inotify;
+
 /* Number of active connections.  */
 static int active_connections;
 
@@ -773,12 +798,14 @@ cleanup (void)
 static int
 parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
 {
+  int i;
+
   if (!pargs)
     { /* reset mode */
       opt.quiet = 0;
       opt.verbose = 0;
       opt.debug = 0;
-      opt.no_grab = 0;
+      opt.no_grab = 1;
       opt.debug_pinentry = 0;
       opt.pinentry_program = NULL;
       opt.pinentry_touch_file = NULL;
@@ -804,7 +831,9 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
       opt.allow_emacs_pinentry = 0;
       opt.disable_scdaemon = 0;
       disable_check_own_socket = 0;
+      /* Note: When changing the next line, change also gpgconf_list.  */
       opt.ssh_fingerprint_digest = GCRY_MD_MD5;
+      opt.s2k_count = 0;
       return 1;
     }
 
@@ -832,7 +861,8 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
         }
       break;
 
-    case oNoGrab: opt.no_grab = 1; break;
+    case oNoGrab: opt.no_grab |= 1; break;
+    case oGrab: opt.no_grab |= 2; break;
 
     case oPinentryProgram: opt.pinentry_program = pargs->r.ret_str; break;
     case oPinentryTouchFile: opt.pinentry_touch_file = pargs->r.ret_str; break;
@@ -887,6 +917,18 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
     case oAllowEmacsPinentry: opt.allow_emacs_pinentry = 1;
       break;
 
+    case oSSHFingerprintDigest:
+      i = gcry_md_map_name (pargs->r.ret_str);
+      if (!i)
+        log_error (_("selected digest algorithm is invalid\n"));
+      else
+        opt.ssh_fingerprint_digest = i;
+      break;
+
+    case oS2KCount:
+      opt.s2k_count = pargs->r.ret_ulong;
+      break;
+
     default:
       return 0; /* not handled */
     }
@@ -899,6 +941,9 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
 static void
 finalize_rereadable_options (void)
 {
+  /* Hack to allow --grab to override --no-grab.  */
+  if ((opt.no_grab & 2))
+    opt.no_grab = 0;
 }
 
 
@@ -990,6 +1035,7 @@ main (int argc, char **argv )
   assuan_set_malloc_hooks (&malloc_hooks);
   assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
   assuan_sock_init ();
+  assuan_sock_set_system_hooks (ASSUAN_SYSTEM_NPTH);
   setup_libassuan_logging (&opt.debug, NULL);
 
   setup_libgcrypt_logging ();
@@ -1181,11 +1227,7 @@ main (int argc, char **argv )
        case oSSHSupport:
           ssh_support = 1;
           break;
-       case oSSHFingerprintDigest:
-          opt.ssh_fingerprint_digest = gcry_md_map_name (pargs.r.ret_str);
-          if (opt.ssh_fingerprint_digest == 0)
-            log_error ("Unknown digest algorithm: %s\n", pargs.r.ret_str);
-          break;
+
         case oPuttySupport:
 #        ifdef HAVE_W32_SYSTEM
           putty_support = 1;
@@ -1202,6 +1244,18 @@ main (int argc, char **argv )
           socket_name_browser = pargs.r.ret_str;
           break;
 
+        case oAutoExpandSecmem:
+          /* Try to enable this option.  It will officially only be
+           * supported by Libgcrypt 1.9 but 1.8.2 already supports it
+           * on the quiet and thus we use the numeric value value.  */
+          gcry_control (78 /*GCRYCTL_AUTO_EXPAND_SECMEM*/,
+                        (unsigned int)pargs.r.ret_ulong,  0);
+          break;
+
+        case oListenBacklog:
+          listen_backlog = pargs.r.ret_int;
+          break;
+
         case oDebugQuickRandom:
           /* Only used by the first stage command line parser.  */
           break;
@@ -1379,6 +1433,8 @@ main (int argc, char **argv )
       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);
+      es_printf ("ssh-fingerprint-digest:%lu:\"%s:\n",
+                 GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, "md5");
 #ifdef HAVE_W32_SYSTEM
       es_printf ("enable-putty-support:%lu:\n", GC_OPT_FLAG_NONE);
 #endif
@@ -1390,6 +1446,8 @@ main (int argc, char **argv )
                  GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME);
       es_printf ("enable-extended-key-format:%lu:\n",
                  GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
+      es_printf ("grab:%lu:\n",
+                 GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
 
       agent_exit (0);
     }
@@ -1559,11 +1617,6 @@ main (int argc, char **argv )
       (void)csh_style;
       (void)nodetach;
       initialize_modules ();
-      if (_chdir("\\"))
-        {
-          log_error ("chdir to / failed: %s\n", strerror (errno));
-          exit (1);
-        }
 
 #else /*!HAVE_W32_SYSTEM*/
 
@@ -1712,12 +1765,12 @@ main (int argc, char **argv )
           log_get_prefix (&oldflags);
           log_set_prefix (NULL, oldflags | GPGRT_LOG_RUN_DETACHED);
           opt.running_detached = 1;
-        }
 
-      if (chdir("/"))
-        {
-          log_error ("chdir to / failed: %s\n", strerror (errno));
-          exit (1);
+          /* Unless we are running with a program given on the command
+           * line we can assume that the inotify things works and thus
+           * we can avoid tye regular stat calls.  */
+          if (!argc)
+            reliable_homedir_inotify = 1;
         }
 
       {
@@ -1730,6 +1783,13 @@ main (int argc, char **argv )
       }
 #endif /*!HAVE_W32_SYSTEM*/
 
+      if (gnupg_chdir (gnupg_daemon_rootdir ()))
+        {
+          log_error ("chdir to '%s' failed: %s\n",
+                     gnupg_daemon_rootdir (), strerror (errno));
+          exit (1);
+        }
+
       log_info ("%s %s started\n", strusage(11), strusage(13) );
       handle_connections (fd, fd_extra, fd_browser, fd_ssh);
       assuan_sock_close (fd);
@@ -2199,9 +2259,10 @@ create_server_socket (char *name, int primary, int cygwin,
     log_error (_("can't set permissions of '%s': %s\n"),
                unaddr->sun_path, strerror (errno));
 
-  if (listen (FD2INT(fd), 5 ) == -1)
+  if (listen (FD2INT(fd), listen_backlog ) == -1)
     {
-      log_error (_("listen() failed: %s\n"), strerror (errno));
+      log_error ("listen(fd,%d) failed: %s\n",
+                 listen_backlog, strerror (errno));
       *name = 0; /* Inhibit removal of the socket by cleanup(). */
       assuan_sock_close (fd);
       xfree (unaddr);
@@ -2304,6 +2365,7 @@ static void
 handle_tick (void)
 {
   static time_t last_minute;
+  struct stat statbuf;
 
   if (!last_minute)
     last_minute = time (NULL);
@@ -2336,6 +2398,14 @@ handle_tick (void)
     }
 #endif
 
+  /* Check whether the homedir is still available.  */
+  if (!shutdown_pending
+      && (!have_homedir_inotify || !reliable_homedir_inotify)
+      && stat (gnupg_homedir (), &statbuf) && errno == ENOENT)
+    {
+      shutdown_pending = 1;
+      log_info ("homedir has been removed - shutting down\n");
+    }
 }
 
 
@@ -2370,7 +2440,7 @@ agent_sigusr2_action (void)
 
 #ifndef HAVE_W32_SYSTEM
 /* The signal handler for this program.  It is expected to be run in
-   its own trhead and not in the context of a signal handler.  */
+   its own thread and not in the context of a signal handler.  */
 static void
 handle_signal (int signo)
 {
@@ -2810,9 +2880,11 @@ handle_connections (gnupg_fd_t listen_fd,
                                                    gnupg_homedir ())))
     {
       if (gpg_err_code (err) != GPG_ERR_NOT_SUPPORTED)
-        log_info ("error enabling daemon termination bu homedir removal: %s\n",
+        log_info ("error enabling daemon termination by homedir removal: %s\n",
                   gpg_strerror (err));
     }
+  else
+    have_homedir_inotify = 1;
 
   /* 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
@@ -2952,27 +3024,34 @@ handle_connections (gnupg_fd_t listen_fd,
           next timeout.  */
        continue;
 
+      /* The inotify fds are set even when a shutdown is pending (see
+       * above).  So we must handle them in any case.  To avoid that
+       * they trigger a second time we close them immediately.  */
+      if (sock_inotify_fd != -1
+          && FD_ISSET (sock_inotify_fd, &read_fdset)
+          && gnupg_inotify_has_name (sock_inotify_fd, GPG_AGENT_SOCK_NAME))
+        {
+          shutdown_pending = 1;
+          close (sock_inotify_fd);
+          sock_inotify_fd = -1;
+          log_info ("socket file has been removed - shutting down\n");
+        }
+
+      if (home_inotify_fd != -1
+          && FD_ISSET (home_inotify_fd, &read_fdset))
+        {
+          shutdown_pending = 1;
+          close (home_inotify_fd);
+          home_inotify_fd = -1;
+          log_info ("homedir has been removed - shutting down\n");
+        }
+
       if (!shutdown_pending)
         {
           int idx;
           ctrl_t ctrl;
           npth_t thread;
 
-          if (sock_inotify_fd != -1
-              && FD_ISSET (sock_inotify_fd, &read_fdset)
-              && gnupg_inotify_has_name (sock_inotify_fd, GPG_AGENT_SOCK_NAME))
-            {
-              shutdown_pending = 1;
-              log_info ("socket file has been removed - shutting down\n");
-            }
-
-          if (home_inotify_fd != -1
-              && FD_ISSET (home_inotify_fd, &read_fdset))
-            {
-              shutdown_pending = 1;
-              log_info ("homedir has been removed - shutting down\n");
-            }
-
           for (idx=0; idx < DIM(listentbl); idx++)
             {
               if (listentbl[idx].l_fd == GNUPG_INVALID_FD)