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