Preparing a new release. Updated gettext
[gnupg.git] / agent / gpg-agent.c
index 0fc1bb8..4e3f0b5 100644 (file)
@@ -30,6 +30,7 @@
 #include <time.h>
 #include <fcntl.h>
 #include <sys/socket.h>
+#include <sys/stat.h>
 #include <sys/un.h>
 #include <unistd.h>
 #include <signal.h>
@@ -76,10 +77,12 @@ enum cmd_and_opt_values
   oLCctype,
   oLCmessages,
   oScdaemonProgram,
-  oDefCacheTTL,
   oDisablePth,
+  oDefCacheTTL,
+  oMaxCacheTTL,
 
   oIgnoreCacheForSigning,
+  oAllowMarkTrusted,
   oKeepTTY,
   oKeepDISPLAY,
 
@@ -109,26 +112,33 @@ static ARGPARSE_OPTS opts[] = {
   { oLogFile, "log-file"   ,2, N_("use a log file for the server")},
   { oDisablePth, "disable-pth", 0, N_("do not allow multiple connections")},
 
-  { oPinentryProgram, "pinentry-program", 2 , "path to PIN Entry program" },
-  { oDisplay,    "display",     2, "set the display" },
-  { oTTYname,    "ttyname",     2, "set the tty terminal node name" },
-  { oTTYtype,    "ttytype",     2, "set the tty terminal type" },
-  { oLCctype,    "lc-ctype",    2, "set the tty LC_CTYPE value" },
-  { oLCmessages, "lc-messages", 2, "set the tty LC_MESSAGES value" },
+  { oPinentryProgram, "pinentry-program", 2 ,
+                               N_("|PGM|use PGM as the PIN-Entry program") },
+  { oScdaemonProgram, "scdaemon-program", 2 ,
+                               N_("|PGM|use PGM as the SCdaemon program") },
 
-  { oScdaemonProgram, "scdaemon-program", 2 , "path to SCdaemon program" },
-  { oDefCacheTTL, "default-cache-ttl", 4,
-                                 "|N|expire cached PINs after N seconds"},
-  { oIgnoreCacheForSigning, "ignore-cache-for-signing", 0,
-                                 "do not use the PIN cache when signing"},
+  { oDisplay,    "display",     2, "@" },
+  { oTTYname,    "ttyname",     2, "@" },
+  { oTTYtype,    "ttytype",     2, "@" },
+  { oLCctype,    "lc-ctype",    2, "@" },
+  { oLCmessages, "lc-messages", 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")},
+  { oMaxCacheTTL, "max-cache-ttl", 4, "@" },
+  { oIgnoreCacheForSigning, "ignore-cache-for-signing", 0,
+                               N_("do not use the PIN cache when signing")},
+  { oAllowMarkTrusted, "allow-mark-trusted", 0,
+                             N_("allow clients to mark keys as \"trusted\"")},
   {0}
 };
 
 
-#define DEFAULT_CACHE_TTL (10*60) /* 10 minutes */
+#define DEFAULT_CACHE_TTL (10*60)  /* 10 minutes */
+#define MAX_CACHE_TTL     (120*60) /* 2 hours */
 
 static volatile int caught_fatal_sig = 0;
 
@@ -155,6 +165,10 @@ static char *config_filename;
 /* Helper to implement --debug-level */
 static const char *debug_level;
 
+/* Keep track of the current log file so that we can avoid updating
+   the log file afte a SIGHUP if id didn't changed. Malloced. */
+static char *current_logfile;
+
 /* Local prototypes. */
 static void create_directories (void);
 #ifdef USE_GNU_PTH
@@ -164,6 +178,7 @@ static void handle_connections (int listen_fd);
 GCRY_THREAD_OPTION_PTH_IMPL;
 
 #endif /*USE_GNU_PTH*/
+static void check_for_running_agent (void);
 
 
 
@@ -317,9 +332,10 @@ cleanup_sh (int sig)
 /* Handle options which are allowed to be reset after program start.
    Return true when the current option in PARGS could be handled and
    false if not.  As a special feature, passing a value of NULL for
-   PARGS, resets the options to the default. */
+   PARGS, resets the options to the default.  REREAD should be set
+   true if it is not the initial option parsing. */
 static int
-parse_rereadable_options (ARGPARSE_ARGS *pargs)
+parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
 {
   if (!pargs)
     { /* reset mode */
@@ -330,7 +346,9 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs)
       opt.pinentry_program = NULL;
       opt.scdaemon_program = NULL;
       opt.def_cache_ttl = DEFAULT_CACHE_TTL;
+      opt.max_cache_ttl = MAX_CACHE_TTL;
       opt.ignore_cache_for_signing = 0;
+      opt.allow_mark_trusted = 0;
       return 1;
     }
 
@@ -343,15 +361,29 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs)
     case oDebugAll: opt.debug = ~0; break;
     case oDebugLevel: debug_level = pargs->r.ret_str; break;
 
