Updated FSF's address.
[gnupg.git] / tools / symcryptrun.c
index 9f3a55f..406cbb2 100644 (file)
@@ -15,7 +15,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 
  */
 
 
@@ -56,6 +57,9 @@
 
    Other classes may be added in the future.  */
 
 
    Other classes may be added in the future.  */
 
+#define SYMC_BAD_PASSPHRASE    2
+#define SYMC_CANCELED          3
+
 \f
 #include <config.h>
 
 \f
 #include <config.h>
 
@@ -82,6 +86,7 @@
 #define JNLIB_NEED_LOG_LOGV
 #include "i18n.h"
 #include "../common/util.h"
 #define JNLIB_NEED_LOG_LOGV
 #include "i18n.h"
 #include "../common/util.h"
+#include "mkdtemp.h"
 
 /* FIXME: Bah.  For spwq_secure_free.  */
 #define SIMPLE_PWQUERY_IMPLEMENTATION 1
 
 /* FIXME: Bah.  For spwq_secure_free.  */
 #define SIMPLE_PWQUERY_IMPLEMENTATION 1
@@ -107,6 +112,37 @@ my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
 }
 
 \f
 }
 
 \f
+/* From simple-gettext.c.  */
+
+/* We assume to have `unsigned long int' value with at least 32 bits.  */
+#define HASHWORDBITS 32
+
+/* The so called `hashpjw' function by P.J. Weinberger
+   [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
+   1986, 1987 Bell Telephone Laboratories, Inc.]  */
+
+static __inline__ ulong
+hash_string( const char *str_param )
+{
+    unsigned long int hval, g;
+    const char *str = str_param;
+
+    hval = 0;
+    while (*str != '\0')
+    {
+       hval <<= 4;
+       hval += (unsigned long int) *str++;
+       g = hval & ((unsigned long int) 0xf << (HASHWORDBITS - 4));
+       if (g != 0)
+       {
+         hval ^= g >> (HASHWORDBITS - 8);
+         hval ^= g;
+       }
+    }
+    return hval;
+}
+
+\f
 /* Constants to identify the commands and options. */
 enum cmd_and_opt_values
   {
 /* Constants to identify the commands and options. */
 enum cmd_and_opt_values
   {
@@ -124,6 +160,7 @@ enum cmd_and_opt_values
     oKeyfile,
     oDecrypt,
     oEncrypt,
     oKeyfile,
     oDecrypt,
     oEncrypt,
+    oInput
   };
 
 
   };
 
 
@@ -132,23 +169,23 @@ static ARGPARSE_OPTS opts[] =
   {
     { 301, NULL, 0, N_("@\nCommands:\n ") },
 
   {
     { 301, NULL, 0, N_("@\nCommands:\n ") },
 
-    { oDecrypt, "decrypt", 0, N_("decryption modus")},
-    { oEncrypt, "encrypt", 0, N_("encryption modus")},
+    { oDecrypt, "decrypt", 0, N_("decryption modus") },
+    { oEncrypt, "encrypt", 0, N_("encryption modus") },
     
     { 302, NULL, 0, N_("@\nOptions:\n ") },
     
     
     { 302, NULL, 0, N_("@\nOptions:\n ") },
     
-    { oClass, "class", 2, N_("tool class (confucius)")},
-    { oProgram, "program", 2, N_("program filename")},
-
-    { oKeyfile, "keyfile", 2, N_("secret key file (required)")},
+    { oClass, "class", 2, N_("tool class (confucius)") },
+    { oProgram, "program", 2, N_("program filename") },
 
 
+    { oKeyfile, "keyfile", 2, N_("secret key file (required)") },
+    { oInput, "inputfile", 2, N_("input file name (default stdin)") },
     { oVerbose, "verbose",  0, N_("verbose") },
     { oQuiet, "quiet",      0, N_("quiet") },
     { oVerbose, "verbose",  0, N_("verbose") },
     { oQuiet, "quiet",      0, N_("quiet") },
-    { oLogFile, "log-file", 2, N_("use a log file for the server")},
-    { oOptions,  "options"  , 2, N_("|FILE|read options from FILE")},
+    { oLogFile, "log-file", 2, N_("use a log file for the server") },
+    { oOptions,  "options"  , 2, N_("|FILE|read options from FILE") },
 
     /* Hidden options.  */
 
     /* Hidden options.  */
-    { oNoVerbose, "no-verbose",  0, "@"},
+    { oNoVerbose, "no-verbose",  0, "@" },
     { oHomedir, "homedir", 2, "@" },   
     { oNoOptions, "no-options", 0, "@" },/* shortcut for --options /dev/null */
 
     { oHomedir, "homedir", 2, "@" },   
     { oNoOptions, "no-options", 0, "@" },/* shortcut for --options /dev/null */
 
@@ -166,6 +203,7 @@ struct
   char *class;
   char *program;
   char *keyfile;
   char *class;
   char *program;
   char *keyfile;
+  char *input;
 } opt;
 
 \f
 } opt;
 
 \f
