blob: a1178579b920548d4f70ec7fa5bde0e091af478d [file] [log] [blame]
bigbiff bigbiff9c754052013-01-09 09:09:08 -05001/*
2** Copyright 1998-2003 University of Illinois Board of Trustees
3** Copyright 1998-2003 Mark D. Roth
4** All rights reserved.
5**
6** block.c - libtar code to handle tar archive header blocks
7**
8** Mark D. Roth <roth@uiuc.edu>
9** Campus Information Technologies and Educational Services
10** University of Illinois at Urbana-Champaign
11*/
12
13#include <internal.h>
bigbiff bigbiff9c754052013-01-09 09:09:08 -050014#include <errno.h>
15
16#ifdef STDC_HEADERS
17# include <string.h>
18# include <stdlib.h>
19#endif
20
21#define BIT_ISSET(bitmask, bit) ((bitmask) & (bit))
22
Vojtech Bocek25fd68d2013-08-27 03:10:10 +020023// Used to identify selinux_context in extended ('x')
24// metadata. From RedHat implementation.
25#define SELINUX_TAG "RHT.security.selinux="
26#define SELINUX_TAG_LEN 21
bigbiff bigbiff9c754052013-01-09 09:09:08 -050027
Ethan Yonker79f88bd2016-12-09 14:52:12 -060028// Used to identify e4crypt_policy in extended ('x')
29#define E4CRYPT_TAG "TWRP.security.e4crypt="
30#define E4CRYPT_TAG_LEN 22
31
Ethan Yonker71187742017-01-13 13:30:10 -060032// Used to identify Posix capabilities in extended ('x')
33#define CAPABILITIES_TAG "SCHILY.xattr.security.capability="
34#define CAPABILITIES_TAG_LEN 33
35
bigbiff bigbiff9c754052013-01-09 09:09:08 -050036/* read a header block */
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -050037/* FIXME: the return value of this function should match the return value
38 of tar_block_read(), which is a macro which references a prototype
39 that returns a ssize_t. So far, this is safe, since tar_block_read()
40 only ever reads 512 (T_BLOCKSIZE) bytes at a time, so any difference
41 in size of ssize_t and int is of negligible risk. BUT, if
42 T_BLOCKSIZE ever changes, or ever becomes a variable parameter
43 controllable by the user, all the code that calls it,
44 including this function and all code that calls it, should be
45 fixed for security reasons.
46 Thanks to Chris Palmer for the critique.
47*/
bigbiff bigbiff9c754052013-01-09 09:09:08 -050048int
49th_read_internal(TAR *t)
50{
51 int i;
52 int num_zero_blocks = 0;
53
54#ifdef DEBUG
55 printf("==> th_read_internal(TAR=\"%s\")\n", t->pathname);
56#endif
57
58 while ((i = tar_block_read(t, &(t->th_buf))) == T_BLOCKSIZE)
59 {
60 /* two all-zero blocks mark EOF */
61 if (t->th_buf.name[0] == '\0')
62 {
63 num_zero_blocks++;
64 if (!BIT_ISSET(t->options, TAR_IGNORE_EOT)
65 && num_zero_blocks >= 2)
66 return 0; /* EOF */
67 else
68 continue;
69 }
70
71 /* verify magic and version */
72 if (BIT_ISSET(t->options, TAR_CHECK_MAGIC)
73 && strncmp(t->th_buf.magic, TMAGIC, TMAGLEN - 1) != 0)
74 {
75#ifdef DEBUG
76 puts("!!! unknown magic value in tar header");
77#endif
78 return -2;
79 }
80
81 if (BIT_ISSET(t->options, TAR_CHECK_VERSION)
82 && strncmp(t->th_buf.version, TVERSION, TVERSLEN) != 0)
83 {
84#ifdef DEBUG
85 puts("!!! unknown version value in tar header");
86#endif
87 return -2;
88 }
89
90 /* check chksum */
91 if (!BIT_ISSET(t->options, TAR_IGNORE_CRC)
92 && !th_crc_ok(t))
93 {
94#ifdef DEBUG
95 puts("!!! tar header checksum error");
96#endif
97 return -2;
98 }
99
100 break;
101 }
102
103#ifdef DEBUG
104 printf("<== th_read_internal(): returning %d\n", i);
105#endif
106 return i;
107}
108
109
110/* wrapper function for th_read_internal() to handle GNU extensions */
111int
112th_read(TAR *t)
113{
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500114 int i;
115 size_t sz, j, blocks;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500116 char *ptr;
117
118#ifdef DEBUG
119 printf("==> th_read(t=0x%lx)\n", t);
120#endif
121
122 if (t->th_buf.gnu_longname != NULL)
123 free(t->th_buf.gnu_longname);
124 if (t->th_buf.gnu_longlink != NULL)
125 free(t->th_buf.gnu_longlink);
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200126#ifdef HAVE_SELINUX
127 if (t->th_buf.selinux_context != NULL)
128 free(t->th_buf.selinux_context);
129#endif
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600130#ifdef HAVE_EXT4_CRYPT
131 if (t->th_buf.e4crypt_policy != NULL) {
132 free(t->th_buf.e4crypt_policy);
133 }
134#endif
Ethan Yonker71187742017-01-13 13:30:10 -0600135 if (t->th_buf.has_cap_data)
136 {
137 memset(&t->th_buf.cap_data, 0, sizeof(struct vfs_cap_data));
138 t->th_buf.has_cap_data = 0;
139 }
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200140
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500141 memset(&(t->th_buf), 0, sizeof(struct tar_header));
142
143 i = th_read_internal(t);
144 if (i == 0)
145 return 1;
146 else if (i != T_BLOCKSIZE)
147 {
148 if (i != -1)
149 errno = EINVAL;
150 return -1;
151 }
152
153 /* check for GNU long link extention */
154 if (TH_ISLONGLINK(t))
155 {
156 sz = th_get_size(t);
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500157 blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
158 if (blocks > ((size_t)-1 / T_BLOCKSIZE))
159 {
160 errno = E2BIG;
161 return -1;
162 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500163#ifdef DEBUG
164 printf(" th_read(): GNU long linkname detected "
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500165 "(%ld bytes, %d blocks)\n", sz, blocks);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500166#endif
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500167 t->th_buf.gnu_longlink = (char *)malloc(blocks * T_BLOCKSIZE);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500168 if (t->th_buf.gnu_longlink == NULL)
169 return -1;
170
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500171 for (j = 0, ptr = t->th_buf.gnu_longlink; j < blocks;
172 j++, ptr += T_BLOCKSIZE)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500173 {
174#ifdef DEBUG
175 printf(" th_read(): reading long linkname "
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500176 "(%d blocks left, ptr == %ld)\n", blocks-j, ptr);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500177#endif
178 i = tar_block_read(t, ptr);
179 if (i != T_BLOCKSIZE)
180 {
181 if (i != -1)
182 errno = EINVAL;
183 return -1;
184 }
185#ifdef DEBUG
186 printf(" th_read(): read block == \"%s\"\n", ptr);
187#endif
188 }
189#ifdef DEBUG
190 printf(" th_read(): t->th_buf.gnu_longlink == \"%s\"\n",
191 t->th_buf.gnu_longlink);
192#endif
193
194 i = th_read_internal(t);
195 if (i != T_BLOCKSIZE)
196 {
197 if (i != -1)
198 errno = EINVAL;
199 return -1;
200 }
201 }
202
203 /* check for GNU long name extention */
204 if (TH_ISLONGNAME(t))
205 {
206 sz = th_get_size(t);
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500207 blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
208 if (blocks > ((size_t)-1 / T_BLOCKSIZE))
209 {
210 errno = E2BIG;
211 return -1;
212 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500213#ifdef DEBUG
214 printf(" th_read(): GNU long filename detected "
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500215 "(%ld bytes, %d blocks)\n", sz, blocks);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500216#endif
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500217 t->th_buf.gnu_longname = (char *)malloc(blocks * T_BLOCKSIZE);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500218 if (t->th_buf.gnu_longname == NULL)
219 return -1;
220
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500221 for (j = 0, ptr = t->th_buf.gnu_longname; j < blocks;
222 j++, ptr += T_BLOCKSIZE)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500223 {
224#ifdef DEBUG
225 printf(" th_read(): reading long filename "
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500226 "(%d blocks left, ptr == %ld)\n", blocks-j, ptr);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500227#endif
228 i = tar_block_read(t, ptr);
229 if (i != T_BLOCKSIZE)
230 {
231 if (i != -1)
232 errno = EINVAL;
233 return -1;
234 }
235#ifdef DEBUG
236 printf(" th_read(): read block == \"%s\"\n", ptr);
237#endif
238 }
239#ifdef DEBUG
240 printf(" th_read(): t->th_buf.gnu_longname == \"%s\"\n",
241 t->th_buf.gnu_longname);
242#endif
243
244 i = th_read_internal(t);
245 if (i != T_BLOCKSIZE)
246 {
247 if (i != -1)
248 errno = EINVAL;
249 return -1;
250 }
251 }
252
Ethan Yonker71187742017-01-13 13:30:10 -0600253 // Extended headers (selinux contexts, posix file capabilities, ext4 encryption policies)
254 while(TH_ISEXTHEADER(t) || TH_ISPOLHEADER(t))
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200255 {
256 sz = th_get_size(t);
257
258 if(sz >= T_BLOCKSIZE) // Not supported
259 {
260#ifdef DEBUG
261 printf(" th_read(): Extended header is too long!\n");
262#endif
263 }
264 else
265 {
266 char buf[T_BLOCKSIZE];
267 i = tar_block_read(t, buf);
268 if (i != T_BLOCKSIZE)
269 {
270 if (i != -1)
271 errno = EINVAL;
272 return -1;
273 }
274
275 // To be sure
276 buf[T_BLOCKSIZE-1] = 0;
277
278 int len = strlen(buf);
Ethan Yonker71187742017-01-13 13:30:10 -0600279 // posix capabilities
280 char *start = strstr(buf, CAPABILITIES_TAG);
281 if(start && start+CAPABILITIES_TAG_LEN < buf+len)
282 {
283 start += CAPABILITIES_TAG_LEN;
284 memcpy(&t->th_buf.cap_data, start, sizeof(struct vfs_cap_data));
285 t->th_buf.has_cap_data = 1;
286#ifdef DEBUG
287 printf(" th_read(): Posix capabilities detected\n");
288#endif
289 } // end posix capabilities
290#ifdef HAVE_SELINUX // selinux contexts
291 start = strstr(buf, SELINUX_TAG);
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200292 if(start && start+SELINUX_TAG_LEN < buf+len)
293 {
294 start += SELINUX_TAG_LEN;
295 char *end = strchr(start, '\n');
296 if(end)
297 {
298 t->th_buf.selinux_context = strndup(start, end-start);
299#ifdef DEBUG
300 printf(" th_read(): SELinux context xattr detected: %s\n", t->th_buf.selinux_context);
301#endif
302 }
303 }
Ethan Yonker71187742017-01-13 13:30:10 -0600304#endif // HAVE_SELINUX
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600305#ifdef HAVE_EXT4_CRYPT
Ethan Yonker71187742017-01-13 13:30:10 -0600306 start = strstr(buf, E4CRYPT_TAG);
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600307 if(start && start+E4CRYPT_TAG_LEN < buf+len)
308 {
309 start += E4CRYPT_TAG_LEN;
310 char *end = strchr(start, '\n');
311 if(end)
312 {
313 t->th_buf.e4crypt_policy = strndup(start, end-start);
314#ifdef DEBUG
315 printf(" th_read(): E4Crypt policy detected: %s\n", t->th_buf.e4crypt_policy);
316#endif
317 }
318 }
Ethan Yonker71187742017-01-13 13:30:10 -0600319#endif // HAVE_EXT4_CRYPT
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600320 }
321
322 i = th_read_internal(t);
323 if (i != T_BLOCKSIZE)
324 {
325 if (i != -1)
326 errno = EINVAL;
327 return -1;
328 }
329 }
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600330
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500331 return 0;
332}
333
Ethan Yonker71187742017-01-13 13:30:10 -0600334/* write an extended block */
335static int
336th_write_extended(TAR *t, char* buf, uint64_t sz)
337{
338 char type2;
339 uint64_t sz2;
340 int i;
341
342 /* save old size and type */
343 type2 = t->th_buf.typeflag;
344 sz2 = th_get_size(t);
345
346 /* write out initial header block with fake size and type */
347 t->th_buf.typeflag = TH_EXT_TYPE;
348
349 if(sz >= T_BLOCKSIZE) // impossible
350 {
351 errno = EINVAL;
352 return -1;
353 }
354
355 th_set_size(t, sz);
356 th_finish(t);
357 i = tar_block_write(t, &(t->th_buf));
358 if (i != T_BLOCKSIZE)
359 {
360 if (i != -1)
361 errno = EINVAL;
362 return -1;
363 }
364
365 i = tar_block_write(t, buf);
366 if (i != T_BLOCKSIZE)
367 {
368 if (i != -1)
369 errno = EINVAL;
370 return -1;
371 }
372
373 /* reset type and size to original values */
374 t->th_buf.typeflag = type2;
375 th_set_size(t, sz2);
376 memset(buf, 0, T_BLOCKSIZE);
377 return 0;
378}
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500379
380/* write a header block */
381int
382th_write(TAR *t)
383{
384 int i, j;
385 char type2;
Ethan Yonker71187742017-01-13 13:30:10 -0600386 uint64_t sz, sz2, total_sz = 0;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500387 char *ptr;
388 char buf[T_BLOCKSIZE];
389
390#ifdef DEBUG
391 printf("==> th_write(TAR=\"%s\")\n", t->pathname);
392 th_print(t);
393#endif
394
395 if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL)
396 {
397#ifdef DEBUG
398 printf("th_write(): using gnu_longlink (\"%s\")\n",
399 t->th_buf.gnu_longlink);
400#endif
401 /* save old size and type */
402 type2 = t->th_buf.typeflag;
403 sz2 = th_get_size(t);
404
405 /* write out initial header block with fake size and type */
406 t->th_buf.typeflag = GNU_LONGLINK_TYPE;
407 sz = strlen(t->th_buf.gnu_longlink);
408 th_set_size(t, sz);
409 th_finish(t);
410 i = tar_block_write(t, &(t->th_buf));
411 if (i != T_BLOCKSIZE)
412 {
413 if (i != -1)
414 errno = EINVAL;
415 return -1;
416 }
417
418 /* write out extra blocks containing long name */
419 for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
420 ptr = t->th_buf.gnu_longlink; j > 1;
421 j--, ptr += T_BLOCKSIZE)
422 {
423 i = tar_block_write(t, ptr);
424 if (i != T_BLOCKSIZE)
425 {
426 if (i != -1)
427 errno = EINVAL;
428 return -1;
429 }
430 }
431 memset(buf, 0, T_BLOCKSIZE);
432 strncpy(buf, ptr, T_BLOCKSIZE);
433 i = tar_block_write(t, &buf);
434 if (i != T_BLOCKSIZE)
435 {
436 if (i != -1)
437 errno = EINVAL;
438 return -1;
439 }
440
441 /* reset type and size to original values */
442 t->th_buf.typeflag = type2;
443 th_set_size(t, sz2);
444 }
445
446 if ((t->options & TAR_GNU) && t->th_buf.gnu_longname != NULL)
447 {
448#ifdef DEBUG
449 printf("th_write(): using gnu_longname (\"%s\")\n",
450 t->th_buf.gnu_longname);
451#endif
452 /* save old size and type */
453 type2 = t->th_buf.typeflag;
454 sz2 = th_get_size(t);
455
456 /* write out initial header block with fake size and type */
457 t->th_buf.typeflag = GNU_LONGNAME_TYPE;
458 sz = strlen(t->th_buf.gnu_longname);
459 th_set_size(t, sz);
460 th_finish(t);
461 i = tar_block_write(t, &(t->th_buf));
462 if (i != T_BLOCKSIZE)
463 {
464 if (i != -1)
465 errno = EINVAL;
466 return -1;
467 }
468
469 /* write out extra blocks containing long name */
470 for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
471 ptr = t->th_buf.gnu_longname; j > 1;
472 j--, ptr += T_BLOCKSIZE)
473 {
474 i = tar_block_write(t, ptr);
475 if (i != T_BLOCKSIZE)
476 {
477 if (i != -1)
478 errno = EINVAL;
479 return -1;
480 }
481 }
482 memset(buf, 0, T_BLOCKSIZE);
483 strncpy(buf, ptr, T_BLOCKSIZE);
484 i = tar_block_write(t, &buf);
485 if (i != T_BLOCKSIZE)
486 {
487 if (i != -1)
488 errno = EINVAL;
489 return -1;
490 }
491
492 /* reset type and size to original values */
493 t->th_buf.typeflag = type2;
494 th_set_size(t, sz2);
495 }
496
Ethan Yonker71187742017-01-13 13:30:10 -0600497 memset(buf, 0, T_BLOCKSIZE);
498 ptr = buf;
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200499#ifdef HAVE_SELINUX
500 if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL)
501 {
502#ifdef DEBUG
503 printf("th_write(): using selinux_context (\"%s\")\n",
504 t->th_buf.selinux_context);
505#endif
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200506 /* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *content* *newline* */
507 // size newline
508 sz = SELINUX_TAG_LEN + strlen(t->th_buf.selinux_context) + 3 + 1;
509
510 if(sz >= 100) // another ascci digit for size
511 ++sz;
512
Ethan Yonker71187742017-01-13 13:30:10 -0600513 total_sz += sz;
514 snprintf(ptr, T_BLOCKSIZE, "%d "SELINUX_TAG"%s\n", (int)sz, t->th_buf.selinux_context);
515 ptr += sz;
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200516 }
517#endif
518
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600519#ifdef HAVE_EXT4_CRYPT
520 if((t->options & TAR_STORE_EXT4_POL) && t->th_buf.e4crypt_policy != NULL)
521 {
522#ifdef DEBUG
523 printf("th_write(): using e4crypt_policy %s\n",
524 t->th_buf.e4crypt_policy);
525#endif
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600526 /* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *content* *newline* */
527 // size newline
528 sz = E4CRYPT_TAG_LEN + EXT4_KEY_DESCRIPTOR_HEX + 3 + 1;
529
530 if(sz >= 100) // another ascci digit for size
531 ++sz;
532
Ethan Yonker71187742017-01-13 13:30:10 -0600533 if (total_sz + sz >= T_BLOCKSIZE)
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600534 {
Ethan Yonker71187742017-01-13 13:30:10 -0600535 if (th_write_extended(t, &buf, total_sz))
536 return -1;
537 ptr = buf;
538 total_sz = sz;
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600539 }
Ethan Yonker71187742017-01-13 13:30:10 -0600540 else
541 total_sz += sz;
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600542
Ethan Yonker71187742017-01-13 13:30:10 -0600543 snprintf(ptr, T_BLOCKSIZE, "%d "E4CRYPT_TAG"%s", (int)sz, t->th_buf.e4crypt_policy);
544 char *nlptr = ptr + sz - 1;
545 *nlptr = '\n';
546 ptr += sz;
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600547 }
548#endif
549
Ethan Yonker71187742017-01-13 13:30:10 -0600550 if((t->options & TAR_STORE_POSIX_CAP) && t->th_buf.has_cap_data)
551 {
552#ifdef DEBUG
553 printf("th_write(): has a posix capability\n");
554#endif
555 sz = CAPABILITIES_TAG_LEN + sizeof(struct vfs_cap_data) + 3 + 1;
556
557 if(sz >= 100) // another ascci digit for size
558 ++sz;
559
560 if (total_sz + sz >= T_BLOCKSIZE)
561 {
562 if (th_write_extended(t, &buf, total_sz))
563 return -1;
564 ptr = buf;
565 total_sz = sz;
566 }
567 else
568 total_sz += sz;
569
570 snprintf(ptr, T_BLOCKSIZE, "%d "CAPABILITIES_TAG, (int)sz);
571 memcpy(ptr + CAPABILITIES_TAG_LEN + 3, &t->th_buf.cap_data, sizeof(struct vfs_cap_data));
572 char *nlptr = ptr + sz - 1;
573 *nlptr = '\n';
574 ptr += sz;
575 }
576 if (total_sz > 0 && th_write_extended(t, &buf, total_sz)) // write any outstanding tar extended header
577 return -1;
578
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500579 th_finish(t);
580
581#ifdef DEBUG
582 /* print tar header */
583 th_print(t);
584#endif
585
586 i = tar_block_write(t, &(t->th_buf));
587 if (i != T_BLOCKSIZE)
588 {
589 if (i != -1)
590 errno = EINVAL;
591 return -1;
592 }
593
594#ifdef DEBUG
595 puts("th_write(): returning 0");
596#endif
597 return 0;
598}
599
600