+    case oLogFile:
+      if (reread 
+          && (!current_logfile || !pargs->r.ret_str
+              || strcmp (current_logfile, pargs->r.ret_str)))
+        {
+          log_set_file (pargs->r.ret_str);
+          xfree (current_logfile);
+          current_logfile = xtrystrdup (pargs->r.ret_str);
+        }
+      break;
+
     case oNoGrab: opt.no_grab = 1; break;
       
     case oPinentryProgram: opt.pinentry_program = pargs->r.ret_str; break;
     case oScdaemonProgram: opt.scdaemon_program = pargs->r.ret_str; break;
 
     case oDefCacheTTL: opt.def_cache_ttl = pargs->r.ret_ulong; break;
+    case oMaxCacheTTL: opt.max_cache_ttl = pargs->r.ret_ulong; break;
       
     case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break;
 
+    case oAllowMarkTrusted: opt.allow_mark_trusted = 1; break;
+
     default:
       return 0; /* not handled */
     }
@@ -389,7 +421,7 @@ 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", 1|4); 
+  log_set_prefix ("gpg-agent", JNLIB_LOG_WITH_PREFIX|JNLIB_LOG_WITH_PID); 
 
   /* Try to auto set the character set.  */
   set_native_charset (NULL); 
@@ -424,7 +456,7 @@ main (int argc, char **argv )
 
   may_coredump = disable_core_dumps ();
 
-  parse_rereadable_options (NULL); /* Reset them to default values. */
+  parse_rereadable_options (NULL, 0); /* Reset them to default values. */
 
   shell = getenv ("SHELL");
   if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") )
@@ -503,7 +535,7 @@ main (int argc, char **argv )
 
   while (optfile_parse( configfp, configname, &configlineno, &pargs, opts) )
     {
-      if (parse_rereadable_options (&pargs))
+      if (parse_rereadable_options (&pargs, 0))
         continue; /* Already handled */
       switch (pargs.r_opt)
         {
@@ -569,7 +601,9 @@ main (int argc, char **argv )
       fprintf (stderr, "%s\n", strusage(15) );
     }
 #ifdef IS_DEVELOPMENT_VERSION
-  log_info ("NOTE: this is a development version!\n");
+  /* We don't want to print it here because gpg-agent is useful of its
+     own and quite matured.  */
+  /*log_info ("NOTE: this is a development version!\n");*/
 #endif
 
   set_debug ();
@@ -626,20 +660,28 @@ main (int argc, char **argv )
               GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME,
               GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME,
               GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME,
-              GC_OPT_FLAG_NONE );
+              GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME );
       printf ("default-cache-ttl:%lu:%d:\n",
               GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, DEFAULT_CACHE_TTL );
       printf ("no-grab:%lu:\n", 
               GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
       printf ("ignore-cache-for-signing:%lu:\n",
               GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
+      printf ("allow-mark-trusted:%lu:\n",
+              GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
 
       agent_exit (0);
     }
 
+  /* If this has been called without any options, we merely check
+     whether an agent is already running.  We do this here so that we
+     don't clobber a logfile but print it directly to stderr. */
   if (!pipe_server && !is_daemon)
-    log_info (_("please use the option `--daemon'"
-                " to run the program in the background\n"));
+    {
+      log_set_prefix (NULL, JNLIB_LOG_WITH_PREFIX); 
+      check_for_running_agent ();
+      agent_exit (0);
+    }
   
 #ifdef ENABLE_NLS
   /* gpg-agent usually does not output any messages because it runs in
@@ -652,11 +694,14 @@ main (int argc, char **argv )
     bind_textdomain_codeset (PACKAGE_GT, "UTF-8");
 #endif
 
-  /* now start with logging to a file if this is desired */
+  /* Now start with logging to a file if this is desired. */
   if (logfile)
     {
       log_set_file (logfile);
-      log_set_prefix (NULL, 1|2|4);
+      log_set_prefix (NULL, (JNLIB_LOG_WITH_PREFIX
+                             |JNLIB_LOG_WITH_TIME
+                             |JNLIB_LOG_WITH_PID));
+      current_logfile = xstrdup (logfile);
     }
 
   /* Make sure that we have a default ttyname. */
@@ -670,9 +715,9 @@ main (int argc, char **argv )
       start_command_handler (-1, -1);
     }
   else if (!is_daemon)
-    ;
+    ; /* NOTREACHED */
   else
-    { /* regular server mode */
+    { /* Regular server mode */
       int fd;
       pid_t pid;
       int len;
@@ -754,7 +799,7 @@ main (int argc, char **argv )
           exit (1);
         }
       else if (pid) 
-        { /* we are the parent */
+        { /* We are the parent */
           char *infostr;
           
           close (fd);
@@ -803,17 +848,20 @@ main (int argc, char **argv )
         } /* end parent */
       
 
-      /* this is the child */
+      /* 
+         This is the child
+       */
 
-      /* detach from tty and put process into a new session */
+      /* Detach from tty and put process into a new session */
       if (!nodetach )
         { 
           int i;
+          unsigned int oldflags;
 
-          /* close stdin, stdout and stderr unless it is the log stream */
+          /* Close stdin, stdout and stderr unless it is the log stream */
           for (i=0; i <= 2; i++) 
             {
-              if ( log_get_fd () != i)
+              if (!log_test_fd (i) )
                 close (i);
             }
           if (setsid() == -1)
@@ -822,6 +870,9 @@ main (int argc, char **argv )
               cleanup ();
               exit (1);
             }
+
+          log_get_prefix (&oldflags);
+          log_set_prefix (NULL, oldflags | JNLIB_LOG_RUN_DETACHED);
           opt.running_detached = 1;
         }
 
@@ -949,7 +1000,7 @@ reread_configuration (void)
       return;
     }
 
-  parse_rereadable_options (NULL); /* Start from the default values. */
+  parse_rereadable_options (NULL, 1); /* Start from the default values. */
 
   memset (&pargs, 0, sizeof pargs);
   dummy = 0;
@@ -960,7 +1011,7 @@ reread_configuration (void)
       if (pargs.r_opt < -1)
         pargs.err = 1; /* Print a warning. */
       else /* Try to parse this option - ignore unchangeable ones. */
-        parse_rereadable_options (&pargs);
+        parse_rereadable_options (&pargs, 1);
     }
   fclose (fp);
   set_debug ();
