Line data Source code
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 <http://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 <errno.h>
31 : #include <stdio.h>
32 : #include <stdlib.h>
33 : #include <string.h>
34 : #include <assert.h>
35 :
36 : #include "util.h"
37 : #include "i18n.h"
38 : #include "sysutils.h"
39 : #include "../common/openpgpdefs.h"
40 : #include "../common/init.h"
41 :
42 : #include "gpgtar.h"
43 :
44 :
45 : /* Constants to identify the commands and options. */
46 : enum cmd_and_opt_values
47 : {
48 : aNull = 0,
49 : aEncrypt = 'e',
50 : aDecrypt = 'd',
51 : aSign = 's',
52 : aList = 't',
53 :
54 : oSymmetric = 'c',
55 : oRecipient = 'r',
56 : oUser = 'u',
57 : oOutput = 'o',
58 : oQuiet = 'q',
59 : oVerbose = 'v',
60 : oFilesFrom = 'T',
61 : oNoVerbose = 500,
62 :
63 : aSignEncrypt,
64 : oSkipCrypto,
65 : oOpenPGP,
66 : oCMS,
67 : oSetFilename,
68 : oNull
69 : };
70 :
71 :
72 : /* The list of commands and options. */
73 : static ARGPARSE_OPTS opts[] = {
74 : ARGPARSE_group (300, N_("@Commands:\n ")),
75 :
76 : ARGPARSE_c (aEncrypt, "encrypt", N_("create an archive")),
77 : ARGPARSE_c (aDecrypt, "decrypt", N_("extract an archive")),
78 : ARGPARSE_c (aSign, "sign", N_("create a signed archive")),
79 : ARGPARSE_c (aList, "list-archive", N_("list an archive")),
80 :
81 : ARGPARSE_group (301, N_("@\nOptions:\n ")),
82 :
83 : ARGPARSE_s_n (oSymmetric, "symmetric", N_("use symmetric encryption")),
84 : ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")),
85 : ARGPARSE_s_s (oUser, "local-user",
86 : N_("|USER-ID|use USER-ID to sign or decrypt")),
87 : ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")),
88 : ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
89 : ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
90 : ARGPARSE_s_n (oSkipCrypto, "skip-crypto", N_("skip the crypto processing")),
91 : ARGPARSE_s_s (oSetFilename, "set-filename", "@"),
92 : ARGPARSE_s_s (oFilesFrom, "files-from",
93 : N_("|FILE|get names to create from FILE")),
94 : ARGPARSE_s_n (oNull, "null", N_("-T reads null-terminated names")),
95 : ARGPARSE_s_n (oOpenPGP, "openpgp", "@"),
96 : ARGPARSE_s_n (oCMS, "cms", "@"),
97 :
98 : ARGPARSE_end ()
99 : };
100 :
101 :
102 :
103 : static void tar_and_encrypt (char **inpattern);
104 : static void decrypt_and_untar (const char *fname);
105 : static void decrypt_and_list (const char *fname);
106 :
107 :
108 :
109 :
110 : /* Print usage information and and provide strings for help. */
111 : static const char *
112 0 : my_strusage( int level )
113 : {
114 : const char *p;
115 :
116 0 : switch (level)
117 : {
118 0 : case 11: p = "@GPGTAR@ (@GNUPG@)";
119 0 : break;
120 0 : case 13: p = VERSION; break;
121 0 : case 17: p = PRINTABLE_OS_NAME; break;
122 0 : case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
123 :
124 : case 1:
125 : case 40:
126 0 : p = _("Usage: gpgtar [options] [files] [directories] (-h for help)");
127 0 : break;
128 : case 41:
129 0 : p = _("Syntax: gpgtar [options] [files] [directories]\n"
130 : "Encrypt or sign files into an archive\n");
131 0 : break;
132 :
133 0 : default: p = NULL; break;
134 : }
135 0 : return p;
136 : }
137 :
138 :
139 : static void
140 0 : set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd)
141 : {
142 0 : enum cmd_and_opt_values cmd = *ret_cmd;
143 :
144 0 : if (!cmd || cmd == new_cmd)
145 0 : cmd = new_cmd;
146 0 : else if (cmd == aSign && new_cmd == aEncrypt)
147 0 : cmd = aSignEncrypt;
148 0 : else if (cmd == aEncrypt && new_cmd == aSign)
149 0 : cmd = aSignEncrypt;
150 : else
151 : {
152 0 : log_error (_("conflicting commands\n"));
153 0 : exit (2);
154 : }
155 :
156 0 : *ret_cmd = cmd;
157 0 : }
158 :
159 :
160 :
161 : /* gpgtar main. */
162 : int
163 0 : main (int argc, char **argv)
164 : {
165 : ARGPARSE_ARGS pargs;
166 : const char *fname;
167 0 : int no_more_options = 0;
168 0 : enum cmd_and_opt_values cmd = 0;
169 0 : int skip_crypto = 0;
170 0 : const char *files_from = NULL;
171 0 : int null_names = 0;
172 :
173 : assert (sizeof (struct ustar_raw_header) == 512);
174 :
175 0 : gnupg_reopen_std (GPGTAR_NAME);
176 0 : set_strusage (my_strusage);
177 0 : log_set_prefix (GPGTAR_NAME, 1);
178 :
179 : /* Make sure that our subsystems are ready. */
180 0 : i18n_init();
181 0 : init_common_subsystems (&argc, &argv);
182 :
183 : /* Parse the command line. */
184 0 : pargs.argc = &argc;
185 0 : pargs.argv = &argv;
186 0 : pargs.flags = ARGPARSE_FLAG_KEEP;
187 0 : while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts))
188 : {
189 0 : switch (pargs.r_opt)
190 : {
191 0 : case oOutput: opt.outfile = pargs.r.ret_str; break;
192 0 : case oSetFilename: opt.filename = pargs.r.ret_str; break;
193 0 : case oQuiet: opt.quiet = 1; break;
194 0 : case oVerbose: opt.verbose++; break;
195 0 : case oNoVerbose: opt.verbose = 0; break;
196 0 : case oFilesFrom: files_from = pargs.r.ret_str; break;
197 0 : case oNull: null_names = 1; break;
198 :
199 : case aList:
200 : case aDecrypt:
201 : case aEncrypt:
202 : case aSign:
203 0 : set_cmd (&cmd, pargs.r_opt);
204 0 : break;
205 :
206 : case oSymmetric:
207 0 : set_cmd (&cmd, aEncrypt);
208 0 : opt.symmetric = 1;
209 0 : break;
210 :
211 : case oSkipCrypto:
212 0 : skip_crypto = 1;
213 0 : break;
214 :
215 0 : case oOpenPGP: /* Dummy option for now. */ break;
216 0 : case oCMS: /* Dummy option for now. */ break;
217 :
218 0 : default: pargs.err = 2; break;
219 : }
220 : }
221 :
222 0 : if ((files_from && !null_names) || (!files_from && null_names))
223 0 : log_error ("--files-from and --null may only be used in conjunction\n");
224 0 : if (files_from && strcmp (files_from, "-"))
225 0 : log_error ("--files-from only supports argument \"-\"\n");
226 :
227 0 : if (log_get_errorcount (0))
228 0 : exit (2);
229 :
230 : /* Print a warning if an argument looks like an option. */
231 0 : if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
232 : {
233 : int i;
234 :
235 0 : for (i=0; i < argc; i++)
236 0 : if (argv[i][0] == '-' && argv[i][1] == '-')
237 0 : log_info (_("NOTE: '%s' is not considered an option\n"), argv[i]);
238 : }
239 :
240 0 : switch (cmd)
241 : {
242 : case aList:
243 0 : if (argc > 1)
244 0 : usage (1);
245 0 : fname = argc ? *argv : NULL;
246 0 : if (opt.filename)
247 0 : log_info ("note: ignoring option --set-filename\n");
248 0 : if (files_from)
249 0 : log_info ("note: ignoring option --files-from\n");
250 0 : if (skip_crypto)
251 0 : gpgtar_list (fname);
252 : else
253 0 : decrypt_and_list (fname);
254 0 : break;
255 :
256 : case aEncrypt:
257 0 : if ((!argc && !null_names)
258 0 : || (argc && null_names))
259 0 : usage (1);
260 0 : if (opt.filename)
261 0 : log_info ("note: ignoring option --set-filename\n");
262 0 : if (skip_crypto)
263 0 : gpgtar_create (null_names? NULL :argv);
264 : else
265 0 : tar_and_encrypt (null_names? NULL : argv);
266 0 : break;
267 :
268 : case aDecrypt:
269 0 : if (argc != 1)
270 0 : usage (1);
271 0 : if (opt.outfile)
272 0 : log_info ("note: ignoring option --output\n");
273 0 : if (files_from)
274 0 : log_info ("note: ignoring option --files-from\n");
275 0 : fname = argc ? *argv : NULL;
276 0 : if (skip_crypto)
277 0 : gpgtar_extract (fname);
278 : else
279 0 : decrypt_and_untar (fname);
280 0 : break;
281 :
282 : default:
283 0 : log_error (_("invalid command (there is no implicit command)\n"));
284 0 : break;
285 : }
286 :
287 0 : return log_get_errorcount (0)? 1:0;
288 : }
289 :
290 :
291 : /* Read the next record from STREAM. RECORD is a buffer provided by
292 : the caller and must be at leadt of size RECORDSIZE. The function
293 : return 0 on success and and error code on failure; a diagnostic
294 : printed as well. Note that there is no need for an EOF indicator
295 : because a tarball has an explicit EOF record. */
296 : gpg_error_t
297 0 : read_record (estream_t stream, void *record)
298 : {
299 : gpg_error_t err;
300 : size_t nread;
301 :
302 0 : nread = es_fread (record, 1, RECORDSIZE, stream);
303 0 : if (nread != RECORDSIZE)
304 : {
305 0 : err = gpg_error_from_syserror ();
306 0 : if (es_ferror (stream))
307 0 : log_error ("error reading '%s': %s\n",
308 : es_fname_get (stream), gpg_strerror (err));
309 : else
310 0 : log_error ("error reading '%s': premature EOF "
311 : "(size of last record: %zu)\n",
312 : es_fname_get (stream), nread);
313 : }
314 : else
315 0 : err = 0;
316 :
317 0 : return err;
318 : }
319 :
320 :
321 : /* Write the RECORD of size RECORDSIZE to STREAM. FILENAME is the
322 : name of the file used for diagnostics. */
323 : gpg_error_t
324 0 : write_record (estream_t stream, const void *record)
325 : {
326 : gpg_error_t err;
327 : size_t nwritten;
328 :
329 0 : nwritten = es_fwrite (record, 1, RECORDSIZE, stream);
330 0 : if (nwritten != RECORDSIZE)
331 : {
332 0 : err = gpg_error_from_syserror ();
333 0 : log_error ("error writing '%s': %s\n",
334 : es_fname_get (stream), gpg_strerror (err));
335 : }
336 : else
337 0 : err = 0;
338 :
339 0 : return err;
340 : }
341 :
342 :
343 : /* Return true if FP is an unarmored OpenPGP message. Note that this
344 : fucntion reads a few bytes from FP but pushes them back. */
345 : #if 0
346 : static int
347 : openpgp_message_p (estream_t fp)
348 : {
349 : int ctb;
350 :
351 : ctb = es_getc (fp);
352 : if (ctb != EOF)
353 : {
354 : if (es_ungetc (ctb, fp))
355 : log_fatal ("error ungetting first byte: %s\n",
356 : gpg_strerror (gpg_error_from_syserror ()));
357 :
358 : if ((ctb & 0x80))
359 : {
360 : switch ((ctb & 0x40) ? (ctb & 0x3f) : ((ctb>>2)&0xf))
361 : {
362 : case PKT_MARKER:
363 : case PKT_SYMKEY_ENC:
364 : case PKT_ONEPASS_SIG:
365 : case PKT_PUBKEY_ENC:
366 : case PKT_SIGNATURE:
367 : case PKT_COMMENT:
368 : case PKT_OLD_COMMENT:
369 : case PKT_PLAINTEXT:
370 : case PKT_COMPRESSED:
371 : case PKT_ENCRYPTED:
372 : return 1; /* Yes, this seems to be an OpenPGP message. */
373 : default:
374 : break;
375 : }
376 : }
377 : }
378 : return 0;
379 : }
380 : #endif
381 :
382 :
383 :
384 :
385 : static void
386 0 : tar_and_encrypt (char **inpattern)
387 : {
388 : (void)inpattern;
389 0 : log_error ("tar_and_encrypt has not yet been implemented\n");
390 0 : }
391 :
392 :
393 :
394 : static void
395 0 : decrypt_and_untar (const char *fname)
396 : {
397 : (void)fname;
398 0 : log_error ("decrypt_and_untar has not yet been implemented\n");
399 0 : }
400 :
401 :
402 :
403 : static void
404 0 : decrypt_and_list (const char *fname)
405 : {
406 : (void)fname;
407 0 : log_error ("decrypt_and_list has not yet been implemented\n");
408 0 : }
|