1
/****************************************************************************
2
****************************************************************************
6
****************************************************************************
7
****************************************************************************/
22
#include <sys/types.h>
29
#define PROGRAM_NAME "gpgwrap"
30
#define VERSION_STRING PROGRAM_NAME " " VERSION "-" VERSION_DATE
31
#define EXEC_ARGV_SIZE 1024
32
#define PASSPHRASE_BUFFER_SIZE 0x10000
33
#define LIST_BUFFER_SIZE 0x10000
34
#define CMDLINE_MAX_FILES 1024
35
#define GPGWRAP_MODE_DEFAULT 0
36
#define GPGWRAP_MODE_VERSION 1
37
#define GPGWRAP_MODE_FILE 2
38
#define GPGWRAP_MODE_PRINT 3
42
static char program_name[] = PROGRAM_NAME;
43
static char environ_name[] = "GPGWRAP_PASSPHRASE";
44
static int mode = GPGWRAP_MODE_DEFAULT;
45
static int verbose = 0;
46
static int interactive = 0;
47
static int ask_twice = 0;
48
static int check_exit_code = 0;
49
static char *calling_path = NULL;
50
static char *environ_var = NULL;
51
static char *passphrase_file = NULL;
52
static char *option_name = "--passphrase-fd";
53
static char *files[CMDLINE_MAX_FILES];
54
static int nfiles = 0;
55
static char **gpg_cmd = NULL;
59
/****************************************************************************
61
****************************************************************************/
73
/****************************************************************************
75
****************************************************************************/
76
#define do_error(args...) \
79
fprintf(stderr, "%s: ", program_name); \
80
fprintf(stderr, args); \
81
fprintf(stderr, "\n"); \
88
/****************************************************************************
90
****************************************************************************/
91
#define do_warning(args...) \
94
fprintf(stderr, "%s: ", program_name); \
95
fprintf(stderr, args); \
96
fprintf(stderr, "\n"); \
102
/****************************************************************************
104
****************************************************************************/
110
do_error("could not allocate memory");
115
/****************************************************************************
117
****************************************************************************/
123
do_error("passphrase too long");
128
/****************************************************************************
130
****************************************************************************/
131
#define do_verbose(level, args...) \
134
if (verbose < level) break; \
135
fprintf(stderr, "%s[%d]: ", program_name, getpid()); \
136
fprintf(stderr, args); \
137
fprintf(stderr, "\n"); \
143
/****************************************************************************
145
****************************************************************************/
146
#define do_verbose_start(level, args...) \
149
if (verbose < level) break; \
150
fprintf(stderr, "%s[%d] ", program_name, getpid()); \
151
fprintf(stderr, args); \
157
/****************************************************************************
159
****************************************************************************/
160
#define do_verbose_append(level, args...) \
163
if (verbose < level) break; \
164
fprintf(stderr, args); \
170
/****************************************************************************
172
****************************************************************************/
173
#define do_snprintf(string, max, args...) do_snprintf2(snprintf(string, max, args), max)
177
/****************************************************************************
179
****************************************************************************/
186
if ((len == -1) || (len >= max)) do_error("do_snprintf() size exceeded");
192
/****************************************************************************
194
****************************************************************************/
207
* look for "unusual" characters and convert them to
208
* backslash escaped octal numbers
211
for (i = j = 0, msize--; i < size; i++)
214
if (j >= msize) goto error;
215
if ((c < '+') || ((c > ';') && (c < 'A')) ||
216
((c > 'Z') && (c != '_') && (c < 'a')) ||
217
((c > 'z') && (c != '~')))
219
c1 = (unsigned char) c;
220
if (j >= msize - 4) goto error;
222
mbuffer[j++] = '0' + (c1 >> 6);
223
mbuffer[j++] = '0' + ((c1 >> 3) & 7);
224
mbuffer[j++] = '0' + (c1 & 7);
226
else mbuffer[j++] = c;
231
do_error("could not mangle passphrase");
236
/****************************************************************************
237
* unmangle_passphrase
238
****************************************************************************/
246
int i, j, c1, c2, c3;
248
/* replace backslash escaped octal numbers */
250
for (i = j = 0; j < size; i++)
255
if (j > size - 3) goto error;
259
if ((c1 < '0') || (c1 > '3') || (c2 < '0') || (c2 > '7') ||
260
(c3 < '0') || (c3 > '7')) goto error;
264
c = (char) (((c1 << 6) | (c2 << 3) | c3) & 0xff);
270
do_error("could not unmangle passphrase");
275
/****************************************************************************
277
****************************************************************************/
286
do_verbose(2, "reading passphrase from file '%s'", passphrase_file);
287
if (strcmp(passphrase_file, "-") == 0) fd = STDIN_FILENO;
288
else fd = open(passphrase_file, O_RDONLY);
289
if (fd == -1) do_perror();
290
for (len = 0; (i = read(fd, buffer, size)) > 0; len += i)
294
if (size == 0) do_error_too_long();
296
if (i == -1) do_perror();
297
if (close(fd) == -1) do_perror();
303
/****************************************************************************
305
****************************************************************************/
314
struct termios t, tt;
315
char tty[] = "/dev/tty";
316
char pp[] = "Passphrase: ";
317
char pp2[] = "\nPassphrase (again): ";
321
* don't touch stdin, just open the controlling tty and ask for the
325
do_verbose(2, "opening '%s' to prompt for passphrase", tty);
326
fd = open(tty, O_RDWR);
327
if (fd == -1) do_perror();
328
write(fd, pp, strlen(pp));
332
tcsetattr(fd, TCSAFLUSH, &tt);
333
len = read(fd, buffer, size);
334
if (len == -1) do_perror();
335
if ((ask_twice) && (len < size))
337
buffer2 = (char *) alloca(sizeof (char) * size);
338
if (buffer2 == NULL) do_error_oom();
339
write(fd, pp2, strlen(pp2));
340
len2 = read(fd, buffer2, size);
341
if (len2 == -1) do_perror();
343
tcsetattr(fd, TCSAFLUSH, &t);
344
if ((len != len2) || (memcmp(buffer, buffer2, len) != 0)) do_error("passphrases are not the same");
349
tcsetattr(fd, TCSAFLUSH, &t);
352
* if the above read() returns with len == size, we don't
353
* know if there are more bytes, so we assume passphrase is
357
if (len >= size) do_error_too_long();
359
if (close(fd) == -1) do_perror();
361
/* ignore trailing \012 */
368
/****************************************************************************
370
****************************************************************************/
380
env = getenv(environ_name);
381
if ((env != NULL) && (! interactive))
383
do_verbose(2, "got passphrase from environment variable: %s=%s", environ_name, env);
386
* first unmangle the content of the environment
387
* variable inplace, then clear the memory
391
len = unmangle_passphrase(env, len2);
392
if (len > size) do_error_too_long();
393
memcpy(buffer, env, len);
394
memset(env, 0, len2);
396
else len = prompt_passphrase(buffer, size);
402
/****************************************************************************
404
****************************************************************************/
410
int status, value = 1;
412
do_verbose(2, "waiting for child");
414
if (! check_exit_code) return;
415
do_verbose(2, "checking child exit code");
416
if (! WIFEXITED(status)) goto out;
417
value = WEXITSTATUS(status);
418
if (value == 0) return;
419
do_verbose(2, "child process terminated abnormal, exiting");
426
/****************************************************************************
428
****************************************************************************/
438
* parent will write passphrase to the opened pipe, child will
442
if (pipe(fds) == -1) do_perror();
443
do_verbose(2, "forking");
451
if (close(fds[1]) == -1) do_perror();
459
signal(SIGPIPE, SIG_IGN);
460
if (close(fds[0]) == -1) do_perror();
463
i = write(fds[1], buffer, size);
464
if ((i == -1) && (errno == EPIPE)) break;
465
if (i == -1) do_perror();
469
if (size > 0) do_warning("only partial passphrase written");
470
if (close(fds[1]) == -1) do_perror();
477
/****************************************************************************
479
****************************************************************************/
486
char buffer[PASSPHRASE_BUFFER_SIZE];
488
if ((passphrase_file == NULL) || (interactive))
490
len = environ_or_prompt(buffer, sizeof (buffer));
491
fd = do_fork(buffer, len);
493
else if (strcmp(passphrase_file, "-") == 0)
495
len = read_passphrase(buffer, sizeof (buffer));
496
fd = do_fork(buffer, len);
500
do_verbose(2, "opening file '%s' to pass fd", passphrase_file);
501
fd = open(passphrase_file, O_RDONLY);
502
if (fd == -1) do_perror();
509
/****************************************************************************
511
****************************************************************************/
520
if ((passphrase_file == NULL) || (interactive)) len = environ_or_prompt(buffer, size);
521
else len = read_passphrase(buffer, size);
527
/****************************************************************************
529
****************************************************************************/
540
* putenv() only stores the given pointer in **environ, so we have
544
size = strlen(environ_name) + (4 * len) + 2;
545
old_var = environ_var;
546
environ_var = (char *) malloc(sizeof (char) * size);
547
if (environ_var == NULL) do_error_oom();
548
len2 = do_snprintf(environ_var, size, "%s=", environ_name);
549
if ((buffer != NULL) && (len > 0)) mangle_passphrase(buffer, len, &environ_var[len2], size - len2);
550
do_verbose(2, "setting environment variable: %s", environ_var);
551
if (putenv(environ_var) == -1) do_perror();
552
if (old_var != NULL) free(old_var);
557
/****************************************************************************
559
****************************************************************************/
566
if (clear) do_putenv(NULL, 0);
571
do_verbose_start(1, "executing:");
572
for (i = 0; argv[i] != NULL; i++) do_verbose_append(1, " %s", argv[i]);
573
do_verbose_append(1, "\n");
575
execvp(argv[0], argv);
577
/* only reached if execvp fails */
584
/****************************************************************************
586
****************************************************************************/
595
char *argv[EXEC_ARGV_SIZE];
596
char homedir_eq[] = "--homedir=";
597
char options_eq[] = "--options=";
600
* get fd to read passphrase from, parent will return with fd == -1
604
fd = get_passphrase_fd();
605
if (fd == -1) return;
607
/* create argv for execvp */
609
do_snprintf(fd_num, sizeof (fd_num), "%d", fd);
610
for (i = 0, j = 0, k = 1; gpg_cmd[i] != NULL; i++, k--)
614
* check if there is enough space to store option_name
618
if (i >= (EXEC_ARGV_SIZE - 4)) do_error("too many gpg arguments specified");
619
if (strcmp(gpg_cmd[i], option_name) == 0) do_error("gpg command already has a '%s' option", option_name);
622
if ((strncmp(gpg_cmd[i], homedir_eq, sizeof (homedir_eq) - 1) == 0) || (strncmp(gpg_cmd[i], options_eq, sizeof (options_eq) - 1) == 0)) k = 1;
623
else if ((strcmp(gpg_cmd[i], "--homedir") == 0) || (strcmp(gpg_cmd[i], "--options") == 0)) k = 2;
626
argv[j++] = option_name;
630
argv[j++] = gpg_cmd[i];
634
argv[j++] = option_name;
643
/****************************************************************************
645
****************************************************************************/
651
char shell_cmd[LIST_BUFFER_SIZE];
652
char verbose_string[128] = "";
653
char *argv[] = { "sh", "-c", NULL, NULL };
656
/* fork a child and disallow it to read stdin from parent */
658
if (pipe(fds) == -1) do_perror();
659
do_verbose(1, "forking");
669
if (close(fds[0]) == -1) do_perror();
670
if (close(fds[1]) == -1) do_perror();
677
if (close(fds[1]) == -1) do_perror();
678
if (fds[0] != STDIN_FILENO) dup2(fds[0], STDIN_FILENO);
680
/* create argv for execvp */
682
for (i = 0; i < verbose; i++)
684
if (strlen(verbose_string) >= sizeof (verbose_string) - 4) break;
685
strcat(verbose_string, " -v");
687
do_snprintf(shell_cmd, sizeof (shell_cmd), "exec %s%s -o %s -- %s",
688
calling_path, verbose_string, option_name, line);
695
/****************************************************************************
697
****************************************************************************/
706
char lbuffer[LIST_BUFFER_SIZE];
707
int inuse, start, free, nread, llen;
708
char *line, *next_line;
712
do_verbose(1, "reading gpg commands from file: '%s'", path);
713
if (strcmp(path, "-") == 0) fd = STDIN_FILENO;
714
else fd = open(path, O_RDONLY);
715
if (fd == -1) do_perror();
717
/* export passphrase to environment */
719
do_putenv(buffer, len);
721
/* read gpg commands */
723
for (inuse = 0, free = LIST_BUFFER_SIZE; (nread = read(fd, &lbuffer[inuse], free)) > 0; )
726
for (line = lbuffer; (next_line = memchr(line, '\n', inuse)) != NULL; )
729
llen = (int) (next_line - line) + 1;
730
if (llen != strlen(line) + 1) do_error("line contains \\0 character");
733
line = next_line + 1;
735
start = (int) (line - lbuffer);
736
if ((start == 0) && (inuse == LIST_BUFFER_SIZE)) do_error("line too long");
737
if ((start > 0) && (inuse > 0)) memmove(lbuffer, &lbuffer[start], inuse);
738
free = LIST_BUFFER_SIZE - inuse;
741
/* check for error while read() */
743
if (nread == -1) do_perror();
744
if (close(fd) == -1) do_perror();
746
/* check if there are bytes left */
748
if (inuse > 0) do_error("last line incomplete");
753
/****************************************************************************
755
****************************************************************************/
761
while (*s != '\0') *s++ = ' ';
766
/****************************************************************************
768
****************************************************************************/
774
char space1[] = VERSION_STRING;
775
char space2[] = PROGRAM_NAME;
777
cmdline_fill_space(space1);
778
cmdline_fill_space(space2);
779
printf(VERSION_STRING " | written by Karsten Scheibler\n"
780
"%s | http://unusedino.de/gpgwrap/\n"
781
"%s | gpgwrap@unusedino.de\n\n"
783
"or: %s -P [-v] [-i] [-a] [-p <file>]\n"
784
"or: %s -F [-v] [-i] [-a] [-c] [-p <file>] [-o <name>]\n"
785
" %s [--] <file> [<file> ... ]\n"
786
"or: %s [-v] [-i] [-a] [-p <file>] [-o <name>]\n"
787
" %s [--] gpg [gpg options]\n\n"
788
" -V print out version\n"
789
" -P get the passphrase and print it mangled to stdout\n"
790
" -F read gpg commands from file\n"
791
" -v be more verbose\n"
792
" -i be interactive, always prompt for passphrase\n"
793
" -a ask twice if prompting for passphrase\n"
794
" -c check exit code of child processes\n"
795
" -p <file> read passphrase from <file>\n"
796
" -o <name> specify name of \"--passphrase-fd\" option\n"
798
space1, space1, program_name, program_name, program_name,
799
space2, program_name, space2);
805
/****************************************************************************
807
****************************************************************************/
814
if (file == NULL) do_error("%s expects a file name", msg);
820
/****************************************************************************
821
* cmdline_check_stdin
822
****************************************************************************/
829
static int stdin_count = 0;
831
cmdline_check_arg(msg, file);
832
if (strcmp(file, "-") == 0) stdin_count++;
833
if (stdin_count > 1) do_error("%s used stdin although already used before", msg);
839
/****************************************************************************
841
****************************************************************************/
852
calling_path = argv[0];
853
for (args = 0, argv++; (arg = *argv++) != NULL; args++)
855
if ((arg[0] != '-') || (ignore))
857
if (mode == GPGWRAP_MODE_FILE) goto get_file;
861
else if ((strcmp(arg, "-") == 0) && (mode == GPGWRAP_MODE_FILE))
864
if (nfiles >= CMDLINE_MAX_FILES) do_error("too many files specified");
865
files[nfiles++] = cmdline_check_stdin("-F/--file", arg);
867
else if (strcmp(arg, "--") == 0)
871
else if ((strcmp(arg, "-h") == 0) || (strcmp(arg, "--help") == 0))
875
else if (((strcmp(arg, "-V") == 0) || (strcmp(arg, "--version") == 0)) && (args == 0))
877
mode = GPGWRAP_MODE_VERSION;
879
else if (((strcmp(arg, "-F") == 0) || (strcmp(arg, "--file") == 0)) && (args == 0))
881
mode = GPGWRAP_MODE_FILE;
883
else if (((strcmp(arg, "-P") == 0) || (strcmp(arg, "--print") == 0)) && (args == 0))
885
mode = GPGWRAP_MODE_PRINT;
887
else if (mode == GPGWRAP_MODE_VERSION)
891
else if ((strcmp(arg, "-v") == 0) || (strcmp(arg, "--verbose") == 0))
895
else if ((strcmp(arg, "-i") == 0) || (strcmp(arg, "--interactive") == 0))
899
else if ((strcmp(arg, "-a") == 0) || (strcmp(arg, "--ask-twice") == 0))
903
else if ((strcmp(arg, "-p") == 0) || (strcmp(arg, "--passphrase-file") == 0))
905
if (passphrase_file != NULL) do_error("-p/--passphrase-file specified more than once");
906
passphrase_file = cmdline_check_stdin("-p/--passphrase-file", *argv++);
908
else if (mode == GPGWRAP_MODE_PRINT)
912
else if ((strcmp(arg, "-o") == 0) || (strcmp(arg, "--option-name") == 0))
914
option_name = cmdline_check_arg("-o/--option-name", *argv++);
916
else if (mode != GPGWRAP_MODE_FILE)
920
else if ((strcmp(arg, "-c") == 0) || (strcmp(arg, "--check-exit-code") == 0))
927
do_error("unrecognized option '%s'", arg);
930
if ((mode == GPGWRAP_MODE_DEFAULT) && (nfiles == 0) && (gpg_cmd == NULL)) do_error("no gpg command specified");
931
if ((mode == GPGWRAP_MODE_FILE) && (nfiles == 0)) do_error("no files to process");
932
if ((mode == GPGWRAP_MODE_PRINT) && (nfiles > 0)) do_error("no additional arguments allowed");
933
if (mode != GPGWRAP_MODE_FILE) check_exit_code = 1;
938
/****************************************************************************
940
****************************************************************************/
949
* we need setlinebuf(), because otherwise do_verbose() output of
950
* parent and child processes may get mixed in some cases
957
cmdline_parse(argc, argv);
961
if (mode == GPGWRAP_MODE_VERSION)
963
printf(VERSION_STRING "\n");
965
else if (mode == GPGWRAP_MODE_FILE)
968
char buffer[PASSPHRASE_BUFFER_SIZE];
970
len = get_passphrase(buffer, sizeof (buffer));
971
for (i = 0; i < nfiles; i++) exec_list(files[i], buffer, len);
973
else if (mode == GPGWRAP_MODE_PRINT)
975
char buffer[PASSPHRASE_BUFFER_SIZE];
976
char mbuffer[PASSPHRASE_BUFFER_SIZE];
979
len = get_passphrase(buffer, sizeof (buffer));
980
mangle_passphrase(buffer, len, mbuffer, sizeof (mbuffer));
981
printf("%s\n", mbuffer);
989
/******************************************************** Karsten Scheibler */