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