1 /* gpgtar.c - A simple TAR implementation mainly useful for Windows.
2 * Copyright (C) 2010 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 3 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, see <https://www.gnu.org/licenses/>.
18 * SPDX-License-Identifier: GPL-3.0-or-later
19 */
20
21 /* GnuPG comes with a shell script gpg-zip which creates archive files
22 in the same format as PGP Zip, which is actually a USTAR format.
23 That is fine and works nicely on all Unices but for Windows we
24 don't have a compatible shell and the supply of tar programs is
25 limited. Given that we need just a few tar option and it is an
26 open question how many Unix concepts are to be mapped to Windows,
27 we might as well write our own little tar customized for use with
28 gpg. So here we go. */
29
30 #include <config.h>
31
32 #include <ctype.h>
33 #include <errno.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <assert.h>
38
39 #define INCLUDED_BY_MAIN_MODULE 1
40 #include "../common/util.h"
41 #include "../common/i18n.h"
42 #include "../common/sysutils.h"
43 #include "../common/openpgpdefs.h"
44 #include "../common/init.h"
45 #include "../common/strlist.h"
46
47 #include "gpgtar.h"
48
49
50 /* Constants to identify the commands and options. */
51 enum cmd_and_opt_values
52 {
53 aNull = 0,
54 aCreate = 600,
55 aExtract,
56 aEncrypt = 'e',
57 aDecrypt = 'd',
58 aSign = 's',
59 aList = 't',
60
61 oSymmetric = 'c',
62 oRecipient = 'r',
63 oUser = 'u',
64 oOutput = 'o',
65 oDirectory = 'C',
66 oQuiet = 'q',
67 oVerbose = 'v',
68 oFilesFrom = 'T',
69 oNoVerbose = 500,
70
71 aSignEncrypt,
72 oGpgProgram,
73 oSkipCrypto,
74 oOpenPGP,
75 oCMS,
76 oSetFilename,
77 oNull,
78 oUtf8Strings,
79
80 /* Compatibility with gpg-zip. */
81 oGpgArgs,
82 oTarArgs,
83 oTarProgram,
84
85 /* Debugging. */
86 oDryRun,
87 };
88
89
90 /* The list of commands and options. */
91 static gpgrt_opt_t opts[] = {
92 ARGPARSE_group (300, N_("@Commands:\n ")),
93
94 ARGPARSE_c (aCreate, "create", N_("create an archive")),
95 ARGPARSE_c (aExtract, "extract", N_("extract an archive")),
96 ARGPARSE_c (aEncrypt, "encrypt", N_("create an encrypted archive")),
97 ARGPARSE_c (aDecrypt, "decrypt", N_("extract an encrypted archive")),
98 ARGPARSE_c (aSign, "sign", N_("create a signed archive")),
99 ARGPARSE_c (aList, "list-archive", N_("list an archive")),
100
101 ARGPARSE_group (301, N_("@\nOptions:\n ")),
102
103 ARGPARSE_s_n (oSymmetric, "symmetric", N_("use symmetric encryption")),
104 ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")),
105 ARGPARSE_s_s (oUser, "local-user",
106 N_("|USER-ID|use USER-ID to sign or decrypt")),
107 ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")),
108 ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
109 ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
110 ARGPARSE_s_s (oGpgProgram, "gpg", "@"),
111 ARGPARSE_s_n (oSkipCrypto, "skip-crypto", N_("skip the crypto processing")),
112 ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")),
113 ARGPARSE_s_s (oSetFilename, "set-filename", "@"),
114 ARGPARSE_s_n (oOpenPGP, "openpgp", "@"),
115 ARGPARSE_s_n (oCMS, "cms", "@"),
116
117 ARGPARSE_group (302, N_("@\nTar options:\n ")),
118
119 ARGPARSE_s_s (oDirectory, "directory",
120 N_("|DIRECTORY|change to DIRECTORY first")),
121 ARGPARSE_s_s (oFilesFrom, "files-from",
122 N_("|FILE|get names to create from FILE")),
123 ARGPARSE_s_n (oNull, "null", N_("-T reads null-terminated names")),
124 #ifdef HAVE_W32_SYSTEM
125 ARGPARSE_s_n (oUtf8Strings, "utf8-strings",
126 N_("-T reads UTF-8 encoded names")),
127 #else
128 ARGPARSE_s_n (oUtf8Strings, "utf8-strings", "@"),
129 #endif
130
131 ARGPARSE_s_s (oGpgArgs, "gpg-args", "@"),
132 ARGPARSE_s_s (oTarArgs, "tar-args", "@"),
133 ARGPARSE_s_s (oTarProgram, "tar", "@"),
134
135 ARGPARSE_end ()
136 };
137
138
139 /* The list of commands and options for tar that we understand. */
140 static gpgrt_opt_t tar_opts[] = {
141 ARGPARSE_s_s (oDirectory, "directory",
142 N_("|DIRECTORY|extract files into DIRECTORY")),
143 ARGPARSE_s_s (oFilesFrom, "files-from",
144 N_("|FILE|get names to create from FILE")),
145 ARGPARSE_s_n (oNull, "null", N_("-T reads null-terminated names")),
146
147 ARGPARSE_end ()
148 };
149
150
151 /* Global flags. */
152 static enum cmd_and_opt_values cmd = 0;
153 static int skip_crypto = 0;
154 static const char *files_from = NULL;
155 static int null_names = 0;
156
157
158
159
160 /* Print usage information and provide strings for help. */
161 static const char *
my_strusage(int level)162 my_strusage( int level )
163 {
164 const char *p;
165
166 switch (level)
167 {
168 case 9: p = "GPL-3.0-or-later"; break;
169 case 11: p = "@GPGTAR@ (@GNUPG@)";
170 break;
171 case 13: p = VERSION; break;
172 case 14: p = GNUPG_DEF_COPYRIGHT_LINE; break;
173 case 17: p = PRINTABLE_OS_NAME; break;
174 case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
175
176 case 1:
177 case 40:
178 p = _("Usage: gpgtar [options] [files] [directories] (-h for help)");
179 break;
180 case 41:
181 p = _("Syntax: gpgtar [options] [files] [directories]\n"
182 "Encrypt or sign files into an archive\n");
183 break;
184
185 default: p = NULL; break;
186 }
187 return p;
188 }
189
190
191 static void
set_cmd(enum cmd_and_opt_values * ret_cmd,enum cmd_and_opt_values new_cmd)192 set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd)
193 {
194 enum cmd_and_opt_values c = *ret_cmd;
195
196 if (!c || c == new_cmd)
197 c = new_cmd;
198 else if (c == aSign && new_cmd == aEncrypt)
199 c = aSignEncrypt;
200 else if (c == aEncrypt && new_cmd == aSign)
201 c = aSignEncrypt;
202 else
203 {
204 log_error (_("conflicting commands\n"));
205 exit (2);
206 }
207
208 *ret_cmd = c;
209 }
210
211
212
213 /* Shell-like argument splitting.
214
215 For compatibility with gpg-zip we accept arguments for GnuPG and
216 tar given as a string argument to '--gpg-args' and '--tar-args'.
217 gpg-zip was implemented as a Bourne Shell script, and therefore, we
218 need to split the string the same way the shell would. */
219 static int
shell_parse_stringlist(const char * str,strlist_t * r_list)220 shell_parse_stringlist (const char *str, strlist_t *r_list)
221 {
222 strlist_t list = NULL;
223 const char *s = str;
224 char quoted = 0;
225 char arg[1024];
226 char *p = arg;
227 #define addchar(c) \
228 do { if (p - arg + 2 < sizeof arg) *p++ = (c); else return 1; } while (0)
229 #define addargument() \
230 do { \
231 if (p > arg) \
232 { \
233 *p = 0; \
234 append_to_strlist (&list, arg); \
235 p = arg; \
236 } \
237 } while (0)
238
239 #define unquoted 0
240 #define singlequote '\''
241 #define doublequote '"'
242
243 for (; *s; s++)
244 {
245 switch (quoted)
246 {
247 case unquoted:
248 if (isspace (*s))
249 addargument ();
250 else if (*s == singlequote || *s == doublequote)
251 quoted = *s;
252 else
253 addchar (*s);
254 break;
255
256 case singlequote:
257 if (*s == singlequote)
258 quoted = unquoted;
259 else
260 addchar (*s);
261 break;
262
263 case doublequote:
264 assert (s > str || !"cannot be quoted at first char");
265 if (*s == doublequote && *(s - 1) != '\\')
266 quoted = unquoted;
267 else
268 addchar (*s);
269 break;
270
271 default:
272 assert (! "reached");
273 }
274 }
275
276 /* Append the last argument. */
277 addargument ();
278
279 #undef doublequote
280 #undef singlequote
281 #undef unquoted
282 #undef addargument
283 #undef addchar
284 *r_list = list;
285 return 0;
286 }
287
288
289 /* Like shell_parse_stringlist, but returns an argv vector
290 instead of a strlist. */
291 static int
shell_parse_argv(const char * s,int * r_argc,char *** r_argv)292 shell_parse_argv (const char *s, int *r_argc, char ***r_argv)
293 {
294 int i;
295 strlist_t list;
296
297 if (shell_parse_stringlist (s, &list))
298 return 1;
299
300 *r_argc = strlist_length (list);
301 *r_argv = xtrycalloc (*r_argc, sizeof **r_argv);
302 if (*r_argv == NULL)
303 return 1;
304
305 for (i = 0; list; i++)
306 {
307 gpgrt_annotate_leaked_object (list);
308 (*r_argv)[i] = list->d;
309 list = list->next;
310 }
311 gpgrt_annotate_leaked_object (*r_argv);
312 return 0;
313 }
314
315
316
317 /* Command line parsing. */
318 static void
parse_arguments(gpgrt_argparse_t * pargs,gpgrt_opt_t * popts)319 parse_arguments (gpgrt_argparse_t *pargs, gpgrt_opt_t *popts)
320 {
321 int no_more_options = 0;
322
323 while (!no_more_options && gpgrt_argparse (NULL, pargs, popts))
324 {
325 switch (pargs->r_opt)
326 {
327 case oOutput: opt.outfile = pargs->r.ret_str; break;
328 case oDirectory: opt.directory = pargs->r.ret_str; break;
329 case oSetFilename: opt.filename = pargs->r.ret_str; break;
330 case oQuiet: opt.quiet = 1; break;
331 case oVerbose: opt.verbose++; break;
332 case oNoVerbose: opt.verbose = 0; break;
333 case oFilesFrom: files_from = pargs->r.ret_str; break;
334 case oNull: null_names = 1; break;
335 case oUtf8Strings: opt.utf8strings = 1; break;
336
337 case aList:
338 case aDecrypt:
339 case aEncrypt:
340 case aSign:
341 set_cmd (&cmd, pargs->r_opt);
342 break;
343
344 case aCreate:
345 set_cmd (&cmd, aEncrypt);
346 skip_crypto = 1;
347 break;
348
349 case aExtract:
350 set_cmd (&cmd, aDecrypt);
351 skip_crypto = 1;
352 break;
353
354 case oRecipient:
355 add_to_strlist (&opt.recipients, pargs->r.ret_str);
356 break;
357
358 case oUser:
359 opt.user = pargs->r.ret_str;
360 break;
361
362 case oSymmetric:
363 set_cmd (&cmd, aEncrypt);
364 opt.symmetric = 1;
365 break;
366
367 case oGpgProgram:
368 opt.gpg_program = pargs->r.ret_str;
369 break;
370
371 case oSkipCrypto:
372 skip_crypto = 1;
373 break;
374
375 case oOpenPGP: /* Dummy option for now. */ break;
376 case oCMS: /* Dummy option for now. */ break;
377
378 case oGpgArgs:;
379 {
380 strlist_t list;
381 if (shell_parse_stringlist (pargs->r.ret_str, &list))
382 log_error ("failed to parse gpg arguments '%s'\n",
383 pargs->r.ret_str);
384 else
385 {
386 if (opt.gpg_arguments)
387 strlist_last (opt.gpg_arguments)->next = list;
388 else
389 opt.gpg_arguments = list;
390 }
391 }
392 break;
393
394 case oTarProgram: /* Dummy option. */
395 break;
396
397 case oTarArgs:
398 {
399 int tar_argc;
400 char **tar_argv;
401
402 if (shell_parse_argv (pargs->r.ret_str, &tar_argc, &tar_argv))
403 log_error ("failed to parse tar arguments '%s'\n",
404 pargs->r.ret_str);
405 else
406 {
407 gpgrt_argparse_t tar_args;
408 tar_args.argc = &tar_argc;
409 tar_args.argv = &tar_argv;
410 tar_args.flags = ARGPARSE_FLAG_ARG0;
411 parse_arguments (&tar_args, tar_opts);
412 gpgrt_argparse (NULL, &tar_args, NULL);
413 if (tar_args.err)
414 log_error ("unsupported tar arguments '%s'\n",
415 pargs->r.ret_str);
416 pargs->err = tar_args.err;
417 }
418 }
419 break;
420
421 case oDryRun:
422 opt.dry_run = 1;
423 break;
424
425 default: pargs->err = 2; break;
426 }
427 }
428 }
429
430
431
432 /* gpgtar main. */
433 int
main(int argc,char ** argv)434 main (int argc, char **argv)
435 {
436 gpg_error_t err;
437 const char *fname;
438 gpgrt_argparse_t pargs;
439
440 gnupg_reopen_std (GPGTAR_NAME);
441 gpgrt_set_strusage (my_strusage);
442 log_set_prefix (GPGTAR_NAME, GPGRT_LOG_WITH_PREFIX);
443
444 /* Make sure that our subsystems are ready. */
445 i18n_init();
446 init_common_subsystems (&argc, &argv);
447
448 log_assert (sizeof (struct ustar_raw_header) == 512);
449
450 /* Parse the command line. */
451 pargs.argc = &argc;
452 pargs.argv = &argv;
453 pargs.flags = ARGPARSE_FLAG_KEEP;
454 parse_arguments (&pargs, opts);
455 gpgrt_argparse (NULL, &pargs, NULL);
456
457 if (log_get_errorcount (0))
458 exit (2);
459
460 /* Print a warning if an argument looks like an option. */
461 if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
462 {
463 int i;
464
465 for (i=0; i < argc; i++)
466 if (argv[i][0] == '-' && argv[i][1] == '-')
467 log_info (_("NOTE: '%s' is not considered an option\n"), argv[i]);
468 }
469
470 if (! opt.gpg_program)
471 opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);
472
473 if (opt.verbose > 1)
474 opt.debug_level = 1024;
475
476 switch (cmd)
477 {
478 case aList:
479 if (argc > 1)
480 gpgrt_usage (1);
481 fname = argc ? *argv : NULL;
482 if (opt.filename)
483 log_info ("note: ignoring option --set-filename\n");
484 if (files_from)
485 log_info ("note: ignoring option --files-from\n");
486 err = gpgtar_list (fname, !skip_crypto);
487 if (err && log_get_errorcount (0) == 0)
488 log_error ("listing archive failed: %s\n", gpg_strerror (err));
489 break;
490
491 case aEncrypt:
492 case aSign:
493 case aSignEncrypt:
494 if ((!argc && !files_from)
495 || (argc && files_from))
496 gpgrt_usage (1);
497 if (opt.filename)
498 log_info ("note: ignoring option --set-filename\n");
499 err = gpgtar_create (files_from? NULL : argv,
500 files_from,
501 null_names,
502 !skip_crypto
503 && (cmd == aEncrypt || cmd == aSignEncrypt),
504 cmd == aSign || cmd == aSignEncrypt);
505 if (err && log_get_errorcount (0) == 0)
506 log_error ("creating archive failed: %s\n", gpg_strerror (err));
507 break;
508
509 case aDecrypt:
510 if (argc != 1)
511 gpgrt_usage (1);
512 if (opt.outfile)
513 log_info ("note: ignoring option --output\n");
514 if (files_from)
515 log_info ("note: ignoring option --files-from\n");
516 fname = argc ? *argv : NULL;
517 err = gpgtar_extract (fname, !skip_crypto);
518 if (err && log_get_errorcount (0) == 0)
519 log_error ("extracting archive failed: %s\n", gpg_strerror (err));
520 break;
521
522 default:
523 log_error (_("invalid command (there is no implicit command)\n"));
524 break;
525 }
526
527 return log_get_errorcount (0)? 1:0;
528 }
529
530
531 /* Read the next record from STREAM. RECORD is a buffer provided by
532 the caller and must be at leadt of size RECORDSIZE. The function
533 return 0 on success and error code on failure; a diagnostic
534 printed as well. Note that there is no need for an EOF indicator
535 because a tarball has an explicit EOF record. */
536 gpg_error_t
read_record(estream_t stream,void * record)537 read_record (estream_t stream, void *record)
538 {
539 gpg_error_t err;
540 size_t nread;
541
542 nread = es_fread (record, 1, RECORDSIZE, stream);
543 if (nread != RECORDSIZE)
544 {
545 err = gpg_error_from_syserror ();
546 if (es_ferror (stream))
547 log_error ("error reading '%s': %s\n",
548 es_fname_get (stream), gpg_strerror (err));
549 else
550 log_error ("error reading '%s': premature EOF "
551 "(size of last record: %zu)\n",
552 es_fname_get (stream), nread);
553 }
554 else
555 err = 0;
556
557 return err;
558 }
559
560
561 /* Write the RECORD of size RECORDSIZE to STREAM. FILENAME is the
562 name of the file used for diagnostics. */
563 gpg_error_t
write_record(estream_t stream,const void * record)564 write_record (estream_t stream, const void *record)
565 {
566 gpg_error_t err;
567 size_t nwritten;
568
569 nwritten = es_fwrite (record, 1, RECORDSIZE, stream);
570 if (nwritten != RECORDSIZE)
571 {
572 err = gpg_error_from_syserror ();
573 log_error ("error writing '%s': %s\n",
574 es_fname_get (stream), gpg_strerror (err));
575 }
576 else
577 err = 0;
578
579 return err;
580 }
581
582
583 /* Return true if FP is an unarmored OpenPGP message. Note that this
584 function reads a few bytes from FP but pushes them back. */
585 #if 0
586 static int
587 openpgp_message_p (estream_t fp)
588 {
589 int ctb;
590
591 ctb = es_getc (fp);
592 if (ctb != EOF)
593 {
594 if (es_ungetc (ctb, fp))
595 log_fatal ("error ungetting first byte: %s\n",
596 gpg_strerror (gpg_error_from_syserror ()));
597
598 if ((ctb & 0x80))
599 {
600 switch ((ctb & 0x40) ? (ctb & 0x3f) : ((ctb>>2)&0xf))
601 {
602 case PKT_MARKER:
603 case PKT_SYMKEY_ENC:
604 case PKT_ONEPASS_SIG:
605 case PKT_PUBKEY_ENC:
606 case PKT_SIGNATURE:
607 case PKT_COMMENT:
608 case PKT_OLD_COMMENT:
609 case PKT_PLAINTEXT:
610 case PKT_COMPRESSED:
611 case PKT_ENCRYPTED:
612 return 1; /* Yes, this seems to be an OpenPGP message. */
613 default:
614 break;
615 }
616 }
617 }
618 return 0;
619 }
620 #endif
621