From ae3fb4def811f5bc42563128e8fae0a4288584d1 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 13 Jun 2023 20:01:05 +0100 Subject: [PATCH 03/15] tar: Implement tar-limit This can be used to ensure that the tar filter does not read indefinite amounts of input when opening the tar file. See: https://github.com/kubevirt/containerized-data-importer/pull/2748#issuecomment-1589852102 --- filters/tar/nbdkit-tar-filter.pod | 24 +++++++++++- tests/Makefile.am | 2 + filters/tar/tar.c | 10 +++++ tests/test-tar-limit.sh | 65 +++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 2 deletions(-) create mode 100755 tests/test-tar-limit.sh diff --git a/filters/tar/nbdkit-tar-filter.pod b/filters/tar/nbdkit-tar-filter.pod index eb0cfd4ab..a3c2d1328 100644 --- a/filters/tar/nbdkit-tar-filter.pod +++ b/filters/tar/nbdkit-tar-filter.pod @@ -4,8 +4,9 @@ nbdkit-tar-filter - read and write files inside tar files without unpacking =head1 SYNOPSIS - nbdkit file FILENAME.tar --filter=tar tar-entry=PATH_INSIDE_TAR - [tar=TAR_COMMAND] + nbdkit file FILENAME.tar + --filter=tar tar-entry=PATH_INSIDE_TAR + [tar=TAR_COMMAND] [tar-limit=SIZE] =head1 EXAMPLES @@ -83,6 +84,25 @@ The path of the file inside the tarball to serve. This parameter is required. It must exactly match the name stored in the tarball, so use S> +=item [B]SIZE + +When opening the tar file we have to locate the file (C) +inside the tarball. Because tar files do not have a central index we +must iterate over the tar file to find the entry, and that may be +costly (especially with untrusted tar files). In the worst case where +C starts near the end of the file we may have to iterate +over the whole tar file. If this is a problem you may set +C to some smaller value, eg: + + nbdkit -r curl https://example.com/file.tar \ + --filter=tar tar-entry=disk.img \ + tar-limit=10M + +which ensures no more than 10 megabytes are read before we give up and +reject the tar file (sending an error back to the NBD client). + +The default is 0 meaning no limit. + =item B =item B/PATH/TO/GTAR diff --git a/tests/Makefile.am b/tests/Makefile.am index 6694e409e..f2912aa93 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1957,11 +1957,13 @@ TESTS += \ test-tar.sh \ test-tar-info.sh \ test-tar-info-xz.sh \ + test-tar-limit.sh \ $(NULL) EXTRA_DIST += \ test-tar.sh \ test-tar-info.sh \ test-tar-info-xz.sh \ + test-tar-limit.sh \ $(NULL) # truncate filter tests. diff --git a/filters/tar/tar.c b/filters/tar/tar.c index f3adb2c46..efe47684d 100644 --- a/filters/tar/tar.c +++ b/filters/tar/tar.c @@ -53,6 +53,7 @@ #include "utils.h" static const char *entry; /* File within tar (tar-entry=...) */ +static int64_t tar_limit = 0; static const char *tar_program = "tar"; /* Offset and size within tarball. @@ -76,6 +77,12 @@ tar_config (nbdkit_next_config *next, nbdkit_backend *nxdata, entry = value; return 0; } + else if (strcmp (key, "tar-limit") == 0) { + tar_limit = nbdkit_parse_size (value); + if (tar_limit == -1) + return -1; + return 0; + } else if (strcmp (key, "tar") == 0) { tar_program = value; return 0; @@ -98,6 +105,7 @@ tar_config_complete (nbdkit_next_config_complete *next, #define tar_config_help \ "tar-entry= (required) The path inside the tar file to serve.\n" \ + "tar-limit=SIZE Limit on reading to find entry.\n" \ "tar= Path of the tar binary." static int @@ -197,6 +205,8 @@ calculate_offset_of_entry (nbdkit_next *next) copysize = next->get_size (next); if (copysize == -1) return -1; + if (tar_limit > 0 && copysize > tar_limit) + copysize = tar_limit; /* Run the tar command. */ nbdkit_debug ("%s", cmd); diff --git a/tests/test-tar-limit.sh b/tests/test-tar-limit.sh new file mode 100755 index 000000000..168f2c497 --- /dev/null +++ b/tests/test-tar-limit.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# nbdkit +# Copyright Red Hat +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of Red Hat nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +# Test the tar filter and tar-limit filter. + +source ./functions.sh +set -e +set -x + +requires test -f disk +requires tar --version +requires test -f disk +requires_nbdinfo + +tar_bad=tar-limit-bad.tar +tar_good=tar-limit-good.tar +tar_filler=tar-limit-filler.img +files="$tar_bad $tar_good $tar_filler" +rm -f $files +cleanup_fn rm -f $files + +# Create two tar files, one where the disk is located before an +# arbitrary boundary and one after. +truncate -s 1M $tar_filler +tar cf $tar_good disk +tar cf $tar_bad $tar_filler disk + +# Check we can read the good disk and reject the bad disk. +cmd="nbdkit -U - file --filter=tar tar-entry=disk tar-limit=131072" + +$cmd $tar_good --run 'nbdinfo "$uri"' + +if $cmd $tar_bad --run 'nbdinfo "$uri"' ; then + echo "ERROR: $0: expected $tar_bad to fail" + exit 1 +fi -- 2.41.0