1 /* symcryptrun.c - Tool to call simple symmetric encryption tools.
2 * Copyright (C) 2005 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
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.
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.
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
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. */
32 /* This program is invoked in the following way:
34 symcryptrun --class CLASS --program PROGRAM --keyfile KEYFILE \
35 [--decrypt | --encrypt]
37 For encryption, the plain text must be provided on STDIN, and the
38 ciphertext will be output to STDOUT. For decryption vice versa.
40 CLASS can currently only be "confucius".
42 PROGRAM must be the path to the crypto engine.
44 KEYFILE must contain the secret key, which may be protected by a
45 passphrase. The passphrase is retrieved via the pinentry program.
48 The GPG Agent _must_ be running before starting symcryptrun.
50 The possible exit status codes:
54 2 No valid passphrase was provided
55 3 The operation was canceled by the user
57 Other classes may be added in the future. */
59 #define SYMC_BAD_PASSPHRASE 2
60 #define SYMC_CANCELED 3
72 #include <sys/types.h>
80 #ifdef HAVE_LANGINFO_CODESET
83 #include <gpg-error.h>
85 #define JNLIB_NEED_LOG_LOGV
87 #include "../common/util.h"
90 /* FIXME: Bah. For spwq_secure_free. */
91 #define SIMPLE_PWQUERY_IMPLEMENTATION 1
92 #include "../common/simple-pwquery.h"
95 /* Used by gcry for logging */
97 my_gcry_logger (void *dummy
, int level
, const char *fmt
, va_list arg_ptr
)
99 /* translate the log levels */
102 case GCRY_LOG_CONT
: level
= JNLIB_LOG_CONT
; break;
103 case GCRY_LOG_INFO
: level
= JNLIB_LOG_INFO
; break;
104 case GCRY_LOG_WARN
: level
= JNLIB_LOG_WARN
; break;
105 case GCRY_LOG_ERROR
:level
= JNLIB_LOG_ERROR
; break;
106 case GCRY_LOG_FATAL
:level
= JNLIB_LOG_FATAL
; break;
107 case GCRY_LOG_BUG
: level
= JNLIB_LOG_BUG
; break;
108 case GCRY_LOG_DEBUG
:level
= JNLIB_LOG_DEBUG
; break;
109 default: level
= JNLIB_LOG_ERROR
; break; }
110 log_logv (level
, fmt
, arg_ptr
);
114 /* From simple-gettext.c. */
116 /* We assume to have `unsigned long int' value with at least 32 bits. */
117 #define HASHWORDBITS 32
119 /* The so called `hashpjw' function by P.J. Weinberger
120 [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
121 1986, 1987 Bell Telephone Laboratories, Inc.] */
123 static __inline__ ulong
124 hash_string( const char *str_param
)
126 unsigned long int hval
, g
;
127 const char *str
= str_param
;
133 hval
+= (unsigned long int) *str
++;
134 g
= hval
& ((unsigned long int) 0xf << (HASHWORDBITS
- 4));
137 hval
^= g
>> (HASHWORDBITS
- 8);
145 /* Constants to identify the commands and options. */
146 enum cmd_and_opt_values
166 /* The list of commands and options. */
167 static ARGPARSE_OPTS opts
[] =
169 { 301, NULL
, 0, N_("@\nCommands:\n ") },
171 { oDecrypt
, "decrypt", 0, N_("decryption modus") },
172 { oEncrypt
, "encrypt", 0, N_("encryption modus") },
174 { 302, NULL
, 0, N_("@\nOptions:\n ") },
176 { oClass
, "class", 2, N_("tool class (confucius)") },
177 { oProgram
, "program", 2, N_("program filename") },
179 { oKeyfile
, "keyfile", 2, N_("secret key file (required)") },
180 { oInput
, "inputfile", 2, N_("input file name (default stdin)") },
181 { oVerbose
, "verbose", 0, N_("verbose") },
182 { oQuiet
, "quiet", 0, N_("quiet") },
183 { oLogFile
, "log-file", 2, N_("use a log file for the server") },
184 { oOptions
, "options" , 2, N_("|FILE|read options from FILE") },
186 /* Hidden options. */
187 { oNoVerbose
, "no-verbose", 0, "@" },
188 { oHomedir
, "homedir", 2, "@" },
189 { oNoOptions
, "no-options", 0, "@" },/* shortcut for --options /dev/null */
195 /* We keep all global options in the structure OPT. */
198 int verbose
; /* Verbosity level. */
199 int quiet
; /* Be extra quiet. */
200 const char *homedir
; /* Configuration directory name */
209 /* Print usage information and and provide strings for help. */
211 my_strusage (int level
)
217 case 11: p
= "symcryptrun (GnuPG)";
219 case 13: p
= VERSION
; break;
220 case 17: p
= PRINTABLE_OS_NAME
; break;
221 case 19: p
= _("Please report bugs to <" PACKAGE_BUGREPORT
">.\n");
224 case 40: p
= _("Usage: symcryptrun [options] (-h for help)");
227 p
= _("Syntax: symcryptrun --class CLASS --program PROGRAM "
228 "--keyfile KEYFILE [options...] COMMAND [inputfile]\n"
229 "Call a simple symmetric encryption tool\n");
231 case 31: p
= "\nHome: "; break;
232 case 32: p
= opt
.homedir
; break;
233 case 33: p
= "\n"; break;
235 default: p
= NULL
; break;
241 /* Initialize the gettext system. */
245 #ifdef USE_SIMPLE_GETTEXT
246 set_gettext_file (PACKAGE_GT
);
249 setlocale (LC_ALL
, "");
250 bindtextdomain (PACKAGE_GT
, LOCALEDIR
);
251 textdomain (PACKAGE_GT
);
257 /* This is in the GNU C library in unistd.h. */
259 #ifndef TEMP_FAILURE_RETRY
260 /* Evaluate EXPRESSION, and repeat as long as it returns -1 with `errno'
263 # define TEMP_FAILURE_RETRY(expression) \
265 ({ long int __result; \
266 do __result = (long int) (expression); \
267 while (__result == -1L && errno == EINTR); \
272 /* Unlink a file, and shred it if SHRED is true. */
274 remove_file (char *name
, int shred
)
277 return unlink (name
);
288 /* -f forces file to be writable, and -u unlinks it afterwards. */
289 char *args
[] = { SHRED
, "-uf", name
, NULL
};
303 if (TEMP_FAILURE_RETRY (waitpid (pid
, &status
, 0)) != pid
)
307 if (!WIFEXITED (status
))
309 log_error (_("%s on %s aborted with status %i\n"),
310 SHRED
, name
, status
);
314 else if (WEXITSTATUS (status
))
316 log_error (_("%s on %s failed with status %i\n"), SHRED
, name
,
317 WEXITSTATUS (status
));
329 "Don't worry that other people don't know you;
330 worry that you don't know other people." Analects--1.16. */
332 /* Create temporary directory with mode 0700. Returns a dynamically
333 allocated string with the filename of the directory. */
335 confucius_mktmpdir (void)
339 name
= strdup ("/tmp/gpg-XXXXXX");
340 if (!name
|| !mkdtemp (name
))
342 log_error (_("can't create temporary directory `%s': %s\n"),
343 name
?name
:"", strerror (errno
));
351 /* Buffer size for I/O operations. */
352 #define CONFUCIUS_BUFSIZE 4096
354 /* Buffer size for output lines. */
355 #define CONFUCIUS_LINESIZE 4096
358 /* Copy the file IN to OUT, either of which may be "-". If PLAIN is
359 true, and the copying fails, and OUT is not STDOUT, then shred the
360 file instead unlinking it. */
362 confucius_copy_file (char *infile
, char *outfile
, int plain
)
367 int out_is_stdout
= 0;
368 char data
[CONFUCIUS_BUFSIZE
];
371 if (infile
[0] == '-' && infile
[1] == '\0')
373 /* FIXME: Is stdin in binary mode? */
379 in
= fopen (infile
, "rb");
382 log_error (_("could not open %s for writing: %s\n"),
383 infile
, strerror (errno
));
388 if (outfile
[0] == '-' && outfile
[1] == '\0')
390 /* FIXME: Is stdout in binary mode? */
396 out
= fopen (outfile
, "wb");
399 log_error (_("could not open %s for writing: %s\n"),
400 infile
, strerror (errno
));
405 /* Now copy the data. */
406 while ((data_len
= fread (data
, 1, sizeof (data
), in
)) > 0)
408 if (fwrite (data
, 1, data_len
, out
) != data_len
)
410 log_error (_("error writing to %s: %s\n"), outfile
,
415 if (data_len
< 0 || ferror (in
))
417 log_error (_("error reading from %s: %s\n"), infile
, strerror (errno
));
421 /* Close IN if appropriate. */
422 if (!in_is_stdin
&& fclose (in
) && ferror (in
))
424 log_error (_("error closing %s: %s\n"), infile
, strerror (errno
));
428 /* Close OUT if appropriate. */
429 if (!out_is_stdout
&& fclose (out
) && ferror (out
))
431 log_error (_("error closing %s: %s\n"), infile
, strerror (errno
));
439 remove_file (outfile
, plain
);
445 /* Get a passphrase in secure storage (if possible). If AGAIN is
446 true, then this is a repeated attempt. If CANCELED is not a null
447 pointer, it will be set to true or false, depending on if the user
448 canceled the operation or not. On error (including cancelation), a
449 null pointer is returned. The passphrase must be deallocated with
450 confucius_drop_pass. CACHEID is the ID to be used for passphrase
451 caching and can be NULL to disable caching. */
453 confucius_get_pass (const char *cacheid
, int again
, int *canceled
)
457 #ifdef HAVE_LANGINFO_CODESET
458 char *orig_codeset
= NULL
;
465 /* The Assuan agent protocol requires us to transmit utf-8 strings */
466 orig_codeset
= bind_textdomain_codeset (PACKAGE_GT
, NULL
);
467 #ifdef HAVE_LANGINFO_CODESET
469 orig_codeset
= nl_langinfo (CODESET
);
471 if (orig_codeset
&& !strcmp (orig_codeset
, "UTF-8"))
475 /* We only switch when we are able to restore the codeset later. */
476 orig_codeset
= xstrdup (orig_codeset
);
477 if (!bind_textdomain_codeset (PACKAGE_GT
, "utf-8"))
482 pw
= simple_pwquery (cacheid
,
483 again
? _("does not match - try again"):NULL
,
484 _("Passphrase:"), NULL
, &err
);
489 bind_textdomain_codeset (PACKAGE_GT
, orig_codeset
);
490 xfree (orig_codeset
);
497 log_error (_("error while asking for the passphrase: %s\n"),
501 log_info (_("cancelled\n"));
511 /* Drop a passphrase retrieved with confucius_get_pass. */
513 confucius_drop_pass (char *pass
)
516 spwq_secure_free (pass
);
520 /* Run a confucius crypto engine. If MODE is oEncrypt, encryption is
521 requested. If it is oDecrypt, decryption is requested. INFILE and
522 OUTFILE are the temporary files used in the process. */
524 confucius_process (int mode
, char *infile
, char *outfile
,
525 int argc
, char *argv
[])
537 signal (SIGPIPE
, SIG_IGN
);
541 log_error (_("no --program option provided\n"));
545 if (mode
!= oDecrypt
&& mode
!= oEncrypt
)
547 log_error (_("only --decrypt and --encrypt are supported\n"));
553 log_error (_("no --keyfile option provided\n"));
557 /* Generate a hash from the keyfile name for caching. */
558 snprintf (cacheid
, sizeof (cacheid
), "confucius:%lu",
559 hash_string (opt
.keyfile
));
560 cacheid
[sizeof (cacheid
) - 1] = '\0';
561 args
= malloc (sizeof (char *) * (10 + argc
));
564 log_error (_("cannot allocate args vector\n"));
567 args
[0] = opt
.program
;
568 args
[1] = (mode
== oEncrypt
) ? "-m1" : "-m2";
574 args
[7] = opt
.keyfile
;
575 args
[8] = (mode
== oEncrypt
) ? "-af" : "-f";
576 args
[9 + argc
] = NULL
;
578 args
[9 + argc
] = argv
[argc
];
580 if (pipe (cstderr
) < 0)
582 log_error (_("could not create pipe: %s\n"), strerror (errno
));
587 if (openpty (&master
, &slave
, NULL
, NULL
, NULL
) == -1)
589 log_error (_("could not create pty: %s\n"), strerror (errno
));
596 /* We don't want to deal with the worst case scenarios. */
599 assert (cstderr
[0] > 2);
600 assert (cstderr
[1] > 2);
605 log_error (_("could not fork: %s\n"), strerror (errno
));
617 /* Close the parent ends. */
621 /* Change controlling terminal. */
622 if (login_tty (slave
))
624 /* It's too early to output a debug message. */
628 dup2 (cstderr
[1], 2);
631 /* Now kick off the engine program. */
632 execv (opt
.program
, args
);
633 log_error (_("execv failed: %s\n"), strerror (errno
));
639 char buffer
[CONFUCIUS_LINESIZE
];
642 int slave_closed
= 0;
643 int stderr_closed
= 0;
649 /* Listen on the output FDs. */
655 FD_SET (master
, &fds
);
657 FD_SET (cstderr
[0], &fds
);
659 res
= select (FD_SETSIZE
, &fds
, NULL
, NULL
, NULL
);
662 log_error (_("select failed: %s\n"), strerror (errno
));
670 if (FD_ISSET (cstderr
[0], &fds
))
672 /* We got some output on stderr. This is just passed
673 through via the logging facility. */
675 res
= read (cstderr
[0], &buffer
[buffer_len
],
676 sizeof (buffer
) - buffer_len
- 1);
679 log_error (_("read failed: %s\n"), strerror (errno
));
693 buffer
[buffer_len
] = '\0';
694 newline
= strchr (buffer
, '\n');
698 log_error ("%s\n", buffer
);
699 buffer_len
-= newline
+ 1 - buffer
;
700 memmove (buffer
, newline
+ 1, buffer_len
);
702 else if (buffer_len
== sizeof (buffer
) - 1)
705 log_error ("%s\n", buffer
);
716 else if (FD_ISSET (master
, &fds
))
720 res
= read (master
, data
, sizeof (data
));
725 /* Slave-side close leads to readable fd and
731 log_error (_("pty read failed: %s\n"), strerror (errno
));
740 /* This never seems to be what happens on slave-side
745 /* Check for password prompt. */
746 if (data
[res
- 1] == ':')
751 /* If this is not the first attempt, the
752 passphrase seems to be wrong, so clear the
755 simple_pwclear (cacheid
);
757 pass
= confucius_get_pass (cacheid
,
758 tries
? 1 : 0, &canceled
);
764 return canceled
? SYMC_CANCELED
: 1;
766 write (master
, pass
, strlen (pass
));
767 write (master
, "\n", 1);
768 confucius_drop_pass (pass
);
775 while (!stderr_closed
|| !slave_closed
);
780 wpid
= waitpid (pid
, &res
, 0);
783 log_error (_("waitpid failed: %s\n"), strerror (errno
));
786 /* State of cached password is unclear. Just remove it. */
787 simple_pwclear (cacheid
);
792 /* Shouldn't happen, as we don't use WNOHANG. */
795 if (!WIFEXITED (res
))
797 log_error (_("child aborted with status %i\n"), res
);
799 /* State of cached password is unclear. Just remove it. */
800 simple_pwclear (cacheid
);
805 if (WEXITSTATUS (res
))
807 /* The passphrase was wrong. Remove it from the cache. */
808 simple_pwclear (cacheid
);
810 /* We probably exceeded our number of attempts at guessing
813 return SYMC_BAD_PASSPHRASE
;
826 /* Class confucius main program. If MODE is oEncrypt, encryption is
827 requested. If it is oDecrypt, decryption is requested. The other
828 parameters are taken from the global option data. */
830 confucius_main (int mode
, int argc
, char *argv
[])
835 int infile_from_stdin
= 0;
838 tmpdir
= confucius_mktmpdir ();
842 if (opt
.input
&& !(opt
.input
[0] == '-' && opt
.input
[1] == '\0'))
843 infile
= xstrdup (opt
.input
);
846 infile_from_stdin
= 1;
848 /* TMPDIR + "/" + "in" + "\0". */
849 infile
= malloc (strlen (tmpdir
) + 1 + 2 + 1);
852 log_error (_("cannot allocate infile string: %s\n"),
857 strcpy (infile
, tmpdir
);
858 strcat (infile
, "/in");
861 /* TMPDIR + "/" + "out" + "\0". */
862 outfile
= malloc (strlen (tmpdir
) + 1 + 3 + 1);
865 log_error (_("cannot allocate outfile string: %s\n"), strerror (errno
));
870 strcpy (outfile
, tmpdir
);
871 strcat (outfile
, "/out");
873 if (infile_from_stdin
)
875 /* Create INFILE and fill it with content. */
876 res
= confucius_copy_file ("-", infile
, mode
== oEncrypt
);
886 /* Run the engine and thus create the output file, handling
887 passphrase retrieval. */
888 res
= confucius_process (mode
, infile
, outfile
, argc
, argv
);
891 remove_file (outfile
, mode
== oDecrypt
);
892 if (infile_from_stdin
)
893 remove_file (infile
, mode
== oEncrypt
);
900 /* Dump the output file to stdout. */
901 res
= confucius_copy_file (outfile
, "-", mode
== oDecrypt
);
904 remove_file (outfile
, mode
== oDecrypt
);
905 if (infile_from_stdin
)
906 remove_file (infile
, mode
== oEncrypt
);
913 remove_file (outfile
, mode
== oDecrypt
);
914 if (infile_from_stdin
)
915 remove_file (infile
, mode
== oEncrypt
);
923 /* symcryptrun's entry point. */
925 main (int argc
, char **argv
)
930 FILE *configfp
= NULL
;
931 char *configname
= NULL
;
932 unsigned configlineno
;
935 char *logfile
= NULL
;
936 int default_config
= 1;
938 set_strusage (my_strusage
);
939 log_set_prefix ("symcryptrun", 1);
941 /* Try to auto set the character set. */
942 set_native_charset (NULL
);
946 opt
.homedir
= default_homedir ();
948 /* Check whether we have a config file given on the commandline */
953 pargs
.flags
= 1|(1<<6); /* do not remove the args, ignore version */
954 while (arg_parse( &pargs
, opts
))
956 if (pargs
.r_opt
== oOptions
)
957 { /* Yes there is one, so we do not try the default one, but
958 read the option file when it is encountered at the
962 else if (pargs
.r_opt
== oNoOptions
)
963 default_config
= 0; /* --no-options */
964 else if (pargs
.r_opt
== oHomedir
)
965 opt
.homedir
= pargs
.r
.ret_str
;
969 configname
= make_filename (opt
.homedir
, "symcryptrun.conf", NULL
);
975 pargs
.flags
= 1; /* do not remove the args */
980 configfp
= fopen (configname
, "r");
985 log_error (_("option file `%s': %s\n"),
986 configname
, strerror(errno
) );
995 /* Parse the command line. */
996 while (optfile_parse (configfp
, configname
, &configlineno
, &pargs
, opts
))
1000 case oDecrypt
: mode
= oDecrypt
; break;
1001 case oEncrypt
: mode
= oEncrypt
; break;
1003 case oQuiet
: opt
.quiet
= 1; break;
1004 case oVerbose
: opt
.verbose
++; break;
1005 case oNoVerbose
: opt
.verbose
= 0; break;
1007 case oClass
: opt
.class = pargs
.r
.ret_str
; break;
1008 case oProgram
: opt
.program
= pargs
.r
.ret_str
; break;
1009 case oKeyfile
: opt
.keyfile
= pargs
.r
.ret_str
; break;
1010 case oInput
: opt
.input
= pargs
.r
.ret_str
; break;
1012 case oLogFile
: logfile
= pargs
.r
.ret_str
; break;
1015 /* Config files may not be nested (silently ignore them) */
1019 configname
= xstrdup(pargs
.r
.ret_str
);
1023 case oNoOptions
: break; /* no-options */
1024 case oHomedir
: /* Ignore this option here. */; break;
1026 default : pargs
.err
= configfp
? 1:2; break;
1040 log_error (_("either %s or %s must be given\n"),
1041 "--decrypt", "--encrypt");
1043 if (log_get_errorcount (0))
1047 log_set_file (logfile
);
1049 gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN
);
1050 if (!gcry_check_version (NEED_LIBGCRYPT_VERSION
) )
1052 log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
1053 NEED_LIBGCRYPT_VERSION
, gcry_check_version (NULL
) );
1055 gcry_set_log_handler (my_gcry_logger
, NULL
);
1056 gcry_control (GCRYCTL_INIT_SECMEM
, 16384, 0);
1060 log_error (_("no class provided\n"));
1063 else if (!strcmp (opt
.class, "confucius"))
1064 res
= confucius_main (mode
, argc
, argv
);
1067 log_error (_("class %s is not supported\n"), opt
.class);