blob: 5d3c9d82647ee7394876d14c9a8811f245cfe3e5 [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
28/* read a header block */
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -050029/* FIXME: the return value of this function should match the return value
30 of tar_block_read(), which is a macro which references a prototype
31 that returns a ssize_t. So far, this is safe, since tar_block_read()
32 only ever reads 512 (T_BLOCKSIZE) bytes at a time, so any difference
33 in size of ssize_t and int is of negligible risk. BUT, if
34 T_BLOCKSIZE ever changes, or ever becomes a variable parameter
35 controllable by the user, all the code that calls it,
36 including this function and all code that calls it, should be
37 fixed for security reasons.
38 Thanks to Chris Palmer for the critique.
39*/
bigbiff bigbiff9c754052013-01-09 09:09:08 -050040int
41th_read_internal(TAR *t)
42{
43 int i;
44 int num_zero_blocks = 0;
45
46#ifdef DEBUG
47 printf("==> th_read_internal(TAR=\"%s\")\n", t->pathname);
48#endif
49
50 while ((i = tar_block_read(t, &(t->th_buf))) == T_BLOCKSIZE)
51 {
52 /* two all-zero blocks mark EOF */
53 if (t->th_buf.name[0] == '\0')
54 {
55 num_zero_blocks++;
56 if (!BIT_ISSET(t->options, TAR_IGNORE_EOT)
57 && num_zero_blocks >= 2)
58 return 0; /* EOF */
59 else
60 continue;
61 }
62
63 /* verify magic and version */
64 if (BIT_ISSET(t->options, TAR_CHECK_MAGIC)
65 && strncmp(t->th_buf.magic, TMAGIC, TMAGLEN - 1) != 0)
66 {
67#ifdef DEBUG
68 puts("!!! unknown magic value in tar header");
69#endif
70 return -2;
71 }
72
73 if (BIT_ISSET(t->options, TAR_CHECK_VERSION)
74 && strncmp(t->th_buf.version, TVERSION, TVERSLEN) != 0)
75 {
76#ifdef DEBUG
77 puts("!!! unknown version value in tar header");
78#endif
79 return -2;
80 }
81
82 /* check chksum */
83 if (!BIT_ISSET(t->options, TAR_IGNORE_CRC)
84 && !th_crc_ok(t))
85 {
86#ifdef DEBUG
87 puts("!!! tar header checksum error");
88#endif
89 return -2;
90 }
91
92 break;
93 }
94
95#ifdef DEBUG
96 printf("<== th_read_internal(): returning %d\n", i);
97#endif
98 return i;
99}
100
101
102/* wrapper function for th_read_internal() to handle GNU extensions */
103int
104th_read(TAR *t)
105{
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500106 int i;
107 size_t sz, j, blocks;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500108 char *ptr;
109
110#ifdef DEBUG
111 printf("==> th_read(t=0x%lx)\n", t);
112#endif
113
114 if (t->th_buf.gnu_longname != NULL)
115 free(t->th_buf.gnu_longname);
116 if (t->th_buf.gnu_longlink != NULL)
117 free(t->th_buf.gnu_longlink);
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200118#ifdef HAVE_SELINUX
119 if (t->th_buf.selinux_context != NULL)
120 free(t->th_buf.selinux_context);
121#endif
122
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500123 memset(&(t->th_buf), 0, sizeof(struct tar_header));
124
125 i = th_read_internal(t);
126 if (i == 0)
127 return 1;
128 else if (i != T_BLOCKSIZE)
129 {
130 if (i != -1)
131 errno = EINVAL;
132 return -1;
133 }
134
135 /* check for GNU long link extention */
136 if (TH_ISLONGLINK(t))
137 {
138 sz = th_get_size(t);
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500139 blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
140 if (blocks > ((size_t)-1 / T_BLOCKSIZE))
141 {
142 errno = E2BIG;
143 return -1;
144 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500145#ifdef DEBUG
146 printf(" th_read(): GNU long linkname detected "
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500147 "(%ld bytes, %d blocks)\n", sz, blocks);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500148#endif
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500149 t->th_buf.gnu_longlink = (char *)malloc(blocks * T_BLOCKSIZE);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500150 if (t->th_buf.gnu_longlink == NULL)
151 return -1;
152
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500153 for (j = 0, ptr = t->th_buf.gnu_longlink; j < blocks;
154 j++, ptr += T_BLOCKSIZE)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500155 {
156#ifdef DEBUG
157 printf(" th_read(): reading long linkname "
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500158 "(%d blocks left, ptr == %ld)\n", blocks-j, ptr);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500159#endif
160 i = tar_block_read(t, ptr);
161 if (i != T_BLOCKSIZE)
162 {
163 if (i != -1)
164 errno = EINVAL;
165 return -1;
166 }
167#ifdef DEBUG
168 printf(" th_read(): read block == \"%s\"\n", ptr);
169#endif
170 }
171#ifdef DEBUG
172 printf(" th_read(): t->th_buf.gnu_longlink == \"%s\"\n",
173 t->th_buf.gnu_longlink);
174#endif
175
176 i = th_read_internal(t);
177 if (i != T_BLOCKSIZE)
178 {
179 if (i != -1)
180 errno = EINVAL;
181 return -1;
182 }
183 }
184
185 /* check for GNU long name extention */
186 if (TH_ISLONGNAME(t))
187 {
188 sz = th_get_size(t);
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500189 blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
190 if (blocks > ((size_t)-1 / T_BLOCKSIZE))
191 {
192 errno = E2BIG;
193 return -1;
194 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500195#ifdef DEBUG
196 printf(" th_read(): GNU long filename detected "
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500197 "(%ld bytes, %d blocks)\n", sz, blocks);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500198#endif
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500199 t->th_buf.gnu_longname = (char *)malloc(blocks * T_BLOCKSIZE);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500200 if (t->th_buf.gnu_longname == NULL)
201 return -1;
202
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500203 for (j = 0, ptr = t->th_buf.gnu_longname; j < blocks;
204 j++, ptr += T_BLOCKSIZE)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500205 {
206#ifdef DEBUG
207 printf(" th_read(): reading long filename "
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500208 "(%d blocks left, ptr == %ld)\n", blocks-j, ptr);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500209#endif
210 i = tar_block_read(t, ptr);
211 if (i != T_BLOCKSIZE)
212 {
213 if (i != -1)
214 errno = EINVAL;
215 return -1;
216 }
217#ifdef DEBUG
218 printf(" th_read(): read block == \"%s\"\n", ptr);
219#endif
220 }
221#ifdef DEBUG
222 printf(" th_read(): t->th_buf.gnu_longname == \"%s\"\n",
223 t->th_buf.gnu_longname);
224#endif
225
226 i = th_read_internal(t);
227 if (i != T_BLOCKSIZE)
228 {
229 if (i != -1)
230 errno = EINVAL;
231 return -1;
232 }
233 }
234
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200235#ifdef HAVE_SELINUX
236 if(TH_ISEXTHEADER(t))
237 {
238 sz = th_get_size(t);
239
240 if(sz >= T_BLOCKSIZE) // Not supported
241 {
242#ifdef DEBUG
243 printf(" th_read(): Extended header is too long!\n");
244#endif
245 }
246 else
247 {
248 char buf[T_BLOCKSIZE];
249 i = tar_block_read(t, buf);
250 if (i != T_BLOCKSIZE)
251 {
252 if (i != -1)
253 errno = EINVAL;
254 return -1;
255 }
256
257 // To be sure
258 buf[T_BLOCKSIZE-1] = 0;
259
260 int len = strlen(buf);
261 char *start = strstr(buf, SELINUX_TAG);
262 if(start && start+SELINUX_TAG_LEN < buf+len)
263 {
264 start += SELINUX_TAG_LEN;
265 char *end = strchr(start, '\n');
266 if(end)
267 {
268 t->th_buf.selinux_context = strndup(start, end-start);
269#ifdef DEBUG
270 printf(" th_read(): SELinux context xattr detected: %s\n", t->th_buf.selinux_context);
271#endif
272 }
273 }
274 }
275
276 i = th_read_internal(t);
277 if (i != T_BLOCKSIZE)
278 {
279 if (i != -1)
280 errno = EINVAL;
281 return -1;
282 }
283 }
284#endif
285
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500286 return 0;
287}
288
289
290/* write a header block */
291int
292th_write(TAR *t)
293{
294 int i, j;
295 char type2;
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500296 uint64_t sz, sz2;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500297 char *ptr;
298 char buf[T_BLOCKSIZE];
299
300#ifdef DEBUG
301 printf("==> th_write(TAR=\"%s\")\n", t->pathname);
302 th_print(t);
303#endif
304
305 if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL)
306 {
307#ifdef DEBUG
308 printf("th_write(): using gnu_longlink (\"%s\")\n",
309 t->th_buf.gnu_longlink);
310#endif
311 /* save old size and type */
312 type2 = t->th_buf.typeflag;
313 sz2 = th_get_size(t);
314
315 /* write out initial header block with fake size and type */
316 t->th_buf.typeflag = GNU_LONGLINK_TYPE;
317 sz = strlen(t->th_buf.gnu_longlink);
318 th_set_size(t, sz);
319 th_finish(t);
320 i = tar_block_write(t, &(t->th_buf));
321 if (i != T_BLOCKSIZE)
322 {
323 if (i != -1)
324 errno = EINVAL;
325 return -1;
326 }
327
328 /* write out extra blocks containing long name */
329 for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
330 ptr = t->th_buf.gnu_longlink; j > 1;
331 j--, ptr += T_BLOCKSIZE)
332 {
333 i = tar_block_write(t, ptr);
334 if (i != T_BLOCKSIZE)
335 {
336 if (i != -1)
337 errno = EINVAL;
338 return -1;
339 }
340 }
341 memset(buf, 0, T_BLOCKSIZE);
342 strncpy(buf, ptr, T_BLOCKSIZE);
343 i = tar_block_write(t, &buf);
344 if (i != T_BLOCKSIZE)
345 {
346 if (i != -1)
347 errno = EINVAL;
348 return -1;
349 }
350
351 /* reset type and size to original values */
352 t->th_buf.typeflag = type2;
353 th_set_size(t, sz2);
354 }
355
356 if ((t->options & TAR_GNU) && t->th_buf.gnu_longname != NULL)
357 {
358#ifdef DEBUG
359 printf("th_write(): using gnu_longname (\"%s\")\n",
360 t->th_buf.gnu_longname);
361#endif
362 /* save old size and type */
363 type2 = t->th_buf.typeflag;
364 sz2 = th_get_size(t);
365
366 /* write out initial header block with fake size and type */
367 t->th_buf.typeflag = GNU_LONGNAME_TYPE;
368 sz = strlen(t->th_buf.gnu_longname);
369 th_set_size(t, sz);
370 th_finish(t);
371 i = tar_block_write(t, &(t->th_buf));
372 if (i != T_BLOCKSIZE)
373 {
374 if (i != -1)
375 errno = EINVAL;
376 return -1;
377 }
378
379 /* write out extra blocks containing long name */
380 for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
381 ptr = t->th_buf.gnu_longname; j > 1;
382 j--, ptr += T_BLOCKSIZE)
383 {
384 i = tar_block_write(t, ptr);
385 if (i != T_BLOCKSIZE)
386 {
387 if (i != -1)
388 errno = EINVAL;
389 return -1;
390 }
391 }
392 memset(buf, 0, T_BLOCKSIZE);
393 strncpy(buf, ptr, T_BLOCKSIZE);
394 i = tar_block_write(t, &buf);
395 if (i != T_BLOCKSIZE)
396 {
397 if (i != -1)
398 errno = EINVAL;
399 return -1;
400 }
401
402 /* reset type and size to original values */
403 t->th_buf.typeflag = type2;
404 th_set_size(t, sz2);
405 }
406
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200407#ifdef HAVE_SELINUX
408 if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL)
409 {
410#ifdef DEBUG
411 printf("th_write(): using selinux_context (\"%s\")\n",
412 t->th_buf.selinux_context);
413#endif
414 /* save old size and type */
415 type2 = t->th_buf.typeflag;
416 sz2 = th_get_size(t);
417
418 /* write out initial header block with fake size and type */
419 t->th_buf.typeflag = TH_EXT_TYPE;
420
421 /* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *content* *newline* */
422 // size newline
423 sz = SELINUX_TAG_LEN + strlen(t->th_buf.selinux_context) + 3 + 1;
424
425 if(sz >= 100) // another ascci digit for size
426 ++sz;
427
428 if(sz >= T_BLOCKSIZE) // impossible
429 {
430 errno = EINVAL;
431 return -1;
432 }
433
434 th_set_size(t, sz);
435 th_finish(t);
436 i = tar_block_write(t, &(t->th_buf));
437 if (i != T_BLOCKSIZE)
438 {
439 if (i != -1)
440 errno = EINVAL;
441 return -1;
442 }
443
444 memset(buf, 0, T_BLOCKSIZE);
James Christopher Adduono6f57f7c2016-03-01 16:01:53 -0500445 snprintf(buf, T_BLOCKSIZE, "%d "SELINUX_TAG"%s\n", (int)sz, t->th_buf.selinux_context);
Vojtech Bocek25fd68d2013-08-27 03:10:10 +0200446 i = tar_block_write(t, &buf);
447 if (i != T_BLOCKSIZE)
448 {
449 if (i != -1)
450 errno = EINVAL;
451 return -1;
452 }
453
454 /* reset type and size to original values */
455 t->th_buf.typeflag = type2;
456 th_set_size(t, sz2);
457 }
458#endif
459
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500460 th_finish(t);
461
462#ifdef DEBUG
463 /* print tar header */
464 th_print(t);
465#endif
466
467 i = tar_block_write(t, &(t->th_buf));
468 if (i != T_BLOCKSIZE)
469 {
470 if (i != -1)
471 errno = EINVAL;
472 return -1;
473 }
474
475#ifdef DEBUG
476 puts("th_write(): returning 0");
477#endif
478 return 0;
479}
480
481