* configure.ac (gl_INIT): Add gnulib stuff.
[gnupg.git] / tools / symcryptrun.c
1 /* symcryptrun.c - Tool to call simple symmetric encryption tools.
2  *      Copyright (C) 2005 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21
22 /* Sometimes simple encryption tools are already in use for a long
23    time and there is a desire to integrate them into the GnuPG
24    framework.  The protocols and encryption methods might be
25    non-standard or not even properly documented, so that a
26    full-fledged encryption tool with an interface like gpg is not
27    doable.  This simple wrapper program provides a solution: It
28    operates by calling the encryption/decryption module and providing
29    the passphrase for a key (or even the key directly) using the
30    standard pinentry mechanism through gpg-agent.  */
31
32 /* This program is invoked in the following way:
33
34    symcryptrun --class CLASS --program PROGRAM --keyfile KEYFILE \
35      [--decrypt | --encrypt]
36
37    For encryption, the plain text must be provided on STDIN, and the
38    ciphertext will be output to STDOUT.  For decryption vice versa.
39
40    CLASS can currently only be "confucius".
41
42    PROGRAM must be the path to the crypto engine.
43
44    KEYFILE must contain the secret key, which may be protected by a
45    passphrase.  The passphrase is retrieved via the pinentry program.
46
47
48    The GPG Agent _must_ be running before starting symcryptrun.
49
50    The possible exit status codes:
51
52    0    Success
53    1    Some error occured
54    2    No valid passphrase was provided
55    3    The operation was canceled by the user
56
57    Other classes may be added in the future.  */
58
59 \f
60 #include <config.h>
61
62 #include <unistd.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <errno.h>
67 #include <assert.h>
68 #include <sys/stat.h>
69 #include <sys/types.h>
70 #include <sys/wait.h>
71 #include <pty.h>
72 #include <utmp.h>
73 #include <ctype.h>
74 #ifdef HAVE_LOCALE_H
75 #include <locale.h>
76 #endif
77 #ifdef HAVE_LANGINFO_CODESET
78 #include <langinfo.h>
79 #endif
80 #include <gpg-error.h>
81
82 #define JNLIB_NEED_LOG_LOGV
83 #include "i18n.h"
84 #include "../common/util.h"
85 #include "mkdtemp.h"
86
87 /* FIXME: Bah.  For spwq_secure_free.  */
88 #define SIMPLE_PWQUERY_IMPLEMENTATION 1
89 #include "../common/simple-pwquery.h"
90
91 \f
92 /* Used by gcry for logging */
93 static void
94 my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
95 {
96   /* translate the log levels */
97   switch (level)
98     {
99     case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break;
100     case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break;
101     case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break;
102     case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break;
103     case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
104     case GCRY_LOG_BUG:  level = JNLIB_LOG_BUG; break;
105     case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
106     default:            level = JNLIB_LOG_ERROR; break;      }
107   log_logv (level, fmt, arg_ptr);
108 }
109
110 \f
111 /* Constants to identify the commands and options. */
112 enum cmd_and_opt_values
113   {
114     aNull = 0,
115     oQuiet      = 'q',
116     oVerbose    = 'v',
117
118     oNoVerbose  = 500,
119     oOptions,
120     oNoOptions,
121     oLogFile,
122     oHomedir,
123     oClass,
124     oProgram,
125     oKeyfile,
126     oDecrypt,
127     oEncrypt,
128     oInput
129   };
130
131
132 /* The list of commands and options.  */
133 static ARGPARSE_OPTS opts[] =
134   {
135     { 301, NULL, 0, N_("@\nCommands:\n ") },
136
137     { oDecrypt, "decrypt", 0, N_("decryption modus") },
138     { oEncrypt, "encrypt", 0, N_("encryption modus") },
139     
140     { 302, NULL, 0, N_("@\nOptions:\n ") },
141     
142     { oClass, "class", 2, N_("tool class (confucius)") },
143     { oProgram, "program", 2, N_("program filename") },
144
145     { oKeyfile, "keyfile", 2, N_("secret key file (required)") },
146     { oInput, "inputfile", 2, N_("input file name (default stdin)") },
147     { oVerbose, "verbose",  0, N_("verbose") },
148     { oQuiet, "quiet",      0, N_("quiet") },
149     { oLogFile, "log-file", 2, N_("use a log file for the server") },
150     { oOptions,  "options"  , 2, N_("|FILE|read options from FILE") },
151
152     /* Hidden options.  */
153     { oNoVerbose, "no-verbose",  0, "@" },
154     { oHomedir, "homedir", 2, "@" },   
155     { oNoOptions, "no-options", 0, "@" },/* shortcut for --options /dev/null */
156
157     {0}
158   };
159
160
161 /* We keep all global options in the structure OPT.  */
162 struct
163 {
164   int verbose;          /* Verbosity level.  */
165   int quiet;            /* Be extra quiet.  */
166   const char *homedir;  /* Configuration directory name */
167
168   char *class;
169   char *program;
170   char *keyfile;
171   char *input;
172 } opt;
173
174 \f
175 /* Print usage information and and provide strings for help.  */
176 static const char *
177 my_strusage (int level)
178 {
179   const char *p;
180
181   switch (level)
182     {
183     case 11: p = "symcryptrun (GnuPG)";
184       break;
185     case 13: p = VERSION; break;
186     case 17: p = PRINTABLE_OS_NAME; break;
187     case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
188       break;
189     case 1:
190     case 40: p = _("Usage: symcryptrun [options] (-h for help)");
191       break;
192     case 41:
193       p = _("Syntax: symcryptrun --class CLASS --program PROGRAM "
194             "--keyfile KEYFILE [options...] COMMAND [inputfile]\n"
195             "Call a simple symmetric encryption tool\n");
196       break;
197     case 31: p = "\nHome: "; break;
198     case 32: p = opt.homedir; break;
199     case 33: p = "\n"; break;
200
201     default: p = NULL; break;
202     }
203   return p;
204 }
205
206
207 /* Initialize the gettext system.  */
208 static void
209 i18n_init(void)
210 {
211 #ifdef USE_SIMPLE_GETTEXT
212   set_gettext_file (PACKAGE_GT);
213 #else
214 # ifdef ENABLE_NLS
215   setlocale (LC_ALL, "");
216   bindtextdomain (PACKAGE_GT, LOCALEDIR);
217   textdomain (PACKAGE_GT);
218 # endif
219 #endif
220 }
221
222 \f
223 /* This is in the GNU C library in unistd.h.  */
224
225 #ifndef TEMP_FAILURE_RETRY
226 /* Evaluate EXPRESSION, and repeat as long as it returns -1 with `errno'
227    set to EINTR.  */
228
229 # define TEMP_FAILURE_RETRY(expression) \
230   (__extension__                                                              \
231     ({ long int __result;                                                     \
232        do __result = (long int) (expression);                                 \
233        while (__result == -1L && errno == EINTR);                             \
234        __result; }))
235 #endif
236
237
238 /* Unlink a file, and shred it if SHRED is true.  */
239 int
240 remove_file (char *name, int shred)
241 {
242   if (!shred)
243     return unlink (name);
244   else
245     {
246       int status;
247       pid_t pid;
248
249       pid = fork ();
250       if (pid == 0)
251         {
252           /* Child.  */
253           
254           /* -f forces file to be writable, and -u unlinks it afterwards.  */
255           char *args[] = { SHRED, "-uf", name, NULL };
256           
257           execv (SHRED, args);
258           _exit (127);
259         }
260       else if (pid < 0)
261         {
262           /* Fork failed.  */
263           status = -1;
264         }
265       else
266         {
267           /* Parent.  */
268           
269           if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid)
270             status = -1;
271         }
272       
273       if (!WIFEXITED (status))
274         {
275           log_error (_("%s on %s aborted with status %i\n"),
276                      SHRED, name, status);
277           unlink (name);
278           return 1;
279         }
280       else if (WEXITSTATUS (status))
281         {
282           log_error (_("%s on %s failed with status %i\n"), SHRED, name,
283                      WEXITSTATUS (status));
284           unlink (name);
285           return 1;
286         }
287
288       return 0;
289     }
290 }
291
292 \f
293 /* Class Confucius.
294
295    "Don't worry that other people don't know you;
296    worry that you don't know other people."            Analects--1.16.  */
297
298 /* Create temporary directory with mode 0700.  Returns a dynamically
299    allocated string with the filename of the directory.  */
300 static char *
301 confucius_mktmpdir (void)
302 {
303   char *name;
304
305   name = strdup ("/tmp/gpg-XXXXXX");
306   if (!name || !mkdtemp (name))
307     {
308       log_error (_("can't create temporary directory `%s': %s\n"),
309                  name?name:"", strerror (errno));
310       return NULL;
311     }
312
313   return name;
314 }
315
316
317 /* Buffer size for I/O operations.  */
318 #define CONFUCIUS_BUFSIZE 4096
319
320 /* Buffer size for output lines.  */
321 #define CONFUCIUS_LINESIZE 4096
322
323
324 /* Copy the file IN to OUT, either of which may be "-".  If PLAIN is
325    true, and the copying fails, and OUT is not STDOUT, then shred the
326    file instead unlinking it.  */
327 static int
328 confucius_copy_file (char *infile, char *outfile, int plain)
329 {
330   FILE *in;
331   int in_is_stdin = 0;
332   FILE *out;
333   int out_is_stdout = 0;
334   char data[CONFUCIUS_BUFSIZE];
335   ssize_t data_len;
336
337   if (infile[0] == '-' && infile[1] == '\0')
338     {
339       /* FIXME: Is stdin in binary mode?  */
340       in = stdin;
341       in_is_stdin = 1;
342     }
343   else
344     {
345       in = fopen (infile, "rb");
346       if (!in)
347         {
348           log_error (_("could not open %s for writing: %s\n"),
349                      infile, strerror (errno));
350           return 1;
351         }
352     }
353
354   if (outfile[0] == '-' && outfile[1] == '\0')
355     {
356       /* FIXME: Is stdout in binary mode?  */
357       out = stdout;
358       out_is_stdout = 1;
359     }
360   else
361     {
362       out = fopen (outfile, "wb");
363       if (!out)
364         {
365           log_error (_("could not open %s for writing: %s\n"),
366                      infile, strerror (errno));
367           return 1;
368         }
369     }
370
371   /* Now copy the data.  */
372   while ((data_len = fread (data, 1, sizeof (data), in)) > 0)
373     {
374       if (fwrite (data, 1, data_len, out) != data_len)
375         {
376           log_error (_("error writing to %s: %s\n"), outfile,
377                      strerror (errno));
378           goto copy_err;
379         }
380     }
381   if (data_len < 0 || ferror (in))
382     {
383       log_error (_("error reading from %s: %s\n"), infile, strerror (errno));
384       goto copy_err;
385     }
386
387   /* Close IN if appropriate.  */
388   if (!in_is_stdin && fclose (in) && ferror (in))
389     {
390       log_error (_("error closing %s: %s\n"), infile, strerror (errno));
391       goto copy_err;
392     }
393
394   /* Close OUT if appropriate.  */
395   if (!out_is_stdout && fclose (out) && ferror (out))
396     {
397       log_error (_("error closing %s: %s\n"), infile, strerror (errno));
398       goto copy_err;
399     }
400
401   return 0;
402
403  copy_err:
404   if (!out_is_stdout)
405     remove_file (outfile, plain);
406
407   return 1;
408 }
409
410
411 /* Get a passphrase in secure storage (if possible).  If AGAIN is
412    true, then this is a repeated attempt.  If CANCELED is not a null
413    pointer, it will be set to true or false, depending on if the user
414    canceled the operation or not.  On error (including cancelation), a
415    null pointer is returned.  The passphrase must be deallocated with
416    confucius_drop_pass.  */
417 char *
418 confucius_get_pass (int again, int *canceled)
419 {
420   int err;
421   char *pw;
422 #ifdef HAVE_LANGINFO_CODESET
423   char *orig_codeset = NULL;
424 #endif
425
426   if (canceled)
427     *canceled = 0;
428   
429 #ifdef ENABLE_NLS
430   /* The Assuan agent protocol requires us to transmit utf-8 strings */
431   orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL);
432 #ifdef HAVE_LANGINFO_CODESET
433   if (!orig_codeset)
434     orig_codeset = nl_langinfo (CODESET);
435 #endif
436   if (orig_codeset && !strcmp (orig_codeset, "UTF-8"))
437     orig_codeset = NULL;
438   if (orig_codeset)
439     {
440       /* We only switch when we are able to restore the codeset later. */
441       orig_codeset = xstrdup (orig_codeset);
442       if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8"))
443         orig_codeset = NULL; 
444     }
445 #endif
446
447   pw = simple_pwquery (NULL,
448                        again ? _("does not match - try again"):NULL,
449                        _("Passphrase:"), NULL, &err);
450
451 #ifdef ENABLE_NLS
452   if (orig_codeset)
453     {
454       bind_textdomain_codeset (PACKAGE_GT, orig_codeset);
455       xfree (orig_codeset);
456     }
457 #endif
458
459   if (!pw)
460     {
461       if (err)
462         log_error (_("error while asking for the passphrase: %s\n"),
463                    gpg_strerror (err));
464       else
465         {
466           log_info (_("cancelled\n"));
467           if (canceled)
468             *canceled = 1;
469         }      
470     }
471
472   return pw;
473 }
474
475
476 /* Drop a passphrase retrieved with confucius_get_pass.  */
477 void
478 confucius_drop_pass (char *pass)
479 {
480   if (pass)
481     spwq_secure_free (pass);
482 }
483
484
485 /* Run a confucius crypto engine.  If MODE is oEncrypt, encryption is
486    requested.  If it is oDecrypt, decryption is requested.  INFILE and
487    OUTFILE are the temporary files used in the process.  */
488 int
489 confucius_process (int mode, char *infile, char *outfile)
490 {
491   char *const args[] = { opt.program,
492                          mode == oEncrypt ? "-m1" : "-m2",
493                          "-q", infile,
494                          "-z", outfile,
495                          "-s", opt.keyfile,
496                          mode == oEncrypt ? "-af" : "-f",
497                          NULL };
498   int cstderr[2];
499   int master;
500   int slave;
501   int res;
502   pid_t pid;
503   pid_t wpid;
504   int tries = 0;
505
506   signal (SIGPIPE, SIG_IGN);
507
508   if (!opt.program)
509     {
510       log_error (_("no --program option provided\n"));
511       return 1;
512     }
513
514   if (mode != oDecrypt && mode != oEncrypt)
515     {
516       log_error (_("only --decrypt and --encrypt are supported\n"));
517       return 1;
518     }
519
520   if (!opt.keyfile)
521     {
522       log_error (_("no --keyfile option provided\n"));
523       return 1;
524     }
525
526   if (pipe (cstderr) < 0)
527     {
528       log_error (_("could not create pipe: %s\n"), strerror (errno));
529       return 1;
530     }
531
532   if (openpty (&master, &slave, NULL, NULL, NULL) == -1)
533     {
534       log_error (_("could not create pty: %s\n"), strerror (errno));
535       close (cstderr[0]);
536       close (cstderr[1]);
537       return -1;
538     }
539
540   /* We don't want to deal with the worst case scenarios.  */
541   assert (master > 2);
542   assert (slave > 2);
543   assert (cstderr[0] > 2);
544   assert (cstderr[1] > 2);
545
546   pid = fork ();
547   if (pid < 0)
548     {
549       log_error (_("could not fork: %s\n"), strerror (errno));
550       close (master);
551       close (slave);
552       close (cstderr[0]);
553       close (cstderr[1]);
554       return 1;
555     }
556   else if (pid == 0) 
557     {
558       /* Child.  */
559
560       /* Close the parent ends.  */
561       close (master);
562       close (cstderr[0]);
563
564       /* Change controlling terminal.  */
565       if (login_tty (slave))
566         {
567           /* It's too early to output a debug message.  */
568           _exit (1);
569         }
570
571       dup2 (cstderr[1], 2);
572       close (cstderr[1]);
573
574       /* Now kick off the engine program.  */
575       execv (opt.program, args);
576       log_error (_("execv failed: %s\n"), strerror (errno));
577       _exit (1);
578     }
579   else
580     {
581       /* Parent.  */
582       char buffer[CONFUCIUS_LINESIZE];
583       int buffer_len = 0;
584       fd_set fds;
585       int slave_closed = 0;
586       int stderr_closed = 0;
587
588       close (slave);
589       close (cstderr[1]);
590
591       /* Listen on the output FDs.  */
592       do
593         {
594           FD_ZERO (&fds);
595
596           if (!slave_closed)
597             FD_SET (master, &fds);
598           if (!stderr_closed)
599             FD_SET (cstderr[0], &fds);
600
601           res = select (FD_SETSIZE, &fds, NULL, NULL, NULL);
602           if (res < 0)
603             {
604               log_error (_("select failed: %s\n"), strerror (errno));
605
606               kill (pid, SIGTERM);
607               close (master);
608               close (cstderr[0]);
609               return 1;
610             }
611
612           if (FD_ISSET (cstderr[0], &fds))
613             {
614               /* We got some output on stderr.  This is just passed
615                  through via the logging facility.  */
616
617               res = read (cstderr[0], &buffer[buffer_len],
618                           sizeof (buffer) - buffer_len - 1);
619               if (res < 0)
620                 {
621                   log_error (_("read failed: %s\n"), strerror (errno));
622
623                   kill (pid, SIGTERM);
624                   close (master);
625                   close (cstderr[0]);
626                   return 1;
627                 }
628               else  
629                 {
630                   char *newline;
631
632                   buffer_len += res;
633                   for (;;)
634                     {
635                       buffer[buffer_len] = '\0';
636                       newline = strchr (buffer, '\n');
637                       if (newline)
638                         {
639                           *newline = '\0';
640                           log_error ("%s\n", buffer);
641                           buffer_len -= newline + 1 - buffer;
642                           memmove (buffer, newline + 1, buffer_len);
643                         }
644                       else if (buffer_len == sizeof (buffer) - 1)
645                         {
646                           /* Overflow.  */
647                           log_error ("%s\n", buffer);
648                           buffer_len = 0;
649                         }
650                       else
651                         break;
652                     }
653
654                   if (res == 0)
655                     stderr_closed = 1;
656                 }
657             }
658           else if (FD_ISSET (master, &fds))
659             {
660               char data[512];
661
662               res = read (master, data, sizeof (data));
663               if (res < 0)
664                 {
665                   if (errno == EIO)
666                     {
667                       /* Slave-side close leads to readable fd and
668                          EIO.  */
669                       slave_closed = 1;
670                     }
671                   else
672                     {
673                       log_error (_("pty read failed: %s\n"), strerror (errno));
674
675                       kill (pid, SIGTERM);
676                       close (master);
677                       close (cstderr[0]);
678                       return 1;
679                     }
680                 }
681               else if (res == 0)
682                 /* This never seems to be what happens on slave-side
683                    close.  */
684                 slave_closed = 1;
685               else
686                 {
687                   /* Check for password prompt.  */
688                   if (data[res - 1] == ':')
689                     {
690                       char *pass;
691                       int canceled;
692
693                       pass = confucius_get_pass (tries ? 1 : 0, &canceled);
694                       if (!pass)
695                         {
696                           kill (pid, SIGTERM);
697                           close (master);
698                           close (cstderr[0]);
699                           return canceled ? 3 : 1;
700                         }
701                       write (master, pass, strlen (pass));
702                       write (master, "\n", 1);
703                       confucius_drop_pass (pass);
704
705                       tries++;
706                     }
707                 }
708             }
709         }
710       while (!stderr_closed || !slave_closed);
711
712       close (master);
713       close (cstderr[0]);
714
715       wpid = waitpid (pid, &res, 0);
716       if (wpid < 0)
717         {
718           log_error (_("waitpid failed: %s\n"), strerror (errno));
719
720           kill (pid, SIGTERM);
721           return 1;
722         }
723       else
724         {
725           /* Shouldn't happen, as we don't use WNOHANG.  */
726           assert (wpid != 0);
727
728           if (!WIFEXITED (res))
729             {
730               log_error (_("child aborted with status %i\n"), res);
731               return 1;
732             }
733
734           if (WEXITSTATUS (res))
735             {
736               /* We probably exceeded our number of attempts at guessing
737                  the password.  */
738               if (tries >= 3)
739                 return 2;
740               else
741                 return 1;
742             }
743
744           return 0;
745         }
746     }
747
748   /* Not reached.  */
749 }
750
751
752 /* Class confucius main program.  If MODE is oEncrypt, encryption is
753    requested.  If it is oDecrypt, decryption is requested.  The other
754    parameters are taken from the global option data.  */
755 int
756 confucius_main (int mode)
757 {
758   int res;
759   char *tmpdir;
760   char *infile;
761   int infile_from_stdin = 0;
762   char *outfile;
763
764   tmpdir = confucius_mktmpdir ();
765   if (!tmpdir)
766     return 1;
767
768   if (opt.input && !(opt.input[0] == '-' && opt.input[1] == '\0'))
769     infile = xstrdup (opt.input);
770   else
771     {
772       infile_from_stdin = 1;
773
774       /* TMPDIR + "/" + "in" + "\0".  */
775       infile = malloc (strlen (tmpdir) + 1 + 2 + 1);
776       if (!infile)
777         {
778           log_error (_("cannot allocate infile string: %s\n"),
779                      strerror (errno));
780           rmdir (tmpdir);
781           return 1;
782         }
783       strcpy (infile, tmpdir);
784       strcat (infile, "/in");
785     }
786
787   /* TMPDIR + "/" + "out" + "\0".  */
788   outfile = malloc (strlen (tmpdir) + 1 + 3 + 1);
789   if (!outfile)
790     {
791       log_error (_("cannot allocate outfile string: %s\n"), strerror (errno));
792       free (infile);
793       rmdir (tmpdir);
794       return 1;
795     }
796   strcpy (outfile, tmpdir);
797   strcat (outfile, "/out");
798
799   if (infile_from_stdin)
800     {
801       /* Create INFILE and fill it with content.  */
802       res = confucius_copy_file ("-", infile, mode == oEncrypt);
803       if (res)
804         {
805           free (outfile);
806           free (infile);
807           rmdir (tmpdir);
808           return res;
809         }
810     }
811
812   /* Run the engine and thus create the output file, handling
813      passphrase retrieval.  */
814   res = confucius_process (mode, infile, outfile);
815   if (res)
816     {
817       remove_file (outfile, mode == oDecrypt);
818       if (infile_from_stdin)
819         remove_file (infile, mode == oEncrypt);
820       free (outfile);
821       free (infile);
822       rmdir (tmpdir);
823       return res;
824     }
825
826   /* Dump the output file to stdout.  */
827   res = confucius_copy_file (outfile, "-", mode == oDecrypt);
828   if (res)
829     {
830       remove_file (outfile, mode == oDecrypt);
831       if (infile_from_stdin)
832         remove_file (infile, mode == oEncrypt);
833       free (outfile);
834       free (infile);
835       rmdir (tmpdir);
836       return res;
837     }
838   
839   remove_file (outfile, mode == oDecrypt);
840   if (infile_from_stdin)
841     remove_file (infile, mode == oEncrypt);
842   free (outfile);
843   free (infile);
844   rmdir (tmpdir);
845   return 0;
846 }
847
848 \f
849 /* symcryptrun's entry point.  */
850 int
851 main (int argc, char **argv)
852 {
853   ARGPARSE_ARGS pargs;
854   int orig_argc;
855   char **orig_argv;
856   FILE *configfp = NULL;
857   char *configname = NULL;
858   unsigned configlineno; 
859   int mode = 0;
860   int res;
861   char *logfile = NULL;
862   int default_config = 1;
863
864   set_strusage (my_strusage);
865   log_set_prefix ("symcryptrun", 1);
866
867   /* Try to auto set the character set.  */
868   set_native_charset (NULL); 
869
870   i18n_init();
871
872   opt.homedir = default_homedir ();
873
874   /* Check whether we have a config file given on the commandline */
875   orig_argc = argc;
876   orig_argv = argv;
877   pargs.argc = &argc;
878   pargs.argv = &argv;
879   pargs.flags= 1|(1<<6);  /* do not remove the args, ignore version */
880   while (arg_parse( &pargs, opts))
881     {
882       if (pargs.r_opt == oOptions)
883         { /* Yes there is one, so we do not try the default one, but
884              read the option file when it is encountered at the
885              commandline */
886           default_config = 0;
887         }
888       else if (pargs.r_opt == oNoOptions)
889         default_config = 0; /* --no-options */
890       else if (pargs.r_opt == oHomedir)
891         opt.homedir = pargs.r.ret_str;
892     }
893
894   if (default_config)
895     configname = make_filename (opt.homedir, "symcryptrun.conf", NULL );
896   
897   argc = orig_argc;
898   argv = orig_argv;
899   pargs.argc = &argc;
900   pargs.argv = &argv;
901   pargs.flags= 1;  /* do not remove the args */
902  next_pass:
903   if (configname)
904     {
905       configlineno = 0;
906       configfp = fopen (configname, "r");
907       if (!configfp)
908         {
909           if (!default_config)
910             {
911               log_error (_("option file `%s': %s\n"),
912                          configname, strerror(errno) );
913               exit(1);
914             }
915           xfree (configname); 
916           configname = NULL;
917         }
918       default_config = 0;
919     }
920
921   /* Parse the command line. */
922   while (optfile_parse (configfp, configname, &configlineno, &pargs, opts))
923     {
924       switch (pargs.r_opt)
925         {
926         case oDecrypt:   mode = oDecrypt; break;
927         case oEncrypt:   mode = oEncrypt; break;
928
929         case oQuiet:     opt.quiet = 1; break;
930         case oVerbose:   opt.verbose++; break;
931         case oNoVerbose: opt.verbose = 0; break;
932           
933         case oClass:    opt.class = pargs.r.ret_str; break;
934         case oProgram:  opt.program = pargs.r.ret_str; break;
935         case oKeyfile:  opt.keyfile = pargs.r.ret_str; break;
936         case oInput:    opt.input = pargs.r.ret_str; break;
937
938         case oLogFile:  logfile = pargs.r.ret_str; break;
939
940         case oOptions:
941           /* Config files may not be nested (silently ignore them) */
942           if (!configfp)
943             {
944                 xfree(configname);
945                 configname = xstrdup(pargs.r.ret_str);
946                 goto next_pass;
947             }
948           break;
949         case oNoOptions: break; /* no-options */
950         case oHomedir: /* Ignore this option here. */; break;
951
952         default : pargs.err = configfp? 1:2; break;
953         }
954     }
955   if (configfp)
956     {
957       fclose( configfp );
958       configfp = NULL;
959       configname = NULL;
960       goto next_pass;
961     }
962   xfree (configname);
963   configname = NULL;
964
965   /* With --inputfile an argument is not allowed, without only one
966      optional argument is allowed. */
967   if (argc > 1)
968     log_error (_("too many arguments\n"));
969   else if (opt.input && argc)
970     log_error (_("no argument allowed when using option \"%s\"\n"),
971                "--inputfile");
972
973   if (argc)
974     {
975       opt.input = *argv;
976       argv++; argc--;
977     }
978
979   if (!mode)
980     log_error (_("either %s or %s must be given\n"),
981                "--decrypt", "--encrypt");
982
983   if (log_get_errorcount (0))
984     exit (1);
985
986   if (logfile)
987     log_set_file (logfile);
988
989   gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
990   if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
991     {
992       log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
993                  NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
994     }
995   gcry_set_log_handler (my_gcry_logger, NULL);
996   gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
997
998   if (!opt.class)
999     {
1000       log_error (_("no class provided\n"));
1001       res = 1;
1002     }
1003   else if (!strcmp (opt.class, "confucius"))
1004     res = confucius_main (mode);
1005   else
1006     {
1007       log_error (_("class %s is not supported\n"), opt.class);
1008       res = 1;
1009     }
1010
1011   return res;
1012 }