blob: 1b3ba824260fafa051b01ae17d5baa62a305b31c [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 if (t->th_buf.selinux_context != NULL)
127 free(t->th_buf.selinux_context);
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600128#ifdef HAVE_EXT4_CRYPT
129 if (t->th_buf.e4crypt_policy != NULL) {
130 free(t->th_buf.e4crypt_policy);
131 }
132#endif
Ethan Yonker71187742017-01-13 13:30:10 -0600133 if (t->th_buf.has_cap_data)
134 {
135 memset(&t->th_buf.cap_data, 0, sizeof(struct vfs_cap_data));
136 t->th_buf.has_cap_data = 0;
137 }
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200138
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500139 memset(&(t->th_buf), 0, sizeof(struct tar_header));
140
141 i = th_read_internal(t);
142 if (i == 0)
143 return 1;
144 else if (i != T_BLOCKSIZE)
145 {
146 if (i != -1)
147 errno = EINVAL;
148 return -1;
149 }
150
151 /* check for GNU long link extention */
152 if (TH_ISLONGLINK(t))
153 {
154 sz = th_get_size(t);
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500155 blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
156 if (blocks > ((size_t)-1 / T_BLOCKSIZE))
157 {
158 errno = E2BIG;
159 return -1;
160 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500161#ifdef DEBUG
162 printf(" th_read(): GNU long linkname detected "
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500163 "(%ld bytes, %d blocks)\n", sz, blocks);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500164#endif
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500165 t->th_buf.gnu_longlink = (char *)malloc(blocks * T_BLOCKSIZE);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500166 if (t->th_buf.gnu_longlink == NULL)
167 return -1;
168
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500169 for (j = 0, ptr = t->th_buf.gnu_longlink; j < blocks;
170 j++, ptr += T_BLOCKSIZE)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500171 {
172#ifdef DEBUG
173 printf(" th_read(): reading long linkname "
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500174 "(%d blocks left, ptr == %ld)\n", blocks-j, ptr);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500175#endif
176 i = tar_block_read(t, ptr);
177 if (i != T_BLOCKSIZE)
178 {
179 if (i != -1)
180 errno = EINVAL;
181 return -1;
182 }
183#ifdef DEBUG
184 printf(" th_read(): read block == \"%s\"\n", ptr);
185#endif
186 }
187#ifdef DEBUG
188 printf(" th_read(): t->th_buf.gnu_longlink == \"%s\"\n",
189 t->th_buf.gnu_longlink);
190#endif
191
192 i = th_read_internal(t);
193 if (i != T_BLOCKSIZE)
194 {
195 if (i != -1)
196 errno = EINVAL;
197 return -1;
198 }
199 }
200
201 /* check for GNU long name extention */
202 if (TH_ISLONGNAME(t))
203 {
204 sz = th_get_size(t);
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500205 blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
206 if (blocks > ((size_t)-1 / T_BLOCKSIZE))
207 {
208 errno = E2BIG;
209 return -1;
210 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500211#ifdef DEBUG
212 printf(" th_read(): GNU long filename detected "
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500213 "(%ld bytes, %d blocks)\n", sz, blocks);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500214#endif
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500215 t->th_buf.gnu_longname = (char *)malloc(blocks * T_BLOCKSIZE);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500216 if (t->th_buf.gnu_longname == NULL)
217 return -1;
218
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500219 for (j = 0, ptr = t->th_buf.gnu_longname; j < blocks;
220 j++, ptr += T_BLOCKSIZE)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500221 {
222#ifdef DEBUG
223 printf(" th_read(): reading long filename "
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500224 "(%d blocks left, ptr == %ld)\n", blocks-j, ptr);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500225#endif
226 i = tar_block_read(t, ptr);
227 if (i != T_BLOCKSIZE)
228 {
229 if (i != -1)
230 errno = EINVAL;
231 return -1;
232 }
233#ifdef DEBUG
234 printf(" th_read(): read block == \"%s\"\n", ptr);
235#endif
236 }
237#ifdef DEBUG
238 printf(" th_read(): t->th_buf.gnu_longname == \"%s\"\n",
239 t->th_buf.gnu_longname);
240#endif
241
242 i = th_read_internal(t);
243 if (i != T_BLOCKSIZE)
244 {
245 if (i != -1)
246 errno = EINVAL;
247 return -1;
248 }
249 }
250
Ethan Yonker71187742017-01-13 13:30:10 -0600251 // Extended headers (selinux contexts, posix file capabilities, ext4 encryption policies)
252 while(TH_ISEXTHEADER(t) || TH_ISPOLHEADER(t))
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200253 {
254 sz = th_get_size(t);
255
256 if(sz >= T_BLOCKSIZE) // Not supported
257 {
258#ifdef DEBUG
259 printf(" th_read(): Extended header is too long!\n");
260#endif
261 }
262 else
263 {
264 char buf[T_BLOCKSIZE];
265 i = tar_block_read(t, buf);
266 if (i != T_BLOCKSIZE)
267 {
268 if (i != -1)
269 errno = EINVAL;
270 return -1;
271 }
272
273 // To be sure
274 buf[T_BLOCKSIZE-1] = 0;
275
276 int len = strlen(buf);
Ethan Yonker71187742017-01-13 13:30:10 -0600277 // posix capabilities
278 char *start = strstr(buf, CAPABILITIES_TAG);
279 if(start && start+CAPABILITIES_TAG_LEN < buf+len)
280 {
281 start += CAPABILITIES_TAG_LEN;
282 memcpy(&t->th_buf.cap_data, start, sizeof(struct vfs_cap_data));
283 t->th_buf.has_cap_data = 1;
284#ifdef DEBUG
285 printf(" th_read(): Posix capabilities detected\n");
286#endif
287 } // end posix capabilities
Matt Mower87413642017-01-17 21:14:46 -0600288 // selinux contexts
Ethan Yonker71187742017-01-13 13:30:10 -0600289 start = strstr(buf, SELINUX_TAG);
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200290 if(start && start+SELINUX_TAG_LEN < buf+len)
291 {
292 start += SELINUX_TAG_LEN;
293 char *end = strchr(start, '\n');
294 if(end)
295 {
296 t->th_buf.selinux_context = strndup(start, end-start);
297#ifdef DEBUG
298 printf(" th_read(): SELinux context xattr detected: %s\n", t->th_buf.selinux_context);
299#endif
300 }
Matt Mower87413642017-01-17 21:14:46 -0600301 } // end selinux contexts
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600302#ifdef HAVE_EXT4_CRYPT
Ethan Yonker71187742017-01-13 13:30:10 -0600303 start = strstr(buf, E4CRYPT_TAG);
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600304 if(start && start+E4CRYPT_TAG_LEN < buf+len)
305 {
306 start += E4CRYPT_TAG_LEN;
307 char *end = strchr(start, '\n');
308 if(end)
309 {
310 t->th_buf.e4crypt_policy = strndup(start, end-start);
311#ifdef DEBUG
312 printf(" th_read(): E4Crypt policy detected: %s\n", t->th_buf.e4crypt_policy);
313#endif
314 }
315 }
Ethan Yonker71187742017-01-13 13:30:10 -0600316#endif // HAVE_EXT4_CRYPT
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600317 }
318
319 i = th_read_internal(t);
320 if (i != T_BLOCKSIZE)
321 {
322 if (i != -1)
323 errno = EINVAL;
324 return -1;
325 }
326 }
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600327
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500328 return 0;
329}
330
Ethan Yonker71187742017-01-13 13:30:10 -0600331/* write an extended block */
332static int
333th_write_extended(TAR *t, char* buf, uint64_t sz)
334{
335 char type2;
336 uint64_t sz2;
337 int i;
338
339 /* save old size and type */
340 type2 = t->th_buf.typeflag;
341 sz2 = th_get_size(t);
342
343 /* write out initial header block with fake size and type */
344 t->th_buf.typeflag = TH_EXT_TYPE;
345
346 if(sz >= T_BLOCKSIZE) // impossible
347 {
348 errno = EINVAL;
349 return -1;
350 }
351
352 th_set_size(t, sz);
353 th_finish(t);
354 i = tar_block_write(t, &(t->th_buf));
355 if (i != T_BLOCKSIZE)
356 {
357 if (i != -1)
358 errno = EINVAL;
359 return -1;
360 }
361
362 i = tar_block_write(t, buf);
363 if (i != T_BLOCKSIZE)
364 {
365 if (i != -1)
366 errno = EINVAL;
367 return -1;
368 }
369
370 /* reset type and size to original values */
371 t->th_buf.typeflag = type2;
372 th_set_size(t, sz2);
373 memset(buf, 0, T_BLOCKSIZE);
374 return 0;
375}
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500376
377/* write a header block */
378int
379th_write(TAR *t)
380{
381 int i, j;
382 char type2;
Ethan Yonker71187742017-01-13 13:30:10 -0600383 uint64_t sz, sz2, total_sz = 0;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500384 char *ptr;
385 char buf[T_BLOCKSIZE];
386
387#ifdef DEBUG
388 printf("==> th_write(TAR=\"%s\")\n", t->pathname);
389 th_print(t);
390#endif
391
392 if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL)
393 {
394#ifdef DEBUG
395 printf("th_write(): using gnu_longlink (\"%s\")\n",
396 t->th_buf.gnu_longlink);
397#endif
398 /* save old size and type */
399 type2 = t->th_buf.typeflag;
400 sz2 = th_get_size(t);
401
402 /* write out initial header block with fake size and type */
403 t->th_buf.typeflag = GNU_LONGLINK_TYPE;
404 sz = strlen(t->th_buf.gnu_longlink);
405 th_set_size(t, sz);
406 th_finish(t);
407 i = tar_block_write(t, &(t->th_buf));
408 if (i != T_BLOCKSIZE)
409 {
410 if (i != -1)
411 errno = EINVAL;
412 return -1;
413 }
414
415 /* write out extra blocks containing long name */
416 for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
417 ptr = t->th_buf.gnu_longlink; j > 1;
418 j--, ptr += T_BLOCKSIZE)
419 {
420 i = tar_block_write(t, ptr);
421 if (i != T_BLOCKSIZE)
422 {
423 if (i != -1)
424 errno = EINVAL;
425 return -1;
426 }
427 }
428 memset(buf, 0, T_BLOCKSIZE);
429 strncpy(buf, ptr, T_BLOCKSIZE);
430 i = tar_block_write(t, &buf);
431 if (i != T_BLOCKSIZE)
432 {
433 if (i != -1)
434 errno = EINVAL;
435 return -1;
436 }
437
438 /* reset type and size to original values */
439 t->th_buf.typeflag = type2;
440 th_set_size(t, sz2);
441 }
442
443 if ((t->options & TAR_GNU) && t->th_buf.gnu_longname != NULL)
444 {
445#ifdef DEBUG
446 printf("th_write(): using gnu_longname (\"%s\")\n",
447 t->th_buf.gnu_longname);
448#endif
449 /* save old size and type */
450 type2 = t->th_buf.typeflag;
451 sz2 = th_get_size(t);
452
453 /* write out initial header block with fake size and type */
454 t->th_buf.typeflag = GNU_LONGNAME_TYPE;
455 sz = strlen(t->th_buf.gnu_longname);
456 th_set_size(t, sz);
457 th_finish(t);
458 i = tar_block_write(t, &(t->th_buf));
459 if (i != T_BLOCKSIZE)
460 {
461 if (i != -1)
462 errno = EINVAL;
463 return -1;
464 }
465
466 /* write out extra blocks containing long name */
467 for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
468 ptr = t->th_buf.gnu_longname; j > 1;
469 j--, ptr += T_BLOCKSIZE)
470 {
471 i = tar_block_write(t, ptr);
472 if (i != T_BLOCKSIZE)
473 {
474 if (i != -1)
475 errno = EINVAL;
476 return -1;
477 }
478 }
479 memset(buf, 0, T_BLOCKSIZE);
480 strncpy(buf, ptr, T_BLOCKSIZE);
481 i = tar_block_write(t, &buf);
482 if (i != T_BLOCKSIZE)
483 {
484 if (i != -1)
485 errno = EINVAL;
486 return -1;
487 }
488
489 /* reset type and size to original values */
490 t->th_buf.typeflag = type2;
491 th_set_size(t, sz2);
492 }
493
Ethan Yonker71187742017-01-13 13:30:10 -0600494 memset(buf, 0, T_BLOCKSIZE);
495 ptr = buf;
Matt Mower87413642017-01-17 21:14:46 -0600496
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200497 if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL)
498 {
499#ifdef DEBUG
500 printf("th_write(): using selinux_context (\"%s\")\n",
501 t->th_buf.selinux_context);
502#endif
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200503 /* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *content* *newline* */
504 // size newline
505 sz = SELINUX_TAG_LEN + strlen(t->th_buf.selinux_context) + 3 + 1;
506
507 if(sz >= 100) // another ascci digit for size
508 ++sz;
509
Ethan Yonker71187742017-01-13 13:30:10 -0600510 total_sz += sz;
511 snprintf(ptr, T_BLOCKSIZE, "%d "SELINUX_TAG"%s\n", (int)sz, t->th_buf.selinux_context);
512 ptr += sz;
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200513 }
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200514
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600515#ifdef HAVE_EXT4_CRYPT
516 if((t->options & TAR_STORE_EXT4_POL) && t->th_buf.e4crypt_policy != NULL)
517 {
518#ifdef DEBUG
519 printf("th_write(): using e4crypt_policy %s\n",
520 t->th_buf.e4crypt_policy);
521#endif
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600522 /* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *content* *newline* */
523 // size newline
524 sz = E4CRYPT_TAG_LEN + EXT4_KEY_DESCRIPTOR_HEX + 3 + 1;
525
526 if(sz >= 100) // another ascci digit for size
527 ++sz;
528
Ethan Yonker71187742017-01-13 13:30:10 -0600529 if (total_sz + sz >= T_BLOCKSIZE)
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600530 {
Ethan Yonker71187742017-01-13 13:30:10 -0600531 if (th_write_extended(t, &buf, total_sz))
532 return -1;
533 ptr = buf;
534 total_sz = sz;
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600535 }
Ethan Yonker71187742017-01-13 13:30:10 -0600536 else
537 total_sz += sz;
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600538
Ethan Yonker71187742017-01-13 13:30:10 -0600539 snprintf(ptr, T_BLOCKSIZE, "%d "E4CRYPT_TAG"%s", (int)sz, t->th_buf.e4crypt_policy);
540 char *nlptr = ptr + sz - 1;
541 *nlptr = '\n';
542 ptr += sz;
Ethan Yonker79f88bd2016-12-09 14:52:12 -0600543 }
544#endif
545
Ethan Yonker71187742017-01-13 13:30:10 -0600546 if((t->options & TAR_STORE_POSIX_CAP) && t->th_buf.has_cap_data)
547 {
548#ifdef DEBUG
549 printf("th_write(): has a posix capability\n");
550#endif
551 sz = CAPABILITIES_TAG_LEN + sizeof(struct vfs_cap_data) + 3 + 1;
552
553 if(sz >= 100) // another ascci digit for size
554 ++sz;
555
556 if (total_sz + sz >= T_BLOCKSIZE)
557 {
558 if (th_write_extended(t, &buf, total_sz))
559 return -1;
560 ptr = buf;
561 total_sz = sz;
562 }
563 else
564 total_sz += sz;
565
566 snprintf(ptr, T_BLOCKSIZE, "%d "CAPABILITIES_TAG, (int)sz);
567 memcpy(ptr + CAPABILITIES_TAG_LEN + 3, &t->th_buf.cap_data, sizeof(struct vfs_cap_data));
568 char *nlptr = ptr + sz - 1;
569 *nlptr = '\n';
570 ptr += sz;
571 }
572 if (total_sz > 0 && th_write_extended(t, &buf, total_sz)) // write any outstanding tar extended header
573 return -1;
574
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500575 th_finish(t);
576
577#ifdef DEBUG
578 /* print tar header */
579 th_print(t);
580#endif
581
582 i = tar_block_write(t, &(t->th_buf));
583 if (i != T_BLOCKSIZE)
584 {
585 if (i != -1)
586 errno = EINVAL;
587 return -1;
588 }
589
590#ifdef DEBUG
591 puts("th_write(): returning 0");
592#endif
593 return 0;
594}
595
596