[Bug-tar] [PATCH] Option to 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. * NEWS: Update. * doc/tar.texi: Document new option. --- NEWS | 9 + doc/tar.texi | 11 +++ src/common.h | 4 src/extract.c | 59 +++ src/tar.c | 11 +++ 5 files changed, 94 insertions(+) diff --git a/NEWS b/NEWS index ced28e0..3d9bf93 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,15 @@ sign and the specifier letter. This release includes official tar(1) and rmt(8) manpages. Distribution maintainers are kindly asked to use these instead of the home-made pages they provided so far. + +* The --one-top-level option. + +This new command line option tells tar that the working directory +(or the one passed to -C) should not be populated with more than +one name directly under it. Instead a newly created subdirectory +is used whose default name is equal to the archive name with the +suffix stripped off. For example, foo.tar.gz would be extracted +to foo. version 1.27.1 - Sergey Poznyakoff, 2013-11-17 diff --git a/doc/tar.texi b/doc/tar.texi index 424617c..ece40f4 100644 --- a/doc/tar.texi +++ b/doc/tar.texi @@ -3086,6 +3086,17 @@ Used when creating an archive. Prevents @command{tar} from recursing into directories that are on different file systems from the current directory. +@opsummary{one-top-level} +@item --one-top-level +Tells @command{tar} to create a new directory beneath the extraction directory +(or the one passed to @option{-C}) and use it to guard against tarbombs. The +name of the new directory will be equal to the name of the archive with the +extension stripped off. If any archive names (after transformations from +@option{--transform} and @option{--strip-components}) do not already begin with +it, the new directory will be prepended to the names immediately before +extraction. Recognized extensions are @samp{.tar}, @samp{.taz}, @samp{.tbz}, +@samp{.tb2}, @samp{.tgz}, @samp{.tlz} and @samp{.txz}. + @opsummary{overwrite} @item --overwrite diff --git a/src/common.h b/src/common.h index 8f8a337..365379a 100644 --- a/src/common.h +++ b/src/common.h @@ -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; diff --git a/src/extract.c b/src/extract.c index 9b6b7f9..b6fdbbb 100644 --- a/src/extract.c +++ b/src/extract.c @@ -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);
[Bug-tar] [PATCH] Option to 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. * NEWS: Update. * doc/tar.texi: Document new option. --- NEWS | 8 doc/tar.texi | 11 +++ src/common.h | 4 src/extract.c | 59 +++ src/tar.c | 11 +++ 5 files changed, 93 insertions(+) diff --git a/NEWS b/NEWS index 1444143..52e1284 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,14 @@ sign and the specifier letter. current screen width, if {N} is not given. %c- a shortcut for %{%Y-%m-%d %H:%M:%S}t: %ds, %{read,wrote}T%*\r +* The --one-top-level option. + +This new command line option tells tar that the working directory +(or the one passed to -C) should not be populated with more than one +name directly under it. Instead, a newly created subdirectory is +used whose name is equal to the archive name without the extension. +For example, foo.tar.gz would be extracted to foo. + version 1.27.1 - Sergey Poznyakoff, 2013-11-17 diff --git a/doc/tar.texi b/doc/tar.texi index 424617c..ece40f4 100644 --- a/doc/tar.texi +++ b/doc/tar.texi @@ -3086,6 +3086,17 @@ Used when creating an archive. Prevents @command{tar} from recursing into directories that are on different file systems from the current directory. +@opsummary{one-top-level} +@item --one-top-level +Tells @command{tar} to create a new directory beneath the extraction directory +(or the one passed to @option{-C}) and use it to guard against tarbombs. The +name of the new directory will be equal to the name of the archive with the +extension stripped off. If any archive names (after transformations from +@option{--transform} and @option{--strip-components}) do not already begin with +it, the new directory will be prepended to the names immediately before +extraction. Recognized extensions are @samp{.tar}, @samp{.taz}, @samp{.tbz}, +@samp{.tb2}, @samp{.tgz}, @samp{.tlz} and @samp{.txz}. + @opsummary{overwrite} @item --overwrite diff --git a/src/common.h b/src/common.h index 8f8a337..365379a 100644 --- a/src/common.h +++ b/src/common.h @@ -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; diff --git a/src/extract.c b/src/extract.c index 9b6b7f9..b6fdbbb 100644 --- a/src/extract.c +++ b/src/extract.c @@ -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;
[Bug-tar] [PATCH] Option to 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. * NEWS: Update. * doc/tar.texi: Document new option. --- NEWS | 10 ++ doc/tar.texi | 11 +++ src/common.h | 4 src/extract.c | 59 +++ src/tar.c | 11 +++ 5 files changed, 95 insertions(+) diff --git a/NEWS b/NEWS index 1a264b0..17d0004 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,16 @@ GNU tar NEWS - User visible changes. 2013-11-17 Please send GNU tar bug reports to bug-tar@gnu.org +version 1.27.90 (Git) + +* The --one-top-level option. + +This new command line option tells tar that the working directory +(or the one passed to -C) should not be populated with more than one +name directly under it. Instead, a newly created subdirectory is +used whose name is equal to the archive name without the extension. +For example, foo.tar.gz would be extracted to foo. + version 1.27.1 - Sergey Poznyakoff, 2013-11-17 diff --git a/doc/tar.texi b/doc/tar.texi index 9fde5a0..f7376ac 100644 --- a/doc/tar.texi +++ b/doc/tar.texi @@ -3086,6 +3086,17 @@ Used when creating an archive. Prevents @command{tar} from recursing into directories that are on different file systems from the current directory. +@opsummary{one-top-level} +@item --one-top-level +Tells @command{tar} to create a new directory beneath the extraction directory +(or the one passed to @option{-C}) and use it to guard against tarbombs. The +name of the new directory will be equal to the name of the archive with the +extension stripped off. If any archive names (after transformations from +@option{--transform} and @option{--strip-components}) do not already begin with +it, the new directory will be prepended to the names immediately before +extraction. Recognized extensions are @samp{.tar}, @samp{.taz}, @samp{.tbz}, +@samp{.tbz}, @samp{.tb2}, @samp{.tgz}, @samp{.tlz} and @samp{.txz}. + @opsummary{overwrite} @item --overwrite diff --git a/src/common.h b/src/common.h index 42fd539..1f3e48c 100644 --- a/src/common.h +++ b/src/common.h @@ -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; diff --git a/src/extract.c b/src/extract.c index 9b6b7f9..7ea357e 100644 --- a/src/extract.c +++ b/src/extract.c @@ -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
[Bug-tar] [PATCH] Option to 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. * NEWS: Update. * doc/tar.texi: Document new option. --- NEWS | 10 ++ doc/tar.texi | 11 +++ src/common.h | 4 src/extract.c | 56 src/tar.c | 11 +++ 5 files changed, 92 insertions(+) diff --git a/NEWS b/NEWS index 1a264b0..17d0004 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,16 @@ GNU tar NEWS - User visible changes. 2013-11-17 Please send GNU tar bug reports to bug-tar@gnu.org +version 1.27.90 (Git) + +* The --one-top-level option. + +This new command line option tells tar that the working directory +(or the one passed to -C) should not be populated with more than one +name directly under it. Instead, a newly created subdirectory is +used whose name is equal to the archive name without the extension. +For example, foo.tar.gz would be extracted to foo. + version 1.27.1 - Sergey Poznyakoff, 2013-11-17 diff --git a/doc/tar.texi b/doc/tar.texi index 9fde5a0..f7376ac 100644 --- a/doc/tar.texi +++ b/doc/tar.texi @@ -3086,6 +3086,17 @@ Used when creating an archive. Prevents @command{tar} from recursing into directories that are on different file systems from the current directory. +@opsummary{one-top-level} +@item --one-top-level +Tells @command{tar} to create a new directory beneath the extraction directory +(or the one passed to @option{-C}) and use it to guard against tarbombs. The +name of the new directory will be equal to the name of the archive with the +extension stripped off. If any archive names (after transformations from +@option{--transform} and @option{--strip-components}) do not already begin with +it, the new directory will be prepended to the names immediately before +extraction. Recognized extensions are @samp{.tar}, @samp{.taz}, @samp{.tbz}, +@samp{.tbz}, @samp{.tb2}, @samp{.tgz}, @samp{.tlz} and @samp{.txz}. + @opsummary{overwrite} @item --overwrite diff --git a/src/common.h b/src/common.h index 42fd539..1f3e48c 100644 --- a/src/common.h +++ b/src/common.h @@ -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; diff --git a/src/extract.c b/src/extract.c index 9b6b7f9..80666a6 100644 --- a/src/extract.c +++ b/src/extract.c @@ -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,30 @@ 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))) +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 +1681,9 @@ extract_archive (void) typeflag = sparse_member_p