@@ -188,7 +226,7 @@ my_strusage (int level)
       break;
     case 41:
       p = _("Syntax: symcryptrun --class CLASS --program PROGRAM "
       break;
     case 41:
       p = _("Syntax: symcryptrun --class CLASS --program PROGRAM "
-           "--keyfile KEYFILE [options...] COMMAND\n"
+           "--keyfile KEYFILE [options...] COMMAND [inputfile]\n"
             "Call a simple symmetric encryption tool\n");
       break;
     case 31: p = "\nHome: "; break;
             "Call a simple symmetric encryption tool\n");
       break;
     case 31: p = "\nHome: "; break;
@@ -217,6 +255,21 @@ i18n_init(void)
 }
 
 \f
 }
 
 \f
+/* This is in the GNU C library in unistd.h.  */
+
+#ifndef TEMP_FAILURE_RETRY
+/* Evaluate EXPRESSION, and repeat as long as it returns -1 with `errno'
+   set to EINTR.  */
+
+# define TEMP_FAILURE_RETRY(expression) \
+  (__extension__                                                              \
+    ({ long int __result;                                                     \
+       do __result = (long int) (expression);                                 \
+       while (__result == -1L && errno == EINTR);                             \
+       __result; }))
+#endif
+
+
 /* Unlink a file, and shred it if SHRED is true.  */
 int
 remove_file (char *name, int shred)
 /* Unlink a file, and shred it if SHRED is true.  */
 int
 remove_file (char *name, int shred)
@@ -395,9 +448,10 @@ confucius_copy_file (char *infile, char *outfile, int plain)
    pointer, it will be set to true or false, depending on if the user
    canceled the operation or not.  On error (including cancelation), a
    null pointer is returned.  The passphrase must be deallocated with
    pointer, it will be set to true or false, depending on if the user
    canceled the operation or not.  On error (including cancelation), a
    null pointer is returned.  The passphrase must be deallocated with
-   confucius_drop_pass.  */
+   confucius_drop_pass.  CACHEID is the ID to be used for passphrase
+   caching and can be NULL to disable caching.  */
 char *
 char *
-confucius_get_pass (int again, int *canceled)
+confucius_get_pass (const char *cacheid, int again, int *canceled)
 {
   int err;
   char *pw;
 {
   int err;
   char *pw;
@@ -426,7 +480,7 @@ confucius_get_pass (int again, int *canceled)
     }
 #endif
 
     }
 #endif
 
-  pw = simple_pwquery (NULL,
+  pw = simple_pwquery (cacheid,
                        again ? _("does not match - try again"):NULL,
                        _("Passphrase:"), NULL, &err);
 
                        again ? _("does not match - try again"):NULL,
                        _("Passphrase:"), NULL, &err);
 
@@ -468,15 +522,10 @@ confucius_drop_pass (char *pass)
    requested.  If it is oDecrypt, decryption is requested.  INFILE and
    OUTFILE are the temporary files used in the process.  */
 int
    requested.  If it is oDecrypt, decryption is requested.  INFILE and
    OUTFILE are the temporary files used in the process.  */
 int
