diff --git a/MANIFEST.in b/MANIFEST.in index 883aba8a12d..93bb5bee5d9 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -39,6 +39,7 @@ include nova/tests/bundle/1mb.part.0 include nova/tests/bundle/1mb.part.1 include nova/tests/public_key/* include nova/tests/db/nova.austin.sqlite +include nova/tests/image/*.tar.gz include plugins/xenapi/README include plugins/xenapi/etc/xapi.d/plugins/objectstore include plugins/xenapi/etc/xapi.d/plugins/pluginlib_nova.py diff --git a/nova/image/s3.py b/nova/image/s3.py index abf01a94212..801d9e51118 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -100,7 +100,7 @@ def _conn(context): @staticmethod def _download_file(bucket, filename, local_dir): key = bucket.get_key(filename) - local_filename = os.path.join(local_dir, filename) + local_filename = os.path.join(local_dir, os.path.basename(filename)) key.get_contents_to_filename(local_filename) return local_filename @@ -315,8 +315,19 @@ def _decrypt_image(encrypted_filename, encrypted_key, encrypted_iv, {'image_file': encrypted_filename, 'err': err}) + @staticmethod + def _test_for_malicious_tarball(path, filename): + """Raises exception if extracting tarball would escape extract path""" + tar_file = tarfile.open(filename, 'r|gz') + for n in tar_file.getnames(): + if not os.path.abspath(os.path.join(path, n)).startswith(path): + tar_file.close() + raise exception.Error(_('Unsafe filenames in image')) + tar_file.close() + @staticmethod def _untarzip_image(path, filename): + S3ImageService._test_for_malicious_tarball(path, filename) tar_file = tarfile.open(filename, 'r|gz') tar_file.extractall(path) image_file = tar_file.getnames()[0] diff --git a/nova/tests/image/abs.tar.gz b/nova/tests/image/abs.tar.gz new file mode 100644 index 00000000000..4d39507340b Binary files /dev/null and b/nova/tests/image/abs.tar.gz differ diff --git a/nova/tests/image/rel.tar.gz b/nova/tests/image/rel.tar.gz new file mode 100644 index 00000000000..b54f55aa790 Binary files /dev/null and b/nova/tests/image/rel.tar.gz differ diff --git a/nova/tests/image/test_s3.py b/nova/tests/image/test_s3.py index f1ceeb7fe70..4ab36a0f6b5 100644 --- a/nova/tests/image/test_s3.py +++ b/nova/tests/image/test_s3.py @@ -15,6 +15,8 @@ # License for the specific language governing permissions and limitations # under the License. +import os + from nova import context from nova import test from nova.image import s3 @@ -112,3 +114,11 @@ def test_s3_create(self): {'device_name': '/dev/sdb0', 'no_device': True}] self.assertEqual(block_device_mapping, expected_bdm) + + def test_s3_malicious_tarballs(self): + self.assertRaises(exception.Error, + self.image_service._test_for_malicious_tarball, + "/unused", os.path.join(os.path.dirname(__file__), 'abs.tar.gz')) + self.assertRaises(exception.Error, + self.image_service._test_for_malicious_tarball, + "/unused", os.path.join(os.path.dirname(__file__), 'rel.tar.gz'))