Line data Source code
1 : /* gpgtar-extract.c - Extract from a TAR archive
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 : #include <config.h>
21 : #include <errno.h>
22 : #include <stdio.h>
23 : #include <stdlib.h>
24 : #include <string.h>
25 : #include <sys/types.h>
26 : #include <sys/stat.h>
27 : #include <unistd.h>
28 : #include <assert.h>
29 :
30 : #include "i18n.h"
31 : #include "../common/exectool.h"
32 : #include "../common/sysutils.h"
33 : #include "../common/ccparray.h"
34 : #include "gpgtar.h"
35 :
36 :
37 : static gpg_error_t
38 1344 : extract_regular (estream_t stream, const char *dirname,
39 : tar_header_t hdr)
40 : {
41 : gpg_error_t err;
42 : char record[RECORDSIZE];
43 : size_t n, nbytes, nwritten;
44 : char *fname;
45 1344 : estream_t outfp = NULL;
46 :
47 1344 : fname = strconcat (dirname, "/", hdr->name, NULL);
48 1344 : if (!fname)
49 : {
50 0 : err = gpg_error_from_syserror ();
51 0 : log_error ("error creating filename: %s\n", gpg_strerror (err));
52 0 : goto leave;
53 : }
54 : else
55 1344 : err = 0;
56 :
57 1344 : if (opt.dry_run)
58 0 : outfp = es_fopenmem (0, "wb");
59 : else
60 1344 : outfp = es_fopen (fname, "wb");
61 1344 : if (!outfp)
62 : {
63 0 : err = gpg_error_from_syserror ();
64 0 : log_error ("error creating '%s': %s\n", fname, gpg_strerror (err));
65 0 : goto leave;
66 : }
67 :
68 39101 : for (n=0; n < hdr->nrecords;)
69 : {
70 36413 : err = read_record (stream, record);
71 36413 : if (err)
72 0 : goto leave;
73 36413 : n++;
74 36413 : if (n < hdr->nrecords || (hdr->size && !(hdr->size % RECORDSIZE)))
75 35070 : nbytes = RECORDSIZE;
76 : else
77 1343 : nbytes = (hdr->size % RECORDSIZE);
78 :
79 36413 : nwritten = es_fwrite (record, 1, nbytes, outfp);
80 36413 : if (nwritten != nbytes)
81 : {
82 0 : err = gpg_error_from_syserror ();
83 0 : log_error ("error writing '%s': %s\n", fname, gpg_strerror (err));
84 0 : goto leave;
85 : }
86 : }
87 : /* Fixme: Set permissions etc. */
88 :
89 : leave:
90 1344 : if (!err && opt.verbose)
91 0 : log_info ("extracted '%s'\n", fname);
92 1344 : es_fclose (outfp);
93 1344 : if (err && fname && outfp)
94 : {
95 0 : if (gnupg_remove (fname))
96 0 : log_error ("error removing incomplete file '%s': %s\n",
97 : fname, gpg_strerror (gpg_error_from_syserror ()));
98 : }
99 1344 : xfree (fname);
100 1344 : return err;
101 : }
102 :
103 :
104 : static gpg_error_t
105 42 : extract_directory (const char *dirname, tar_header_t hdr)
106 : {
107 : gpg_error_t err;
108 : char *fname;
109 : size_t prefixlen;
110 :
111 42 : prefixlen = strlen (dirname) + 1;
112 42 : fname = strconcat (dirname, "/", hdr->name, NULL);
113 42 : if (!fname)
114 : {
115 0 : err = gpg_error_from_syserror ();
116 0 : log_error ("error creating filename: %s\n", gpg_strerror (err));
117 0 : goto leave;
118 : }
119 : else
120 42 : err = 0;
121 :
122 42 : if (fname[strlen (fname)-1] == '/')
123 0 : fname[strlen (fname)-1] = 0;
124 :
125 : /* Note that we don't need to care about EEXIST because we always
126 : extract into a new hierarchy. */
127 42 : if (! opt.dry_run && gnupg_mkdir (fname, "-rwx------"))
128 : {
129 0 : err = gpg_error_from_syserror ();
130 0 : if (gpg_err_code (err) == GPG_ERR_ENOENT)
131 : {
132 : /* Try to create the directory with parents but keep the
133 : original error code in case of a failure. */
134 : char *p;
135 0 : int rc = 0;
136 :
137 0 : for (p = fname+prefixlen; (p = strchr (p, '/')); p++)
138 : {
139 0 : *p = 0;
140 0 : rc = gnupg_mkdir (fname, "-rwx------");
141 0 : *p = '/';
142 0 : if (rc)
143 0 : break;
144 : }
145 0 : if (!rc && !gnupg_mkdir (fname, "-rwx------"))
146 0 : err = 0;
147 : }
148 0 : if (err)
149 0 : log_error ("error creating directory '%s': %s\n",
150 : fname, gpg_strerror (err));
151 : }
152 :
153 : leave:
154 42 : if (!err && opt.verbose)
155 0 : log_info ("created '%s/'\n", fname);
156 42 : xfree (fname);
157 42 : return err;
158 : }
159 :
160 :
161 : static gpg_error_t
162 1386 : extract (estream_t stream, const char *dirname, tar_header_t hdr)
163 : {
164 : gpg_error_t err;
165 : size_t n;
166 :
167 1386 : n = strlen (hdr->name);
168 : #ifdef HAVE_DOSISH_SYSTEM
169 : if (strchr (hdr->name, '\\'))
170 : {
171 : log_error ("filename '%s' contains a backslash - "
172 : "can't extract on this system\n", hdr->name);
173 : return gpg_error (GPG_ERR_INV_NAME);
174 : }
175 : #endif /*HAVE_DOSISH_SYSTEM*/
176 :
177 1386 : if (!n
178 1386 : || strstr (hdr->name, "//")
179 1386 : || strstr (hdr->name, "/../")
180 1386 : || !strncmp (hdr->name, "../", 3)
181 1386 : || (n >= 3 && !strcmp (hdr->name+n-3, "/.." )))
182 : {
183 0 : log_error ("filename '%s' as suspicious parts - not extracting\n",
184 0 : hdr->name);
185 0 : return gpg_error (GPG_ERR_INV_NAME);
186 : }
187 :
188 1386 : if (hdr->typeflag == TF_REGULAR || hdr->typeflag == TF_UNKNOWN)
189 1344 : err = extract_regular (stream, dirname, hdr);
190 42 : else if (hdr->typeflag == TF_DIRECTORY)
191 42 : err = extract_directory (dirname, hdr);
192 : else
193 : {
194 : char record[RECORDSIZE];
195 :
196 0 : log_info ("unsupported file type %d for '%s' - skipped\n",
197 0 : (int)hdr->typeflag, hdr->name);
198 0 : for (err = 0, n=0; !err && n < hdr->nrecords; n++)
199 0 : err = read_record (stream, record);
200 : }
201 1386 : return err;
202 : }
203 :
204 :
205 : /* Create a new directory to be used for extracting the tarball.
206 : Returns the name of the directory which must be freed by the
207 : caller. In case of an error a diagnostic is printed and NULL
208 : returned. */
209 : static char *
210 0 : create_directory (const char *dirprefix)
211 : {
212 0 : gpg_error_t err = 0;
213 0 : char *prefix_buffer = NULL;
214 0 : char *dirname = NULL;
215 : size_t n;
216 : int idx;
217 :
218 : /* Remove common suffixes. */
219 0 : n = strlen (dirprefix);
220 0 : if (n > 4 && (!compare_filenames (dirprefix + n - 4, EXTSEP_S GPGEXT_GPG)
221 0 : || !compare_filenames (dirprefix + n - 4, EXTSEP_S "pgp")
222 0 : || !compare_filenames (dirprefix + n - 4, EXTSEP_S "asc")
223 0 : || !compare_filenames (dirprefix + n - 4, EXTSEP_S "pem")
224 0 : || !compare_filenames (dirprefix + n - 4, EXTSEP_S "p7m")
225 0 : || !compare_filenames (dirprefix + n - 4, EXTSEP_S "p7e")))
226 : {
227 0 : prefix_buffer = xtrystrdup (dirprefix);
228 0 : if (!prefix_buffer)
229 : {
230 0 : err = gpg_error_from_syserror ();
231 0 : goto leave;
232 : }
233 0 : prefix_buffer[n-4] = 0;
234 0 : dirprefix = prefix_buffer;
235 : }
236 :
237 :
238 :
239 0 : for (idx=1; idx < 5000; idx++)
240 : {
241 0 : xfree (dirname);
242 0 : dirname = xtryasprintf ("%s_%d_", dirprefix, idx);
243 0 : if (!dirname)
244 : {
245 0 : err = gpg_error_from_syserror ();
246 0 : goto leave;
247 : }
248 0 : if (!gnupg_mkdir (dirname, "-rwx------"))
249 0 : goto leave; /* Ready. */
250 0 : if (errno != EEXIST && errno != ENOTDIR)
251 : {
252 0 : err = gpg_error_from_syserror ();
253 0 : goto leave;
254 : }
255 : }
256 0 : err = gpg_error (GPG_ERR_LIMIT_REACHED);
257 :
258 : leave:
259 0 : if (err)
260 : {
261 0 : log_error ("error creating an extract directory: %s\n",
262 : gpg_strerror (err));
263 0 : xfree (dirname);
264 0 : dirname = NULL;
265 : }
266 0 : xfree (prefix_buffer);
267 0 : return dirname;
268 : }
269 :
270 :
271 :
272 : gpg_error_t
273 54 : gpgtar_extract (const char *filename, int decrypt)
274 : {
275 : gpg_error_t err;
276 : estream_t stream;
277 54 : estream_t cipher_stream = NULL;
278 54 : tar_header_t header = NULL;
279 54 : const char *dirprefix = NULL;
280 54 : char *dirname = NULL;
281 :
282 54 : if (filename)
283 : {
284 54 : if (!strcmp (filename, "-"))
285 5 : stream = es_stdin;
286 : else
287 49 : stream = es_fopen (filename, "rb");
288 54 : if (!stream)
289 : {
290 0 : err = gpg_error_from_syserror ();
291 0 : log_error ("error opening '%s': %s\n", filename, gpg_strerror (err));
292 0 : return err;
293 : }
294 : }
295 : else
296 0 : stream = es_stdin;
297 :
298 54 : if (stream == es_stdin)
299 5 : es_set_binary (es_stdin);
300 :
301 54 : if (decrypt)
302 : {
303 : strlist_t arg;
304 : ccparray_t ccp;
305 : const char **argv;
306 :
307 6 : cipher_stream = stream;
308 6 : stream = es_fopenmem (0, "rwb");
309 6 : if (! stream)
310 : {
311 0 : err = gpg_error_from_syserror ();
312 0 : goto leave;
313 : }
314 :
315 6 : ccparray_init (&ccp, 0);
316 :
317 6 : ccparray_put (&ccp, "--decrypt");
318 21 : for (arg = opt.gpg_arguments; arg; arg = arg->next)
319 15 : ccparray_put (&ccp, arg->d);
320 :
321 6 : ccparray_put (&ccp, NULL);
322 6 : argv = ccparray_get (&ccp, NULL);
323 6 : if (!argv)
324 : {
325 0 : err = gpg_error_from_syserror ();
326 0 : goto leave;
327 : }
328 :
329 6 : err = gnupg_exec_tool_stream (opt.gpg_program, argv,
330 : cipher_stream, NULL, stream, NULL, NULL);
331 6 : xfree (argv);
332 6 : if (err)
333 0 : goto leave;
334 :
335 6 : err = es_fseek (stream, 0, SEEK_SET);
336 6 : if (err)
337 0 : goto leave;
338 : }
339 :
340 54 : if (opt.directory)
341 54 : dirname = xtrystrdup (opt.directory);
342 : else
343 : {
344 0 : if (filename)
345 : {
346 0 : dirprefix = strrchr (filename, '/');
347 0 : if (dirprefix)
348 0 : dirprefix++;
349 : else
350 0 : dirprefix = filename;
351 : }
352 0 : else if (opt.filename)
353 : {
354 0 : dirprefix = strrchr (opt.filename, '/');
355 0 : if (dirprefix)
356 0 : dirprefix++;
357 : else
358 0 : dirprefix = opt.filename;
359 : }
360 :
361 0 : if (!dirprefix || !*dirprefix)
362 0 : dirprefix = "GPGARCH";
363 :
364 0 : dirname = create_directory (dirprefix);
365 0 : if (!dirname)
366 : {
367 0 : err = gpg_error (GPG_ERR_GENERAL);
368 0 : goto leave;
369 : }
370 : }
371 :
372 54 : if (opt.verbose)
373 0 : log_info ("extracting to '%s/'\n", dirname);
374 :
375 : for (;;)
376 : {
377 1440 : err = gpgtar_read_header (stream, &header);
378 1440 : if (err || header == NULL)
379 : goto leave;
380 :
381 1386 : err = extract (stream, dirname, header);
382 1386 : if (err)
383 0 : goto leave;
384 1386 : xfree (header);
385 1386 : header = NULL;
386 1386 : }
387 :
388 :
389 : leave:
390 54 : xfree (header);
391 54 : xfree (dirname);
392 54 : if (stream != es_stdin)
393 49 : es_fclose (stream);
394 54 : if (stream != cipher_stream)
395 54 : es_fclose (cipher_stream);
396 54 : return err;
397 : }
|