-confucius_process (int mode, char *infile, char *outfile)
+confucius_process (int mode, char *infile, char *outfile,
+                  int argc, char *argv[])
 {
 {
-  char *const args[] = { opt.program,
-                        mode == oEncrypt ? "-m1" : "-m2",
-                        "-q", infile,
-                        "-z", outfile,
-                        "-s", opt.keyfile,
-                        mode == oEncrypt ? "-af" : "-f",
-                        NULL };
+  char **args;
   int cstderr[2];
   int master;
   int slave;
   int cstderr[2];
   int master;
   int slave;
@@ -484,6 +533,7 @@ confucius_process (int mode, char *infile, char *outfile)
   pid_t pid;
   pid_t wpid;
   int tries = 0;
   pid_t pid;
   pid_t wpid;
   int tries = 0;
+  char cacheid[40];
 
   signal (SIGPIPE, SIG_IGN);
 
 
   signal (SIGPIPE, SIG_IGN);
 
@@ -505,9 +555,33 @@ confucius_process (int mode, char *infile, char *outfile)
       return 1;
     }
 
       return 1;
     }
 
+  /* Generate a hash from the keyfile name for caching.  */
+  snprintf (cacheid, sizeof (cacheid), "confucius:%lu",
+           hash_string (opt.keyfile));
+  cacheid[sizeof (cacheid) - 1] = '\0';
+  args = malloc (sizeof (char *) * (10 + argc));
+  if (!args)
+    {
+      log_error (_("cannot allocate args vector\n"));
+      return 1;
+    }
+  args[0] = opt.program;
+  args[1] = (mode == oEncrypt) ? "-m1" : "-m2";
+  args[2] = "-q";
+  args[3] = infile;
+  args[4] = "-z";
+  args[5] = outfile;
+  args[6] = "-s";
+  args[7] = opt.keyfile;
+  args[8] = (mode == oEncrypt) ? "-af" : "-f";
+  args[9 + argc] = NULL;
+  while (argc--)
+    args[9 + argc] = argv[argc];
+
   if (pipe (cstderr) < 0)
     {
       log_error (_("could not create pipe: %s\n"), strerror (errno));
   if (pipe (cstderr) < 0)
     {
       log_error (_("could not create pipe: %s\n"), strerror (errno));
+      free (args);
       return 1;
     }
 
       return 1;
     }
 
@@ -516,6 +590,7 @@ confucius_process (int mode, char *infile, char *outfile)
       log_error (_("could not create pty: %s\n"), strerror (errno));
       close (cstderr[0]);
       close (cstderr[1]);
       log_error (_("could not create pty: %s\n"), strerror (errno));
       close (cstderr[0]);
       close (cstderr[1]);
+      free (args);
       return -1;
     }
 
       return -1;
     }
 
@@ -533,6 +608,7 @@ confucius_process (int mode, char *infile, char *outfile)
       close (slave);
       close (cstderr[0]);
       close (cstderr[1]);
       close (slave);
       close (cstderr[0]);
       close (cstderr[1]);
+      free (args);
       return 1;
     }
   else if (pid == 0) 
       return 1;
     }
   else if (pid == 0) 
@@ -569,6 +645,7 @@ confucius_process (int mode, char *infile, char *outfile)
 
       close (slave);
       close (cstderr[1]);
 
       close (slave);
       close (cstderr[1]);
+      free (args);
 
       /* Listen on the output FDs.  */
       do
 
       /* Listen on the output FDs.  */
       do
@@ -672,13 +749,20 @@ confucius_process (int mode, char *infile, char *outfile)
                      char *pass;
                      int canceled;
 
                      char *pass;
                      int canceled;
 
