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