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 <http://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>
82 #ifdef HAVE_LANGINFO_CODESET
85 #include <gpg-error.h>
87 #define JNLIB_NEED_LOG_LOGV
89 #include "../common/util.h"
92 /* FIXME: Bah. For spwq_secure_free. */
93 #define SIMPLE_PWQUERY_IMPLEMENTATION 1
94 #include "../common/simple-pwquery.h"
97 /* From simple-gettext.c. */
99 /* We assume to have `unsigned long int' value with at least 32 bits. */
100 #define HASHWORDBITS 32
102 /* The so called `hashpjw' function by P.J. Weinberger
103 [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
104 1986, 1987 Bell Telephone Laboratories, Inc.] */
106 static __inline__ ulong
107 hash_string( const char *str_param
)
109 unsigned long int hval
, g
;
110 const char *str
= str_param
;
116 hval
+= (unsigned long int) *str
++;
117 g
= hval
& ((unsigned long int) 0xf << (HASHWORDBITS
- 4));
120 hval
^= g
>> (HASHWORDBITS
- 8);
128 /* Constants to identify the commands and options. */
129 enum cmd_and_opt_values
149 /* The list of commands and options. */
150 static ARGPARSE_OPTS opts
[] =
152 { 301, NULL
, 0, N_("@\nCommands:\n ") },
154 { oDecrypt
, "decrypt", 0, N_("decryption modus") },
155 { oEncrypt
, "encrypt", 0, N_("encryption modus") },
157 { 302, NULL
, 0, N_("@\nOptions:\n ") },
159 { oClass
, "class", 2, N_("tool class (confucius)") },
160 { oProgram
, "program", 2, N_("program filename") },
162 { oKeyfile
, "keyfile", 2, N_("secret key file (required)") },
163 { oInput
, "inputfile", 2, N_("input file name (default stdin)") },
164 { oVerbose
, "verbose", 0, N_("verbose") },
165 { oQuiet
, "quiet", 0, N_("quiet") },
166 { oLogFile
, "log-file", 2, N_("use a log file for the server") },
167 { oOptions
, "options" , 2, N_("|FILE|read options from FILE") },
169 /* Hidden options. */
170 { oNoVerbose
, "no-verbose", 0, "@" },
171 { oHomedir
, "homedir", 2, "@" },
172 { oNoOptions
, "no-options", 0, "@" },/* shortcut for --options /dev/null */
178 /* We keep all global options in the structure OPT. */
181 int verbose
; /* Verbosity level. */
182 int quiet
; /* Be extra quiet. */
183 const char *homedir
; /* Configuration directory name */
192 /* Print usage information and and provide strings for help. */
194 my_strusage (int level
)
200 case 11: p
= "symcryptrun (GnuPG)";
202 case 13: p
= VERSION
; break;
203 case 17: p
= PRINTABLE_OS_NAME
; break;
204 case 19: p
= _("Please report bugs to <" PACKAGE_BUGREPORT
">.\n");
207 case 40: p
= _("Usage: symcryptrun [options] (-h for help)");
210 p
= _("Syntax: symcryptrun --class CLASS --program PROGRAM "
211 "--keyfile KEYFILE [options...] COMMAND [inputfile]\n"
212 "Call a simple symmetric encryption tool\n");
214 case 31: p
= "\nHome: "; break;
215 case 32: p
= opt
.homedir
; break;
216 case 33: p
= "\n"; break;
218 default: p
= NULL
; break;
225 /* This is in the GNU C library in unistd.h. */
227 #ifndef TEMP_FAILURE_RETRY
228 /* Evaluate EXPRESSION, and repeat as long as it returns -1 with `errno'
231 # define TEMP_FAILURE_RETRY(expression) \
233 ({ long int __result; \
234 do __result = (long int) (expression); \
235 while (__result == -1L && errno == EINTR); \
239 /* Include the implementation of map_spwq_error. */
242 /* Unlink a file, and shred it if SHRED is true. */
244 remove_file (char *name
, int shred
)
247 return unlink (name
);
258 /* -f forces file to be writable, and -u unlinks it afterwards. */
259 char *args
[] = { SHRED
, "-uf", name
, NULL
};
273 if (TEMP_FAILURE_RETRY (waitpid (pid
, &status
, 0)) != pid
)
277 if (!WIFEXITED (status
))
279 log_error (_("%s on %s aborted with status %i\n"),
280 SHRED
, name
, status
);
284 else if (WEXITSTATUS (status
))
286 log_error (_("%s on %s failed with status %i\n"), SHRED
, name
,
287 WEXITSTATUS (status
));
299 "Don't worry that other people don't know you;
300 worry that you don't know other people." Analects--1.16. */
302 /* Create temporary directory with mode 0700. Returns a dynamically
303 allocated string with the filename of the directory. */
305 confucius_mktmpdir (void)
309 name
= strdup ("/tmp/gpg-XXXXXX");
310 if (!name
|| !mkdtemp (name
))
312 log_error (_("can't create temporary directory `%s': %s\n"),
313 name
?name
:"", strerror (errno
));
321 /* Buffer size for I/O operations. */
322 #define CONFUCIUS_BUFSIZE 4096
324 /* Buffer size for output lines. */
325 #define CONFUCIUS_LINESIZE 4096
328 /* Copy the file IN to OUT, either of which may be "-". If PLAIN is
329 true, and the copying fails, and OUT is not STDOUT, then shred the
330 file instead unlinking it. */
332 confucius_copy_file (char *infile
, char *outfile
, int plain
)
337 int out_is_stdout
= 0;
338 char data
[CONFUCIUS_BUFSIZE
];
341 if (infile
[0] == '-' && infile
[1] == '\0')
343 /* FIXME: Is stdin in binary mode? */
349 in
= fopen (infile
, "rb");
352 log_error (_("could not open %s for writing: %s\n"),
353 infile
, strerror (errno
));
358 if (outfile
[0] == '-' && outfile
[1] == '\0')
360 /* FIXME: Is stdout in binary mode? */
366 out
= fopen (outfile
, "wb");
369 log_error (_("could not open %s for writing: %s\n"),
370 infile
, strerror (errno
));
375 /* Now copy the data. */
376 while ((data_len
= fread (data
, 1, sizeof (data
), in
)) > 0)
378 if (fwrite (data
, 1, data_len
, out
) != data_len
)
380 log_error (_("error writing to %s: %s\n"), outfile
,
385 if (data_len
< 0 || ferror (in
))
387 log_error (_("error reading from %s: %s\n"), infile
, strerror (errno
));
391 /* Close IN if appropriate. */
392 if (!in_is_stdin
&& fclose (in
) && ferror (in
))
394 log_error (_("error closing %s: %s\n"), infile
, strerror (errno
));
398 /* Close OUT if appropriate. */
399 if (!out_is_stdout
&& fclose (out
) && ferror (out
))
401 log_error (_("error closing %s: %s\n"), infile
, strerror (errno
));
409 remove_file (outfile
, plain
);
415 /* Get a passphrase in secure storage (if possible). If AGAIN is
416 true, then this is a repeated attempt. If CANCELED is not a null
417 pointer, it will be set to true or false, depending on if the user
418 canceled the operation or not. On error (including cancelation), a
419 null pointer is returned. The passphrase must be deallocated with
420 confucius_drop_pass. CACHEID is the ID to be used for passphrase
421 caching and can be NULL to disable caching. */
423 confucius_get_pass (const char *cacheid
, int again
, int *canceled
)
432 orig_codeset
= i18n_switchto_utf8 ();
433 pw
= simple_pwquery (cacheid
,
434 again
? _("does not match - try again"):NULL
,
435 _("Passphrase:"), NULL
, 0, &err
);
436 err
= map_spwq_error (err
);
437 i18n_switchback (orig_codeset
);
442 log_error (_("error while asking for the passphrase: %s\n"),
446 log_info (_("cancelled\n"));
456 /* Drop a passphrase retrieved with confucius_get_pass. */
458 confucius_drop_pass (char *pass
)
461 spwq_secure_free (pass
);
465 /* Run a confucius crypto engine. If MODE is oEncrypt, encryption is
466 requested. If it is oDecrypt, decryption is requested. INFILE and
467 OUTFILE are the temporary files used in the process. */
469 confucius_process (int mode
, char *infile
, char *outfile
,
470 int argc
, char *argv
[])
482 signal (SIGPIPE
, SIG_IGN
);
486 log_error (_("no --program option provided\n"));
490 if (mode
!= oDecrypt
&& mode
!= oEncrypt
)
492 log_error (_("only --decrypt and --encrypt are supported\n"));
498 log_error (_("no --keyfile option provided\n"));
502 /* Generate a hash from the keyfile name for caching. */
503 snprintf (cacheid
, sizeof (cacheid
), "confucius:%lu",
504 hash_string (opt
.keyfile
));
505 cacheid
[sizeof (cacheid
) - 1] = '\0';
506 args
= malloc (sizeof (char *) * (10 + argc
));
509 log_error (_("cannot allocate args vector\n"));
512 args
[0] = opt
.program
;
513 args
[1] = (mode
== oEncrypt
) ? "-m1" : "-m2";
519 args
[7] = opt
.keyfile
;
520 args
[8] = (mode
== oEncrypt
) ? "-af" : "-f";
521 args
[9 + argc
] = NULL
;
523 args
[9 + argc
] = argv
[argc
];
525 if (pipe (cstderr
) < 0)
527 log_error (_("could not create pipe: %s\n"), strerror (errno
));
532 if (openpty (&master
, &slave
, NULL
, NULL
, NULL
) == -1)
534 log_error (_("could not create pty: %s\n"), strerror (errno
));
541 /* We don't want to deal with the worst case scenarios. */
544 assert (cstderr
[0] > 2);
545 assert (cstderr
[1] > 2);
550 log_error (_("could not fork: %s\n"), strerror (errno
));
562 /* Close the parent ends. */
566 /* Change controlling terminal. */
567 if (login_tty (slave
))
569 /* It's too early to output a debug message. */
573 dup2 (cstderr
[1], 2);
576 /* Now kick off the engine program. */
577 execv (opt
.program
, args
);
578 log_error (_("execv failed: %s\n"), strerror (errno
));
584 char buffer
[CONFUCIUS_LINESIZE
];
587 int slave_closed
= 0;
588 int stderr_closed
= 0;
594 /* Listen on the output FDs. */
600 FD_SET (master
, &fds
);
602 FD_SET (cstderr
[0], &fds
);
604 res
= select (FD_SETSIZE
, &fds
, NULL
, NULL
, NULL
);
607 log_error (_("select failed: %s\n"), strerror (errno
));
615 if (FD_ISSET (cstderr
[0], &fds
))
617 /* We got some output on stderr. This is just passed
618 through via the logging facility. */
620 res
= read (cstderr
[0], &buffer
[buffer_len
],
621 sizeof (buffer
) - buffer_len
- 1);
624 log_error (_("read failed: %s\n"), strerror (errno
));
638 buffer
[buffer_len
] = '\0';
639 newline
= strchr (buffer
, '\n');
643 log_error ("%s\n", buffer
);
644 buffer_len
-= newline
+ 1 - buffer
;
645 memmove (buffer
, newline
+ 1, buffer_len
);
647 else if (buffer_len
== sizeof (buffer
) - 1)
650 log_error ("%s\n", buffer
);
661 else if (FD_ISSET (master
, &fds
))
665 res
= read (master
, data
, sizeof (data
));
670 /* Slave-side close leads to readable fd and
676 log_error (_("pty read failed: %s\n"), strerror (errno
));
685 /* This never seems to be what happens on slave-side
690 /* Check for password prompt. */
691 if (data
[res
- 1] == ':')
696 /* If this is not the first attempt, the
697 passphrase seems to be wrong, so clear the
700 simple_pwclear (cacheid
);
702 pass
= confucius_get_pass (cacheid
,
703 tries
? 1 : 0, &canceled
);
709 return canceled
? SYMC_CANCELED
: 1;
711 write (master
, pass
, strlen (pass
));
712 write (master
, "\n", 1);
713 confucius_drop_pass (pass
);
720 while (!stderr_closed
|| !slave_closed
);
725 wpid
= waitpid (pid
, &res
, 0);
728 log_error (_("waitpid failed: %s\n"), strerror (errno
));
731 /* State of cached password is unclear. Just remove it. */
732 simple_pwclear (cacheid
);
737 /* Shouldn't happen, as we don't use WNOHANG. */
740 if (!WIFEXITED (res
))
742 log_error (_("child aborted with status %i\n"), res
);
744 /* State of cached password is unclear. Just remove it. */
745 simple_pwclear (cacheid
);
750 if (WEXITSTATUS (res
))
752 /* The passphrase was wrong. Remove it from the cache. */
753 simple_pwclear (cacheid
);
755 /* We probably exceeded our number of attempts at guessing
758 return SYMC_BAD_PASSPHRASE
;
771 /* Class confucius main program. If MODE is oEncrypt, encryption is
772 requested. If it is oDecrypt, decryption is requested. The other
773 parameters are taken from the global option data. */
775 confucius_main (int mode
, int argc
, char *argv
[])
780 int infile_from_stdin
= 0;
783 tmpdir
= confucius_mktmpdir ();
787 if (opt
.input
&& !(opt
.input
[0] == '-' && opt
.input
[1] == '\0'))
788 infile
= xstrdup (opt
.input
);
791 infile_from_stdin
= 1;
793 /* TMPDIR + "/" + "in" + "\0". */
794 infile
= malloc (strlen (tmpdir
) + 1 + 2 + 1);
797 log_error (_("cannot allocate infile string: %s\n"),
802 strcpy (infile
, tmpdir
);
803 strcat (infile
, "/in");
806 /* TMPDIR + "/" + "out" + "\0". */
807 outfile
= malloc (strlen (tmpdir
) + 1 + 3 + 1);
810 log_error (_("cannot allocate outfile string: %s\n"), strerror (errno
));
815 strcpy (outfile
, tmpdir
);
816 strcat (outfile
, "/out");
818 if (infile_from_stdin
)
820 /* Create INFILE and fill it with content. */
821 res
= confucius_copy_file ("-", infile
, mode
== oEncrypt
);
831 /* Run the engine and thus create the output file, handling
832 passphrase retrieval. */
833 res
= confucius_process (mode
, infile
, outfile
, argc
, argv
);
836 remove_file (outfile
, mode
== oDecrypt
);
837 if (infile_from_stdin
)
838 remove_file (infile
, mode
== oEncrypt
);
845 /* Dump the output file to stdout. */
846 res
= confucius_copy_file (outfile
, "-", mode
== oDecrypt
);
849 remove_file (outfile
, mode
== oDecrypt
);
850 if (infile_from_stdin
)
851 remove_file (infile
, mode
== oEncrypt
);
858 remove_file (outfile
, mode
== oDecrypt
);
859 if (infile_from_stdin
)
860 remove_file (infile
, mode
== oEncrypt
);
868 /* symcryptrun's entry point. */
870 main (int argc
, char **argv
)
875 FILE *configfp
= NULL
;
876 char *configname
= NULL
;
877 unsigned configlineno
;
880 char *logfile
= NULL
;
881 int default_config
= 1;
883 set_strusage (my_strusage
);
884 log_set_prefix ("symcryptrun", 1);
886 /* Make sure that our subsystems are ready. */
888 init_common_subsystems ();
890 opt
.homedir
= default_homedir ();
892 /* Check whether we have a config file given on the commandline */
897 pargs
.flags
= 1|(1<<6); /* do not remove the args, ignore version */
898 while (arg_parse( &pargs
, opts
))
900 if (pargs
.r_opt
== oOptions
)
901 { /* Yes there is one, so we do not try the default one, but
902 read the option file when it is encountered at the
906 else if (pargs
.r_opt
== oNoOptions
)
907 default_config
= 0; /* --no-options */
908 else if (pargs
.r_opt
== oHomedir
)
909 opt
.homedir
= pargs
.r
.ret_str
;
913 configname
= make_filename (opt
.homedir
, "symcryptrun.conf", NULL
);
919 pargs
.flags
= 1; /* do not remove the args */
924 configfp
= fopen (configname
, "r");
929 log_error (_("option file `%s': %s\n"),
930 configname
, strerror(errno
) );
939 /* Parse the command line. */
940 while (optfile_parse (configfp
, configname
, &configlineno
, &pargs
, opts
))
944 case oDecrypt
: mode
= oDecrypt
; break;
945 case oEncrypt
: mode
= oEncrypt
; break;
947 case oQuiet
: opt
.quiet
= 1; break;
948 case oVerbose
: opt
.verbose
++; break;
949 case oNoVerbose
: opt
.verbose
= 0; break;
951 case oClass
: opt
.class = pargs
.r
.ret_str
; break;
952 case oProgram
: opt
.program
= pargs
.r
.ret_str
; break;
953 case oKeyfile
: opt
.keyfile
= pargs
.r
.ret_str
; break;
954 case oInput
: opt
.input
= pargs
.r
.ret_str
; break;
956 case oLogFile
: logfile
= pargs
.r
.ret_str
; break;
959 /* Config files may not be nested (silently ignore them) */
963 configname
= xstrdup(pargs
.r
.ret_str
);
967 case oNoOptions
: break; /* no-options */
968 case oHomedir
: /* Ignore this option here. */; break;
970 default : pargs
.err
= configfp
? 1:2; break;
984 log_error (_("either %s or %s must be given\n"),
985 "--decrypt", "--encrypt");
987 if (log_get_errorcount (0))
991 log_set_file (logfile
);
993 gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN
);
994 if (!gcry_check_version (NEED_LIBGCRYPT_VERSION
) )
996 log_fatal (_("%s is too old (need %s, have %s)\n"), "libgcrypt",
997 NEED_LIBGCRYPT_VERSION
, gcry_check_version (NULL
) );
999 setup_libgcrypt_logging ();
1000 gcry_control (GCRYCTL_INIT_SECMEM
, 16384, 0);
1002 /* Tell simple-pwquery about the the standard socket name. */
1004 char *tmp
= make_filename (opt
.homedir
, "S.gpg-agent", NULL
);
1005 simple_pw_set_socket (tmp
);
1011 log_error (_("no class provided\n"));
1014 else if (!strcmp (opt
.class, "confucius"))
1016 res
= confucius_main (mode
, argc
, argv
);
1020 log_error (_("class %s is not supported\n"), opt
.class);