-                     pass = confucius_get_pass (tries ? 1 : 0, &canceled);
+                     /* If this is not the first attempt, the
+                        passphrase seems to be wrong, so clear the
+                        cache.  */
+                     if (tries)
+                       simple_pwclear (cacheid);
+
+                     pass = confucius_get_pass (cacheid,
+                                                tries ? 1 : 0, &canceled);
                      if (!pass)
                        {
                          kill (pid, SIGTERM);
                          close (master);
                          close (cstderr[0]);
                      if (!pass)
                        {
                          kill (pid, SIGTERM);
                          close (master);
                          close (cstderr[0]);
-                         return canceled ? 3 : 1;
+                         return canceled ? SYMC_CANCELED : 1;
                        }
                      write (master, pass, strlen (pass));
                      write (master, "\n", 1);
                        }
                      write (master, pass, strlen (pass));
                      write (master, "\n", 1);
@@ -700,6 +784,8 @@ confucius_process (int mode, char *infile, char *outfile)
          log_error (_("waitpid failed: %s\n"), strerror (errno));
 
          kill (pid, SIGTERM);
          log_error (_("waitpid failed: %s\n"), strerror (errno));
 
          kill (pid, SIGTERM);
+         /* State of cached password is unclear.  Just remove it.  */
+         simple_pwclear (cacheid);
          return 1;
        }
       else
          return 1;
        }
       else
@@ -710,15 +796,22 @@ confucius_process (int mode, char *infile, char *outfile)
          if (!WIFEXITED (res))
            {
              log_error (_("child aborted with status %i\n"), res);
          if (!WIFEXITED (res))
            {
              log_error (_("child aborted with status %i\n"), res);
+
+             /* State of cached password is unclear.  Just remove it.  */
+             simple_pwclear (cacheid);
+
              return 1;
            }
 
          if (WEXITSTATUS (res))
            {
              return 1;
            }
 
          if (WEXITSTATUS (res))
            {
+             /* The passphrase was wrong.  Remove it from the cache.  */
+             simple_pwclear (cacheid);
+
              /* We probably exceeded our number of attempts at guessing
                 the password.  */
              if (tries >= 3)
              /* We probably exceeded our number of attempts at guessing
                 the password.  */
              if (tries >= 3)
-               return 2;
+               return SYMC_BAD_PASSPHRASE;
              else
                return 1;
            }
              else
                return 1;
            }
@@ -735,27 +828,36 @@ confucius_process (int mode, char *infile, char *outfile)
    requested.  If it is oDecrypt, decryption is requested.  The other
    parameters are taken from the global option data.  */
 int
    requested.  If it is oDecrypt, decryption is requested.  The other
    parameters are taken from the global option data.  */
 int
