1 /* symcryptrun.c - Tool to call simple symmetric encryption tools.
2 * Copyright (C) 2005, 2007 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 3 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, see <https://www.gnu.org/licenses/>.
21 /* Sometimes simple encryption tools are already in use for a long
22 time and there is a desire to integrate them into the GnuPG
23 framework. The protocols and encryption methods might be
24 non-standard or not even properly documented, so that a
25 full-fledged encryption tool with an interface like gpg is not
26 doable. This simple wrapper program provides a solution: It
27 operates by calling the encryption/decryption module and providing
28 the passphrase for a key (or even the key directly) using the
29 standard pinentry mechanism through gpg-agent. */
31 /* This program is invoked in the following way:
33 symcryptrun --class CLASS --program PROGRAM --keyfile KEYFILE \
34 [--decrypt | --encrypt]
36 For encryption, the plain text must be provided on STDIN, and the
37 ciphertext will be output to STDOUT. For decryption vice versa.
39 CLASS can currently only be "confucius".
41 PROGRAM must be the path to the crypto engine.
43 KEYFILE must contain the secret key, which may be protected by a
44 passphrase. The passphrase is retrieved via the pinentry program.
47 The GPG Agent _must_ be running before starting symcryptrun.
49 The possible exit status codes:
53 2 No valid passphrase was provided
54 3 The operation was canceled by the user
56 Other classes may be added in the future. */
58 #define SYMC_BAD_PASSPHRASE 2
59 #define SYMC_CANCELED 3
72 #include <sys/types.h>
84 #ifdef HAVE_LANGINFO_CODESET
87 #include <gpg-error.h>
90 #include "../common/util.h"
91 #include "../common/init.h"
92 #include "../common/sysutils.h"
94 /* FIXME: Bah. For spwq_secure_free. */
95 #define SIMPLE_PWQUERY_IMPLEMENTATION 1
96 #include "../common/simple-pwquery.h"
99 /* From simple-gettext.c. */
101 /* We assume to have 'unsigned long int' value with at least 32 bits. */
102 #define HASHWORDBITS 32
104 /* The so called 'hashpjw' function by P.J. Weinberger
105 [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
106 1986, 1987 Bell Telephone Laboratories, Inc.] */
108 static __inline__ ulong
109 hash_string( const char *str_param )
111 unsigned long int hval, g;
112 const char *str = str_param;
118 hval += (unsigned long int) *str++;
119 g = hval & ((unsigned long int) 0xf << (HASHWORDBITS - 4));
122 hval ^= g >> (HASHWORDBITS - 8);
130 /* Constants to identify the commands and options. */
131 enum cmd_and_opt_values
151 /* The list of commands and options. */
152 static ARGPARSE_OPTS opts[] =
154 { 301, NULL, 0, N_("@\nCommands:\n ") },
156 { oDecrypt, "decrypt", 0, N_("decryption modus") },
157 { oEncrypt, "encrypt", 0, N_("encryption modus") },
159 { 302, NULL, 0, N_("@\nOptions:\n ") },
161 { oClass, "class", 2, N_("tool class (confucius)") },
162 { oProgram, "program", 2, N_("program filename") },
164 { oKeyfile, "keyfile", 2, N_("secret key file (required)") },
165 { oInput, "inputfile", 2, N_("input file name (default stdin)") },
166 { oVerbose, "verbose", 0, N_("verbose") },
167 { oQuiet, "quiet", 0, N_("quiet") },
168 { oLogFile, "log-file", 2, N_("use a log file for the server") },
169 { oOptions, "options" , 2, N_("|FILE|read options from FILE") },
171 /* Hidden options. */
172 { oNoVerbose, "no-verbose", 0, "@" },
173 { oHomedir, "homedir", 2, "@" },
174 { oNoOptions, "no-options", 0, "@" },/* shortcut for --options /dev/null */
180 /* We keep all global options in the structure OPT. */
183 int verbose; /* Verbosity level. */
184 int quiet; /* Be extra quiet. */
185 const char *homedir; /* Configuration directory name */
194 /* Print usage information and and provide strings for help. */
196 my_strusage (int level)
202 case 11: p = "symcryptrun (@GNUPG@)";
204 case 13: p = VERSION; break;
205 case 17: p = PRINTABLE_OS_NAME; break;
206 case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
209 case 40: p = _("Usage: symcryptrun [options] (-h for help)");
212 p = _("Syntax: symcryptrun --class CLASS --program PROGRAM "
213 "--keyfile KEYFILE [options...] COMMAND [inputfile]\n"
214 "Call a simple symmetric encryption tool\n");
216 case 31: p = "\nHome: "; break;
217 case 32: p = gnupg_homedir (); break;
218 case 33: p = "\n"; break;
220 default: p = NULL; break;
227 /* This is in the GNU C library in unistd.h. */
229 #ifndef TEMP_FAILURE_RETRY
230 /* Evaluate EXPRESSION, and repeat as long as it returns -1 with 'errno'
233 # define TEMP_FAILURE_RETRY(expression) \
235 ({ long int __result; \
236 do __result = (long int) (expression); \
237 while (__result == -1L && errno == EINTR); \
241 /* Unlink a file, and shred it if SHRED is true. */
243 remove_file (char *name, int shred)
246 return unlink (name);
257 /* -f forces file to be writable, and -u unlinks it afterwards. */
258 char *args[] = { SHRED, "-uf", name, NULL };
272 if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid)
276 if (!WIFEXITED (status))
278 log_error (_("%s on %s aborted with status %i\n"),
279 SHRED, name, status);
283 else if (WEXITSTATUS (status))
285 log_error (_("%s on %s failed with status %i\n"), SHRED, name,
286 WEXITSTATUS (status));
298 "Don't worry that other people don't know you;
299 worry that you don't know other people." Analects--1.16. */
301 /* Create temporary directory with mode 0700. Returns a dynamically
302 allocated string with the filename of the directory. */
304 confucius_mktmpdir (void)
308 p = getenv ("TMPDIR");
311 if (p[strlen (p) - 1] == '/')
312 name = xstrconcat (p, "gpg-XXXXXX", NULL);
314 name = xstrconcat (p, "/", "gpg-XXXXXX", NULL);
315 if (!name || !gnupg_mkdtemp (name))
317 log_error (_("can't create temporary directory '%s': %s\n"),
318 name?name:"", strerror (errno));
326 /* Buffer size for I/O operations. */
327 #define CONFUCIUS_BUFSIZE 4096
329 /* Buffer size for output lines. */
330 #define CONFUCIUS_LINESIZE 4096
333 /* Copy the file IN to OUT, either of which may be "-". If PLAIN is
334 true, and the copying fails, and OUT is not STDOUT, then shred the
335 file instead unlinking it. */
337 confucius_copy_file (char *infile, char *outfile, int plain)
342 int out_is_stdout = 0;
343 char data[CONFUCIUS_BUFSIZE];
346 if (infile[0] == '-' && infile[1] == '\0')
348 /* FIXME: Is stdin in binary mode? */
354 in = fopen (infile, "rb");
357 log_error (_("could not open %s for writing: %s\n"),
358 infile, strerror (errno));
363 if (outfile[0] == '-' && outfile[1] == '\0')
365 /* FIXME: Is stdout in binary mode? */
371 out = fopen (outfile, "wb");
374 log_error (_("could not open %s for writing: %s\n"),
375 infile, strerror (errno));
380 /* Now copy the data. */
381 while ((data_len = fread (data, 1, sizeof (data), in)) > 0)
383 if (fwrite (data, 1, data_len, out) != data_len)
385 log_error (_("error writing to %s: %s\n"), outfile,
390 if (data_len < 0 || ferror (in))
392 log_error (_("error reading from %s: %s\n"), infile, strerror (errno));
396 /* Close IN if appropriate. */
397 if (!in_is_stdin && fclose (in) && ferror (in))
399 log_error (_("error closing %s: %s\n"), infile, strerror (errno));
403 /* Close OUT if appropriate. */
404 if (!out_is_stdout && fclose (out) && ferror (out))
406 log_error (_("error closing %s: %s\n"), infile, strerror (errno));
414 remove_file (outfile, plain);
420 /* Get a passphrase in secure storage (if possible). If AGAIN is
421 true, then this is a repeated attempt. If CANCELED is not a null
422 pointer, it will be set to true or false, depending on if the user
423 canceled the operation or not. On error (including cancelation), a
424 null pointer is returned. The passphrase must be deallocated with
425 confucius_drop_pass. CACHEID is the ID to be used for passphrase
426 caching and can be NULL to disable caching. */
428 confucius_get_pass (const char *cacheid, int again, int *canceled)
437 orig_codeset = i18n_switchto_utf8 ();
438 pw = simple_pwquery (cacheid,
439 again ? _("does not match - try again"):NULL,
440 _("Passphrase:"), NULL, 0, &err);
441 i18n_switchback (orig_codeset);
446 log_error (_("error while asking for the passphrase: %s\n"),
450 log_info (_("cancelled\n"));
460 /* Drop a passphrase retrieved with confucius_get_pass. */
462 confucius_drop_pass (char *pass)
465 spwq_secure_free (pass);
469 /* Run a confucius crypto engine. If MODE is oEncrypt, encryption is
470 requested. If it is oDecrypt, decryption is requested. INFILE and
471 OUTFILE are the temporary files used in the process. */
473 confucius_process (int mode, char *infile, char *outfile,
474 int argc, char *argv[])
486 signal (SIGPIPE, SIG_IGN);
490 log_error (_("no --program option provided\n"));
494 if (mode != oDecrypt && mode != oEncrypt)
496 log_error (_("only --decrypt and --encrypt are supported\n"));
502 log_error (_("no --keyfile option provided\n"));
506 /* Generate a hash from the keyfile name for caching. */
507 snprintf (cacheid, sizeof (cacheid), "confucius:%lu",
508 hash_string (opt.keyfile));
509 cacheid[sizeof (cacheid) - 1] = '\0';
510 args = malloc (sizeof (char *) * (10 + argc));
513 log_error (_("cannot allocate args vector\n"));
516 args[0] = opt.program;
517 args[1] = (mode == oEncrypt) ? "-m1" : "-m2";
523 args[7] = opt.keyfile;
524 args[8] = (mode == oEncrypt) ? "-af" : "-f";
525 args[9 + argc] = NULL;
527 args[9 + argc] = argv[argc];
529 if (pipe (cstderr) < 0)
531 log_error (_("could not create pipe: %s\n"), strerror (errno));
536 if (openpty (&master, &slave, NULL, NULL, NULL) == -1)
538 log_error (_("could not create pty: %s\n"), strerror (errno));
545 /* We don't want to deal with the worst case scenarios. */
548 assert (cstderr[0] > 2);
549 assert (cstderr[1] > 2);
554 log_error (_("could not fork: %s\n"), strerror (errno));
566 /* Close the parent ends. */
570 /* Change controlling terminal. */
571 if (login_tty (slave))
573 /* It's too early to output a debug message. */
577 dup2 (cstderr[1], 2);
580 /* Now kick off the engine program. */
581 execv (opt.program, args);
582 log_error (_("execv failed: %s\n"), strerror (errno));
588 char buffer[CONFUCIUS_LINESIZE];
591 int slave_closed = 0;
592 int stderr_closed = 0;
598 /* Listen on the output FDs. */
604 FD_SET (master, &fds);
606 FD_SET (cstderr[0], &fds);
608 res = select (FD_SETSIZE, &fds, NULL, NULL, NULL);
611 log_error (_("select failed: %s\n"), strerror (errno));
619 if (FD_ISSET (cstderr[0], &fds))
621 /* We got some output on stderr. This is just passed
622 through via the logging facility. */
624 res = read (cstderr[0], &buffer[buffer_len],
625 sizeof (buffer) - buffer_len - 1);
628 log_error (_("read failed: %s\n"), strerror (errno));
642 buffer[buffer_len] = '\0';
643 newline = strchr (buffer, '\n');
647 log_error ("%s\n", buffer);
648 buffer_len -= newline + 1 - buffer;
649 memmove (buffer, newline + 1, buffer_len);
651 else if (buffer_len == sizeof (buffer) - 1)
654 log_error ("%s\n", buffer);
665 else if (FD_ISSET (master, &fds))
669 res = read (master, data, sizeof (data));
674 /* Slave-side close leads to readable fd and
680 log_error (_("pty read failed: %s\n"), strerror (errno));
689 /* This never seems to be what happens on slave-side
694 /* Check for password prompt. */
695 if (data[res - 1] == ':')
700 /* If this is not the first attempt, the
701 passphrase seems to be wrong, so clear the
704 simple_pwclear (cacheid);
706 pass = confucius_get_pass (cacheid,
707 tries ? 1 : 0, &canceled);
713 return canceled ? SYMC_CANCELED : 1;
715 write (master, pass, strlen (pass));
716 write (master, "\n", 1);
717 confucius_drop_pass (pass);
724 while (!stderr_closed || !slave_closed);
729 wpid = waitpid (pid, &res, 0);
732 log_error (_("waitpid failed: %s\n"), strerror (errno));
735 /* State of cached password is unclear. Just remove it. */
736 simple_pwclear (cacheid);
741 /* Shouldn't happen, as we don't use WNOHANG. */
744 if (!WIFEXITED (res))
746 log_error (_("child aborted with status %i\n"), res);
748 /* State of cached password is unclear. Just remove it. */
749 simple_pwclear (cacheid);
754 if (WEXITSTATUS (res))
756 /* The passphrase was wrong. Remove it from the cache. */
757 simple_pwclear (cacheid);
759 /* We probably exceeded our number of attempts at guessing
762 return SYMC_BAD_PASSPHRASE;
775 /* Class confucius main program. If MODE is oEncrypt, encryption is
776 requested. If it is oDecrypt, decryption is requested. The other
777 parameters are taken from the global option data. */
779 confucius_main (int mode, int argc, char *argv[])
784 int infile_from_stdin = 0;
787 tmpdir = confucius_mktmpdir ();
791 if (opt.input && !(opt.input[0] == '-' && opt.input[1] == '\0'))
792 infile = xstrdup (opt.input);
795 infile_from_stdin = 1;
797 /* TMPDIR + "/" + "in" + "\0". */
798 infile = malloc (strlen (tmpdir) + 1 + 2 + 1);
801 log_error (_("cannot allocate infile string: %s\n"),
806 strcpy (infile, tmpdir);
807 strcat (infile, "/in");
810 /* TMPDIR + "/" + "out" + "\0". */
811 outfile = malloc (strlen (tmpdir) + 1 + 3 + 1);
814 log_error (_("cannot allocate outfile string: %s\n"), strerror (errno));
819 strcpy (outfile, tmpdir);
820 strcat (outfile, "/out");
822 if (infile_from_stdin)
824 /* Create INFILE and fill it with content. */
825 res = confucius_copy_file ("-", infile, mode == oEncrypt);
835 /* Run the engine and thus create the output file, handling
836 passphrase retrieval. */
837 res = confucius_process (mode, infile, outfile, argc, argv);
840 remove_file (outfile, mode == oDecrypt);
841 if (infile_from_stdin)
842 remove_file (infile, mode == oEncrypt);
849 /* Dump the output file to stdout. */
850 res = confucius_copy_file (outfile, "-", mode == oDecrypt);
853 remove_file (outfile, mode == oDecrypt);
854 if (infile_from_stdin)
855 remove_file (infile, mode == oEncrypt);
862 remove_file (outfile, mode == oDecrypt);
863 if (infile_from_stdin)
864 remove_file (infile, mode == oEncrypt);
872 /* symcryptrun's entry point. */
874 main (int argc, char **argv)
879 FILE *configfp = NULL;
880 char *configname = NULL;
881 unsigned configlineno;
884 char *logfile = NULL;
885 int default_config = 1;
887 early_system_init ();
888 set_strusage (my_strusage);
889 log_set_prefix ("symcryptrun", GPGRT_LOG_WITH_PREFIX);
891 /* Make sure that our subsystems are ready. */
893 init_common_subsystems (&argc, &argv);
895 /* Check whether we have a config file given on the commandline */
900 pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */
901 while (arg_parse( &pargs, opts))
903 if (pargs.r_opt == oOptions)
904 { /* Yes there is one, so we do not try the default one, but
905 read the option file when it is encountered at the
909 else if (pargs.r_opt == oNoOptions)
910 default_config = 0; /* --no-options */
911 else if (pargs.r_opt == oHomedir)
912 gnupg_set_homedir (pargs.r.ret_str);
916 configname = make_filename (gnupg_homedir (), "symcryptrun.conf", NULL );
922 pargs.flags= 1; /* do not remove the args */
927 configfp = fopen (configname, "r");
932 log_error (_("option file '%s': %s\n"),
933 configname, strerror(errno) );
942 /* Parse the command line. */
943 while (optfile_parse (configfp, configname, &configlineno, &pargs, opts))
947 case oDecrypt: mode = oDecrypt; break;
948 case oEncrypt: mode = oEncrypt; break;
950 case oQuiet: opt.quiet = 1; break;
951 case oVerbose: opt.verbose++; break;
952 case oNoVerbose: opt.verbose = 0; break;
954 case oClass: opt.class = pargs.r.ret_str; break;
955 case oProgram: opt.program = pargs.r.ret_str; break;
956 case oKeyfile: opt.keyfile = pargs.r.ret_str; break;
957 case oInput: opt.input = pargs.r.ret_str; break;
959 case oLogFile: logfile = pargs.r.ret_str; break;
962 /* Config files may not be nested (silently ignore them) */
966 configname = xstrdup(pargs.r.ret_str);
970 case oNoOptions: break; /* no-options */
971 case oHomedir: /* Ignore this option here. */; break;
973 default : pargs.err = configfp? 1:2; break;
987 log_error (_("either %s or %s must be given\n"),
988 "--decrypt", "--encrypt");
990 if (log_get_errorcount (0))
994 log_set_file (logfile);
996 gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
997 setup_libgcrypt_logging ();
998 gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
1000 /* Tell simple-pwquery about the the standard socket name. */
1002 char *tmp = make_filename (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL);
1003 simple_pw_set_socket (tmp);
1009 log_error (_("no class provided\n"));
1012 else if (!strcmp (opt.class, "confucius"))
1014 res = confucius_main (mode, argc, argv);
1018 log_error (_("class %s is not supported\n"), opt.class);