Detect tarbombs while extracting

* src/common.h (one_top_level_option): New global.
(one_top_level): New global.
* src/extract.c (extr_init): If one_top_level_option is set, determine
the name one_top_level that might have to be prepended.
(extract_archive): If one_top_level_option is set, prepend one_top_level
to all names that don't already start with it.
* src/tar.c (ONE_TOP_LEVEL_OPTION): New contant.
(options): New option --one-top-level.
(parse_opt): Handle this option.
(decode_options): Make it conflict with --absolute-names.
This commit is contained in:
Connor Behan
2014-01-27 14:42:09 +02:00
committed by Sergey Poznyakoff
parent 95a51b93d0
commit 2af87fa277
5 changed files with 93 additions and 0 deletions
+4
View File
@@ -235,6 +235,10 @@ GLOBAL bool numeric_owner_option;
GLOBAL bool one_file_system_option;
/* Create a top-level directory for extracting based on the archive name. */
GLOBAL bool one_top_level_option;
GLOBAL char *one_top_level;
/* Specified value to be put into tar file in place of stat () results, or
just null and -1 if such an override should not take place. */
GLOBAL char const *owner_name_option;
+59
View File
@@ -191,6 +191,35 @@ extr_init (void)
umask (newdir_umask); /* restore the kernel umask */
current_umask = newdir_umask;
}
/* If the user wants to guarantee that everything is under one directory,
determine its name now and let it be created later. */
if (one_top_level_option)
{
int i;
char *base = base_name (archive_name_array[0]);
for (i = strlen (base) - 1; i > 2; i--)
if (!strncmp (base + i - 3, ".tar", 4) ||
!strncmp (base + i - 3, ".taz", 4) ||
!strncmp (base + i - 3, ".tbz", 4) ||
!strncmp (base + i - 3, ".tb2", 4) ||
!strncmp (base + i - 3, ".tgz", 4) ||
!strncmp (base + i - 3, ".tlz", 4) ||
!strncmp (base + i - 3, ".txz", 4)) break;
if (i <= 3)
{
one_top_level_option = false;
free (base);
return;
}
one_top_level = xmalloc (i - 2);
strncpy (one_top_level, base, i - 3);
one_top_level[i - 3] = '\0';
free (base);
}
}
/* Use fchmod if possible, fchmodat otherwise. */
@@ -1578,6 +1607,33 @@ prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun)
return 1;
}
void
maybe_prepend_name (char **file_name)
{
int i;
for (i = 0; i < strlen (*file_name); i++)
if (!ISSLASH ((*file_name)[i]) && (*file_name)[i] != '.') break;
if (i == strlen (*file_name))
return;
if (!strncmp (*file_name + i, one_top_level, strlen (one_top_level)))
{
int pos = i + strlen (one_top_level);
if (ISSLASH ((*file_name)[pos]) || (*file_name)[pos] == '\0') return;
}
char *new_name = xmalloc (strlen (one_top_level) + strlen (*file_name) + 2);
strcpy (new_name, one_top_level);
strcat (new_name, "/");
strcat (new_name, *file_name);
free (*file_name);
*file_name = new_name;
}
/* Extract a file from the archive. */
void
extract_archive (void)
@@ -1628,6 +1684,9 @@ extract_archive (void)
typeflag = sparse_member_p (&current_stat_info) ?
GNUTYPE_SPARSE : current_header->header.typeflag;
if (one_top_level_option)
maybe_prepend_name (&current_stat_info.file_name);
if (prepare_to_extract (current_stat_info.file_name, typeflag, &fun))
{
if (fun && (*fun) (current_stat_info.file_name, typeflag)
+11
View File
@@ -319,6 +319,7 @@ enum
OCCURRENCE_OPTION,
OLD_ARCHIVE_OPTION,
ONE_FILE_SYSTEM_OPTION,
ONE_TOP_LEVEL_OPTION,
OVERWRITE_DIR_OPTION,
OVERWRITE_OPTION,
OWNER_OPTION,
@@ -489,6 +490,9 @@ static struct argp_option options[] = {
{"keep-directory-symlink", KEEP_DIRECTORY_SYMLINK_OPTION, 0, 0,
N_("preserve existing symlinks to directories when extracting"),
GRID+1 },
{"one-top-level", ONE_TOP_LEVEL_OPTION, 0, 0,
N_("create a subdirectory to avoid having loose files extracted"),
GRID+1 },
#undef GRID
#define GRID 40
@@ -1441,6 +1445,10 @@ parse_opt (int key, char *arg, struct argp_state *state)
one_file_system_option = true;
break;
case ONE_TOP_LEVEL_OPTION:
one_top_level_option = true;
break;
case 'l':
check_links_option = 1;
break;
@@ -2393,6 +2401,9 @@ decode_options (int argc, char **argv)
subcommand_string (subcommand_option)));
}
if (one_top_level_option && absolute_names_option)
USAGE_ERROR ((0, 0, _("--one-top-level cannot be used with --absolute-names")));
if (archive_names == 0)
{
/* If no archive file name given, try TAPE from the environment, or