tar: tighten up pax header validity check
[oweals/busybox.git] / archival / libarchive / get_header_tar.c
1 /* vi: set sw=4 ts=4: */
2 /* Licensed under GPLv2 or later, see file LICENSE in this source tree.
3  *
4  * FIXME:
5  *    In privileged mode if uname and gname map to a uid and gid then use the
6  *    mapped value instead of the uid/gid values in tar header
7  *
8  * References:
9  *    GNU tar and star man pages,
10  *    Opengroup's ustar interchange format,
11  *    http://www.opengroup.org/onlinepubs/007904975/utilities/pax.html
12  */
13
14 #include "libbb.h"
15 #include "bb_archive.h"
16
17 typedef uint32_t aliased_uint32_t FIX_ALIASING;
18 typedef off_t    aliased_off_t    FIX_ALIASING;
19
20
21 const char* FAST_FUNC strip_unsafe_prefix(const char *str)
22 {
23         const char *cp = str;
24         while (1) {
25                 char *cp2;
26                 if (*cp == '/') {
27                         cp++;
28                         continue;
29                 }
30                 if (strncmp(cp, "/../"+1, 3) == 0) {
31                         cp += 3;
32                         continue;
33                 }
34                 cp2 = strstr(cp, "/../");
35                 if (!cp2)
36                         break;
37                 cp = cp2 + 4;
38         }
39         if (cp != str) {
40                 static smallint warned = 0;
41                 if (!warned) {
42                         warned = 1;
43                         bb_error_msg("removing leading '%.*s' from member names",
44                                 (int)(cp - str), str);
45                 }
46         }
47         return cp;
48 }
49
50 /* NB: _DESTROYS_ str[len] character! */
51 static unsigned long long getOctal(char *str, int len)
52 {
53         unsigned long long v;
54         char *end;
55         /* NB: leading spaces are allowed. Using strtoull to handle that.
56          * The downside is that we accept e.g. "-123" too :(
57          */
58         str[len] = '\0';
59         v = strtoull(str, &end, 8);
60         /* std: "Each numeric field is terminated by one or more
61          * <space> or NUL characters". We must support ' '! */
62         if (*end != '\0' && *end != ' ') {
63                 int8_t first = str[0];
64                 if (!(first & 0x80))
65                         bb_error_msg_and_die("corrupted octal value in tar header");
66                 /*
67                  * GNU tar uses "base-256 encoding" for very large numbers.
68                  * Encoding is binary, with highest bit always set as a marker
69                  * and sign in next-highest bit:
70                  * 80 00 .. 00 - zero
71                  * bf ff .. ff - largest positive number
72                  * ff ff .. ff - minus 1
73                  * c0 00 .. 00 - smallest negative number
74                  *
75                  * Example of tar file with 8914993153 (0x213600001) byte file.
76                  * Field starts at offset 7c:
77                  * 00070  30 30 30 00 30 30 30 30  30 30 30 00 80 00 00 00  |000.0000000.....|
78                  * 00080  00 00 00 02 13 60 00 01  31 31 31 32 30 33 33 36  |.....`..11120336|
79                  *
80                  * NB: tarballs with NEGATIVE unix times encoded that way were seen!
81                  */
82                 /* Sign-extend 7bit 'first' to 64bit 'v' (that is, using 6th bit as sign): */
83                 first <<= 1;
84                 first >>= 1; /* now 7th bit = 6th bit */
85                 v = first;   /* sign-extend 8 bits to 64 */
86                 while (--len != 0)
87                         v = (v << 8) + (uint8_t) *++str;
88         }
89         return v;
90 }
91 #define GET_OCTAL(a) getOctal((a), sizeof(a))
92
93 /* "global" is 0 or 1 */
94 static void process_pax_hdr(archive_handle_t *archive_handle, unsigned sz, int global)
95 {
96         char *buf, *p;
97         unsigned blk_sz;
98
99         blk_sz = (sz + 511) & (~511);
100         p = buf = xmalloc(blk_sz + 1);
101         xread(archive_handle->src_fd, buf, blk_sz);
102         archive_handle->offset += blk_sz;
103
104         /* prevent bb_strtou from running off the buffer */
105         buf[sz] = '\0';
106
107         while (sz != 0) {
108                 char *end, *value;
109                 unsigned len;
110
111                 /* Every record has this format: "LEN NAME=VALUE\n" */
112                 len = bb_strtou(p, &end, 10);
113                 /* expect errno to be EINVAL, because the character
114                  * following the digits should be a space
115                  */
116                 p += len;
117                 sz -= len;
118                 if (
119                 /** (int)sz < 0 - not good enough for huge malicious VALUE of 2^32-1 */
120                     (int)(sz|len) < 0 /* this works */
121                  || len == 0
122                  || errno != EINVAL
123                  || *end != ' '
124                 ) {
125                         bb_error_msg("malformed extended header, skipped");
126                         // More verbose version:
127                         //bb_error_msg("malformed extended header at %"OFF_FMT"d, skipped",
128                         //              archive_handle->offset - (sz + len));
129                         break;
130                 }
131                 /* overwrite the terminating newline with NUL
132                  * (we do not bother to check that it *was* a newline)
133                  */
134                 p[-1] = '\0';
135                 value = end + 1;
136
137 #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
138                 if (!global && strncmp(value, "path=", sizeof("path=") - 1) == 0) {
139                         value += sizeof("path=") - 1;
140                         free(archive_handle->tar__longname);
141                         archive_handle->tar__longname = xstrdup(value);
142                         continue;
143                 }
144 #endif
145
146 #if ENABLE_FEATURE_TAR_SELINUX
147                 /* Scan for SELinux contexts, via "RHT.security.selinux" keyword.
148                  * This is what Red Hat's patched version of tar uses.
149                  */
150 # define SELINUX_CONTEXT_KEYWORD "RHT.security.selinux"
151                 if (strncmp(value, SELINUX_CONTEXT_KEYWORD"=", sizeof(SELINUX_CONTEXT_KEYWORD"=") - 1) == 0) {
152                         value += sizeof(SELINUX_CONTEXT_KEYWORD"=") - 1;
153                         free(archive_handle->tar__sctx[global]);
154                         archive_handle->tar__sctx[global] = xstrdup(value);
155                         continue;
156                 }
157 #endif
158         }
159
160         free(buf);
161 }
162
163 char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
164 {
165         file_header_t *file_header = archive_handle->file_header;
166         struct tar_header_t tar;
167         char *cp;
168         int i, sum_u, sum;
169 #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
170         int sum_s;
171 #endif
172         int parse_names;
173
174         /* Our "private data" */
175 #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
176 # define p_longname (archive_handle->tar__longname)
177 # define p_linkname (archive_handle->tar__linkname)
178 #else
179 # define p_longname 0
180 # define p_linkname 0
181 #endif
182
183 #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS || ENABLE_FEATURE_TAR_SELINUX
184  again:
185 #endif
186         /* Align header */
187         data_align(archive_handle, 512);
188
189  again_after_align:
190
191 #if ENABLE_DESKTOP || ENABLE_FEATURE_TAR_AUTODETECT
192         /* to prevent misdetection of bz2 sig */
193         *(aliased_uint32_t*)&tar = 0;
194         i = full_read(archive_handle->src_fd, &tar, 512);
195         /* If GNU tar sees EOF in above read, it says:
196          * "tar: A lone zero block at N", where N = kilobyte
197          * where EOF was met (not EOF block, actual EOF!),
198          * and exits with EXIT_SUCCESS.
199          * We will mimic exit(EXIT_SUCCESS), although we will not mimic
200          * the message and we don't check whether we indeed
201          * saw zero block directly before this. */
202         if (i == 0) {
203                 bb_error_msg("short read");
204                 /* this merely signals end of archive, not exit(1): */
205                 return EXIT_FAILURE;
206         }
207         if (i != 512) {
208                 IF_FEATURE_TAR_AUTODETECT(goto autodetect;)
209                 bb_error_msg_and_die("short read");
210         }
211
212 #else
213         i = 512;
214         xread(archive_handle->src_fd, &tar, i);
215 #endif
216         archive_handle->offset += i;
217
218         /* If there is no filename its an empty header */
219         if (tar.name[0] == 0 && tar.prefix[0] == 0) {
220                 if (archive_handle->tar__end) {
221                         /* Second consecutive empty header - end of archive.
222                          * Read until the end to empty the pipe from gz or bz2
223                          */
224                         while (full_read(archive_handle->src_fd, &tar, 512) == 512)
225                                 continue;
226                         return EXIT_FAILURE; /* "end of archive" */
227                 }
228                 archive_handle->tar__end = 1;
229                 return EXIT_SUCCESS; /* "decoded one header" */
230         }
231         archive_handle->tar__end = 0;
232
233         /* Check header has valid magic, "ustar" is for the proper tar,
234          * five NULs are for the old tar format  */
235         if (strncmp(tar.magic, "ustar", 5) != 0
236          && (!ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
237              || memcmp(tar.magic, "\0\0\0\0", 5) != 0)
238         ) {
239 #if ENABLE_FEATURE_TAR_AUTODETECT
240  autodetect:
241                 /* Two different causes for lseek() != 0:
242                  * unseekable fd (would like to support that too, but...),
243                  * or not first block (false positive, it's not .gz/.bz2!) */
244                 if (lseek(archive_handle->src_fd, -i, SEEK_CUR) != 0)
245                         goto err;
246                 if (setup_unzip_on_fd(archive_handle->src_fd, /*fail_if_not_detected:*/ 0) != 0)
247  err:
248                         bb_error_msg_and_die("invalid tar magic");
249                 archive_handle->offset = 0;
250                 goto again_after_align;
251 #endif
252                 bb_error_msg_and_die("invalid tar magic");
253         }
254
255         /* Do checksum on headers.
256          * POSIX says that checksum is done on unsigned bytes, but
257          * Sun and HP-UX gets it wrong... more details in
258          * GNU tar source. */
259 #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
260         sum_s = ' ' * sizeof(tar.chksum);
261 #endif
262         sum_u = ' ' * sizeof(tar.chksum);
263         for (i = 0; i < 148; i++) {
264                 sum_u += ((unsigned char*)&tar)[i];
265 #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
266                 sum_s += ((signed char*)&tar)[i];
267 #endif
268         }
269         for (i = 156; i < 512; i++) {
270                 sum_u += ((unsigned char*)&tar)[i];
271 #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
272                 sum_s += ((signed char*)&tar)[i];
273 #endif
274         }
275         /* This field does not need special treatment (getOctal) */
276         {
277                 char *endp; /* gcc likes temp var for &endp */
278                 sum = strtoul(tar.chksum, &endp, 8);
279                 if ((*endp != '\0' && *endp != ' ')
280                  || (sum_u != sum IF_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum))
281                 ) {
282                         bb_error_msg_and_die("invalid tar header checksum");
283                 }
284         }
285         /* don't use xstrtoul, tar.chksum may have leading spaces */
286         sum = strtoul(tar.chksum, NULL, 8);
287         if (sum_u != sum IF_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)) {
288                 bb_error_msg_and_die("invalid tar header checksum");
289         }
290
291         /* 0 is reserved for high perf file, treat as normal file */
292         if (!tar.typeflag) tar.typeflag = '0';
293         parse_names = (tar.typeflag >= '0' && tar.typeflag <= '7');
294
295         /* getOctal trashes subsequent field, therefore we call it
296          * on fields in reverse order */
297         if (tar.devmajor[0]) {
298                 char t = tar.prefix[0];
299                 /* we trash prefix[0] here, but we DO need it later! */
300                 unsigned minor = GET_OCTAL(tar.devminor);
301                 unsigned major = GET_OCTAL(tar.devmajor);
302                 file_header->device = makedev(major, minor);
303                 tar.prefix[0] = t;
304         }
305         file_header->link_target = NULL;
306         if (!p_linkname && parse_names && tar.linkname[0]) {
307                 file_header->link_target = xstrndup(tar.linkname, sizeof(tar.linkname));
308                 /* FIXME: what if we have non-link object with link_target? */
309                 /* Will link_target be free()ed? */
310         }
311 #if ENABLE_FEATURE_TAR_UNAME_GNAME
312         file_header->tar__uname = tar.uname[0] ? xstrndup(tar.uname, sizeof(tar.uname)) : NULL;
313         file_header->tar__gname = tar.gname[0] ? xstrndup(tar.gname, sizeof(tar.gname)) : NULL;
314 #endif
315         file_header->mtime = GET_OCTAL(tar.mtime);
316         file_header->size = GET_OCTAL(tar.size);
317         file_header->gid = GET_OCTAL(tar.gid);
318         file_header->uid = GET_OCTAL(tar.uid);
319         /* Set bits 0-11 of the files mode */
320         file_header->mode = 07777 & GET_OCTAL(tar.mode);
321
322         file_header->name = NULL;
323         if (!p_longname && parse_names) {
324                 /* we trash mode[0] here, it's ok */
325                 //tar.name[sizeof(tar.name)] = '\0'; - gcc 4.3.0 would complain
326                 tar.mode[0] = '\0';
327                 if (tar.prefix[0]) {
328                         /* and padding[0] */
329                         //tar.prefix[sizeof(tar.prefix)] = '\0'; - gcc 4.3.0 would complain
330                         tar.padding[0] = '\0';
331                         file_header->name = concat_path_file(tar.prefix, tar.name);
332                 } else
333                         file_header->name = xstrdup(tar.name);
334         }
335
336         /* Set bits 12-15 of the files mode */
337         /* (typeflag was not trashed because chksum does not use getOctal) */
338         switch (tar.typeflag) {
339         case '1': /* hardlink */
340                 /* we mark hardlinks as regular files with zero size and a link name */
341                 file_header->mode |= S_IFREG;
342                 /* on size of link fields from star(4)
343                  * ... For tar archives written by pre POSIX.1-1988
344                  * implementations, the size field usually contains the size of
345                  * the file and needs to be ignored as no data may follow this
346                  * header type.  For POSIX.1- 1988 compliant archives, the size
347                  * field needs to be 0.  For POSIX.1-2001 compliant archives,
348                  * the size field may be non zero, indicating that file data is
349                  * included in the archive.
350                  * i.e; always assume this is zero for safety.
351                  */
352                 goto size0;
353         case '7':
354         /* case 0: */
355         case '0':
356 #if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
357                 if (last_char_is(file_header->name, '/')) {
358                         goto set_dir;
359                 }
360 #endif
361                 file_header->mode |= S_IFREG;
362                 break;
363         case '2':
364                 file_header->mode |= S_IFLNK;
365                 /* have seen tarballs with size field containing
366                  * the size of the link target's name */
367  size0:
368                 file_header->size = 0;
369                 break;
370         case '3':
371                 file_header->mode |= S_IFCHR;
372                 goto size0; /* paranoia */
373         case '4':
374                 file_header->mode |= S_IFBLK;
375                 goto size0;
376         case '5':
377  IF_FEATURE_TAR_OLDGNU_COMPATIBILITY(set_dir:)
378                 file_header->mode |= S_IFDIR;
379                 goto size0;
380         case '6':
381                 file_header->mode |= S_IFIFO;
382                 goto size0;
383 #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
384         case 'L':
385                 /* free: paranoia: tar with several consecutive longnames */
386                 free(p_longname);
387                 /* For paranoia reasons we allocate extra NUL char */
388                 p_longname = xzalloc(file_header->size + 1);
389                 /* We read ASCIZ string, including NUL */
390                 xread(archive_handle->src_fd, p_longname, file_header->size);
391                 archive_handle->offset += file_header->size;
392                 /* return get_header_tar(archive_handle); */
393                 /* gcc 4.1.1 didn't optimize it into jump */
394                 /* so we will do it ourself, this also saves stack */
395                 goto again;
396         case 'K':
397                 free(p_linkname);
398                 p_linkname = xzalloc(file_header->size + 1);
399                 xread(archive_handle->src_fd, p_linkname, file_header->size);
400                 archive_handle->offset += file_header->size;
401                 /* return get_header_tar(archive_handle); */
402                 goto again;
403         case 'D':       /* GNU dump dir */
404         case 'M':       /* Continuation of multi volume archive */
405         case 'N':       /* Old GNU for names > 100 characters */
406         case 'S':       /* Sparse file */
407         case 'V':       /* Volume header */
408 #endif
409         case 'g':       /* pax global header */
410         case 'x': {     /* pax extended header */
411                 if ((uoff_t)file_header->size > 0xfffff) /* paranoia */
412                         goto skip_ext_hdr;
413                 process_pax_hdr(archive_handle, file_header->size, (tar.typeflag == 'g'));
414                 goto again_after_align;
415         }
416  skip_ext_hdr:
417         {
418                 off_t sz;
419                 bb_error_msg("warning: skipping header '%c'", tar.typeflag);
420                 sz = (file_header->size + 511) & ~(off_t)511;
421                 archive_handle->offset += sz;
422                 sz >>= 9; /* sz /= 512 but w/o contortions for signed div */
423                 while (sz--)
424                         xread(archive_handle->src_fd, &tar, 512);
425                 /* return get_header_tar(archive_handle); */
426                 goto again_after_align;
427         }
428         default:
429                 bb_error_msg_and_die("unknown typeflag: 0x%x", tar.typeflag);
430         }
431
432 #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
433         if (p_longname) {
434                 file_header->name = p_longname;
435                 p_longname = NULL;
436         }
437         if (p_linkname) {
438                 file_header->link_target = p_linkname;
439                 p_linkname = NULL;
440         }
441 #endif
442
443         /* Everything up to and including last ".." component is stripped */
444         overlapping_strcpy(file_header->name, strip_unsafe_prefix(file_header->name));
445
446         /* Strip trailing '/' in directories */
447         /* Must be done after mode is set as '/' is used to check if it's a directory */
448         cp = last_char_is(file_header->name, '/');
449
450         if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
451                 archive_handle->action_header(/*archive_handle->*/ file_header);
452                 /* Note that we kill the '/' only after action_header() */
453                 /* (like GNU tar 1.15.1: verbose mode outputs "dir/dir/") */
454                 if (cp)
455                         *cp = '\0';
456                 archive_handle->action_data(archive_handle);
457                 if (archive_handle->accept || archive_handle->reject
458                  || (archive_handle->ah_flags & ARCHIVE_REMEMBER_NAMES)
459                 ) {
460                         llist_add_to(&archive_handle->passed, file_header->name);
461                 } else /* Caller isn't interested in list of unpacked files */
462                         free(file_header->name);
463         } else {
464                 data_skip(archive_handle);
465                 free(file_header->name);
466         }
467         archive_handle->offset += file_header->size;
468
469         free(file_header->link_target);
470         /* Do not free(file_header->name)!
471          * It might be inserted in archive_handle->passed - see above */
472 #if ENABLE_FEATURE_TAR_UNAME_GNAME
473         free(file_header->tar__uname);
474         free(file_header->tar__gname);
475 #endif
476         return EXIT_SUCCESS; /* "decoded one header" */
477 }