-confucius_main (int mode)
+confucius_main (int mode, int argc, char *argv[])
 {
   int res;
   char *tmpdir;
   char *infile;
 {
   int res;
   char *tmpdir;
   char *infile;
+  int infile_from_stdin = 0;
   char *outfile;
 
   tmpdir = confucius_mktmpdir ();
   if (!tmpdir)
     return 1;
   char *outfile;
 
   tmpdir = confucius_mktmpdir ();
   if (!tmpdir)
     return 1;
-  
-  /* TMPDIR + "/" + "in" + "\0".  */
-  infile = malloc (strlen (tmpdir) + 1 + 2 + 1);
-  if (!infile)
+
+  if (opt.input && !(opt.input[0] == '-' && opt.input[1] == '\0'))
+    infile = xstrdup (opt.input);
+  else
     {
     {
-      log_error (_("cannot allocate infile string: %s\n"), strerror (errno));
-      rmdir (tmpdir);
-      return 1;
+      infile_from_stdin = 1;
+
+      /* TMPDIR + "/" + "in" + "\0".  */
+      infile = malloc (strlen (tmpdir) + 1 + 2 + 1);
+      if (!infile)
+       {
+         log_error (_("cannot allocate infile string: %s\n"),
+                    strerror (errno));
+         rmdir (tmpdir);
+         return 1;
+       }
+      strcpy (infile, tmpdir);
+      strcat (infile, "/in");
     }
     }
-  strcpy (infile, tmpdir);
-  strcat (infile, "/in");
 
   /* TMPDIR + "/" + "out" + "\0".  */
   outfile = malloc (strlen (tmpdir) + 1 + 3 + 1);
 
   /* TMPDIR + "/" + "out" + "\0".  */
   outfile = malloc (strlen (tmpdir) + 1 + 3 + 1);
@@ -769,23 +871,27 @@ confucius_main (int mode)
   strcpy (outfile, tmpdir);
   strcat (outfile, "/out");
 
   strcpy (outfile, tmpdir);
   strcat (outfile, "/out");
 
-  /* Create INFILE and fill it with content.  */
-  res = confucius_copy_file ("-", infile, mode == oEncrypt);
-  if (res)
+  if (infile_from_stdin)
     {
     {
-      free (outfile);
-      free (infile);
-      rmdir (tmpdir);
-      return res;
+      /* Create INFILE and fill it with content.  */
+      res = confucius_copy_file ("-", infile, mode == oEncrypt);
+      if (res)
+       {
+         free (outfile);
+         free (infile);
+         rmdir (tmpdir);
+         return res;
+       }
     }
 
   /* Run the engine and thus create the output file, handling
      passphrase retrieval.  */
     }
 
   /* Run the engine and thus create the output file, handling
      passphrase retrieval.  */
-  res = confucius_process (mode, infile, outfile);
+  res = confucius_process (mode, infile, outfile, argc, argv);
   if (res)
     {
       remove_file (outfile, mode == oDecrypt);
   if (res)
     {
       remove_file (outfile, mode == oDecrypt);
-      remove_file (infile, mode == oEncrypt);
+      if (infile_from_stdin)
+       remove_file (infile, mode == oEncrypt);
       free (outfile);
       free (infile);
       rmdir (tmpdir);
       free (outfile);
       free (infile);
       rmdir (tmpdir);
@@ -797,7 +903,8 @@ confucius_main (int mode)
   if (res)
     {
       remove_file (outfile, mode == oDecrypt);
   if (res)
     {
       remove_file (outfile, mode == oDecrypt);
-      remove_file (infile, mode == oEncrypt);
+      if (infile_from_stdin)
+       remove_file (infile, mode == oEncrypt);
       free (outfile);
       free (infile);
       rmdir (tmpdir);
       free (outfile);
       free (infile);
       rmdir (tmpdir);
@@ -805,7 +912,8 @@ confucius_main (int mode)
     }
   
   remove_file (outfile, mode == oDecrypt);
     }
   
   remove_file (outfile, mode == oDecrypt);
-  remove_file (infile, mode == oEncrypt);
+  if (infile_from_stdin)
+    remove_file (infile, mode == oEncrypt);
   free (outfile);
   free (infile);
   rmdir (tmpdir);
   free (outfile);
   free (infile);
   rmdir (tmpdir);
@@ -900,6 +1008,7 @@ main (int argc, char **argv)
        case oClass:    opt.class = pargs.r.ret_str; break;
        case oProgram:  opt.program = pargs.r.ret_str; break;
        case oKeyfile:  opt.keyfile = pargs.r.ret_str; break;
        case oClass:    opt.class = pargs.r.ret_str; break;
        case oProgram:  opt.program = pargs.r.ret_str; break;
        case oKeyfile:  opt.keyfile = pargs.r.ret_str; break;
+       case oInput:    opt.input = pargs.r.ret_str; break;
 
         case oLogFile:  logfile = pargs.r.ret_str; break;
 
 
         case oLogFile:  logfile = pargs.r.ret_str; break;
 
@@ -953,7 +1062,7 @@ main (int argc, char **argv)
       res = 1;
     }
   else if (!strcmp (opt.class, "confucius"))
       res = 1;
     }
   else if (!strcmp (opt.class, "confucius"))
-    res = confucius_main (mode);
+    res = confucius_main (mode, argc, argv);
   else
     {
       log_error (_("class %s is not supported\n"), opt.class);
   else
     {
       log_error (_("class %s is not supported\n"), opt.class);