@@ -1048,18 +1099,15 @@ handle_signal (int signo)
                 "re-reading configuration and flushing cache\n");
       agent_flush_cache ();
       reread_configuration ();
+      agent_reload_trustlist ();
       break;
       
     case SIGUSR1:
-      if (opt.verbose < 5)
-        opt.verbose++;
-      log_info ("SIGUSR1 received - verbosity set to %d\n", opt.verbose);
+      log_info ("SIGUSR1 received - no action defined\n");
       break;
-
+      
     case SIGUSR2:
-      if (opt.verbose)
-        opt.verbose--;
-      log_info ("SIGUSR2 received - verbosity set to %d\n", opt.verbose );
+      log_info ("SIGUSR2 received - checking smartcard status\n");
       break;
 
     case SIGTERM:
@@ -1098,6 +1146,12 @@ start_connection_thread (void *arg)
 
   if (opt.verbose)
     log_info ("handler for fd %d started\n", fd);
+
+  /* FIXME: Move this housekeeping into a ticker function.  Calling it
+     for each connection should work but won't work anymore if our
+     cleints start to keep connections. */
+  agent_trustlist_housekeeping ();
+
   start_command_handler (-1, fd);
   if (opt.verbose)
     log_info ("handler for fd %d terminated\n", fd);
@@ -1119,7 +1173,7 @@ handle_connections (int listen_fd)
 
   tattr = pth_attr_new();
   pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
-  pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 32*1024);
+  pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 256*1024);
   pth_attr_set (tattr, PTH_ATTR_NAME, "gpg-agent");
 
   sigemptyset (&sigs );
@@ -1176,3 +1230,56 @@ handle_connections (int listen_fd)
   log_info ("%s %s stopped\n", strusage(11), strusage(13));
 }
 #endif /*USE_GNU_PTH*/
+
+
+/* Figure out whether an agent is available and running. Prints an
+   error if not.  */
+static void
+check_for_running_agent ()
+{
+  int rc;
+  char *infostr, *p;
+  assuan_context_t ctx;
+  int prot, pid;
+
+  infostr = getenv ("GPG_AGENT_INFO");
+  if (!infostr || !*infostr)
+    {
+      log_error (_("no gpg-agent running in this session\n"));
+      return;
+    }
+
+  infostr = xstrdup (infostr);
+  if ( !(p = strchr (infostr, ':')) || p == infostr)
+    {
+      log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
+      xfree (infostr);
+      return;
+    }
+
+  *p++ = 0;
+  pid = atoi (p);
+  while (*p && *p != ':')
+    p++;
+  prot = *p? atoi (p+1) : 0;
+  if (prot != 1)
+    {
+      log_error (_("gpg-agent protocol version %d is not supported\n"),
+                 prot);
+      xfree (infostr);
+      return;
+    }
+
+  rc = assuan_socket_connect (&ctx, infostr, pid);
+  xfree (infostr);
+  if (rc)
+    {
+      log_error ("can't connect to the agent: %s\n", assuan_strerror (rc));
+      return;
+    }
+
+  if (!opt.quiet)
+    log_info ("gpg-agent running and available\n");
+
+  assuan_disconnect (ctx);
+}