From 3d58b437b4e0d9f138770ff596a9cddaaaf0aa01 Mon Sep 17 00:00:00 2001 From: grossmj Date: Wed, 27 Nov 2024 14:51:34 +1000 Subject: [PATCH] Remove restrictions based on file extension when listing images and fix ELF header checks --- gns3server/utils/images.py | 78 ++++++++++++++++++++------------- tests/utils/test_images.py | 89 ++++++++++++++++++++++++-------------- 2 files changed, 104 insertions(+), 63 deletions(-) diff --git a/gns3server/utils/images.py b/gns3server/utils/images.py index ac5d5476a..a3316d076 100644 --- a/gns3server/utils/images.py +++ b/gns3server/utils/images.py @@ -45,7 +45,7 @@ def list_images(emulator_type): # We limit recursion to path outside the default images directory # the reason is in the default directory manage file organization and - # it should be flatten to keep things simple + # it should be flat to keep things simple recurse = True if os.path.commonprefix([directory, general_images_directory]) == general_images_directory: recurse = False @@ -53,37 +53,53 @@ def list_images(emulator_type): directory = os.path.normpath(directory) for root, _, filenames in _os_walk(directory, recurse=recurse): for filename in filenames: - if filename not in files: - if filename.endswith(".md5sum") or filename.startswith("."): + if filename in files: + log.warning("File {} has already been found, skipping...".format(filename)) + continue + if filename.endswith(".md5sum") or filename.startswith("."): + continue + + files.add(filename) + + filesize = os.stat(os.path.join(root, filename)).st_size + if filesize < 7: + log.warning("File {} is too small to be an image, skipping...".format(filename)) + continue + + try: + with open(os.path.join(root, filename), "rb") as f: + # read the first 7 bytes of the file. + elf_header_start = f.read(7) + if emulator_type == "dynamips" and elf_header_start != b'\x7fELF\x01\x02\x01': + # IOS images must start with the ELF magic number, be 32-bit, big endian and have an ELF version of 1 + log.warning("IOS image {} does not start with a valid ELF magic number, skipping...".format(filename)) continue - elif ((filename.endswith(".image") or filename.endswith(".bin")) and emulator_type == "dynamips") \ - or ((filename.endswith(".bin") or filename.startswith("i86bi")) and emulator_type == "iou") \ - or (not filename.endswith(".bin") and not filename.endswith(".image") and emulator_type == "qemu"): - files.add(filename) - - # It the image is located in the standard directory the path is relative - if os.path.commonprefix([root, default_directory]) != default_directory: - path = os.path.join(root, filename) - else: - path = os.path.relpath(os.path.join(root, filename), default_directory) - - try: - if emulator_type in ["dynamips", "iou"]: - with open(os.path.join(root, filename), "rb") as f: - # read the first 7 bytes of the file. - elf_header_start = f.read(7) - # valid IOU or IOS images must start with the ELF magic number, be 32-bit or 64-bit, - # little endian and have an ELF version of 1 - if elf_header_start != b'\x7fELF\x02\x01\x01' and elf_header_start != b'\x7fELF\x01\x01\x01': - continue - - images.append({ - "filename": filename, - "path": force_unix_path(path), - "md5sum": md5sum(os.path.join(root, filename)), - "filesize": os.stat(os.path.join(root, filename)).st_size}) - except OSError as e: - log.warning("Can't add image {}: {}".format(path, str(e))) + elif emulator_type == "iou" and elf_header_start != b'\x7fELF\x02\x01\x01' and elf_header_start != b'\x7fELF\x01\x01\x01': + # IOU images must start with the ELF magic number, be 32-bit or 64-bit, little endian and have an ELF version of 1 + log.warning("IOU image {} does not start with a valid ELF magic number, skipping...".format(filename)) + continue + elif emulator_type == "qemu" and elf_header_start[:4] == b'\x7fELF': + # QEMU images should not start with an ELF magic number + log.warning("QEMU image {} starts with an ELF magic number, skipping...".format(filename)) + continue + + # It the image is located in the standard directory the path is relative + if os.path.commonprefix([root, default_directory]) != default_directory: + path = os.path.join(root, filename) + else: + path = os.path.relpath(os.path.join(root, filename), default_directory) + + images.append( + { + "filename": filename, + "path": force_unix_path(path), + "md5sum": md5sum(os.path.join(root, filename)), + "filesize": filesize + } + ) + + except OSError as e: + log.warning("Can't add image {}: {}".format(path, str(e))) return images diff --git a/tests/utils/test_images.py b/tests/utils/test_images.py index 76d7b8a8d..aea1d5811 100644 --- a/tests/utils/test_images.py +++ b/tests/utils/test_images.py @@ -114,30 +114,49 @@ def test_remove_checksum(tmpdir): def test_list_images(tmpdir): - path1 = tmpdir / "images1" / "IOS" / "test1.image" - path1.write(b'\x7fELF\x01\x01\x01', ensure=True) - path1 = force_unix_path(str(path1)) - - path2 = tmpdir / "images2" / "test2.image" - path2.write(b'\x7fELF\x01\x01\x01', ensure=True) - path2 = force_unix_path(str(path2)) + # IOS image in the images directory + ios_image_1 = tmpdir / "images1" / "IOS" / "ios_image_1.image" + ios_image_1.write(b'\x7fELF\x01\x02\x01', ensure=True) + ios_image_1 = force_unix_path(str(ios_image_1)) - # Invalid image because not a valid elf file - path = tmpdir / "images2" / "test_invalid.image" - path.write(b'NOTANELF', ensure=True) + # IOS image in an additional images path + ios_image_2 = tmpdir / "images2" / "ios_image_2.image" + ios_image_2.write(b'\x7fELF\x01\x02\x01', ensure=True) + ios_image_2 = force_unix_path(str(ios_image_2)) - if sys.platform.startswith("linux"): - path3 = tmpdir / "images1" / "IOU" / "test3.bin" - path3.write(b'\x7fELF\x02\x01\x01', ensure=True) - path3 = force_unix_path(str(path3)) + # Not a valid elf file + not_elf_file = tmpdir / "images1" / "IOS" / "not_elf.image" + not_elf_file.write(b'NOTANELF', ensure=True) + not_elf_file = force_unix_path(str(not_elf_file)) - path4 = tmpdir / "images1" / "QEMU" / "test4.qcow2" - path4.write("1", ensure=True) - path4 = force_unix_path(str(path4)) + # Invalid image because it is very small + small_file = tmpdir / "images1" / "too_small.image" + small_file.write(b'1', ensure=True) - path5 = tmpdir / "images1" / "QEMU" / "test4.qcow2.md5sum" - path5.write("1", ensure=True) - path5 = force_unix_path(str(path5)) + if sys.platform.startswith("linux"): + # 64-bit IOU image + iou_image_1 = tmpdir / "images1" / "IOU" / "iou64.bin" + iou_image_1.write(b'\x7fELF\x02\x01\x01', ensure=True) + iou_image_1 = force_unix_path(str(iou_image_1)) + # 32-bit IOU image + iou_image_2 = tmpdir / "images1" / "IOU" / "iou32.bin" + iou_image_2.write(b'\x7fELF\x01\x01\x01', ensure=True) # 32-bit IOU image + iou_image_2 = force_unix_path(str(iou_image_2)) + + + # Qemu image + qemu_image_1 = tmpdir / "images1" / "QEMU" / "qemu_image.qcow2" + qemu_image_1.write("1234567", ensure=True) + qemu_image_1 = force_unix_path(str(qemu_image_1)) + + # ELF file inside the Qemu + elf_file = tmpdir / "images1" / "QEMU" / "elf_file.bin" + elf_file.write(b'\x7fELF\x02\x01\x01', ensure=True) # ELF file + elf_file = force_unix_path(str(elf_file)) + + md5sum_file = tmpdir / "images1" / "QEMU" / "image.qcow2.md5sum" + md5sum_file.write("1", ensure=True) + md5sum_file = force_unix_path(str(md5sum_file)) with patch("gns3server.config.Config.get_section_config", return_value={ "images_path": str(tmpdir / "images1"), @@ -146,34 +165,40 @@ def test_list_images(tmpdir): assert list_images("dynamips") == [ { - 'filename': 'test1.image', + 'filename': 'ios_image_1.image', 'filesize': 7, - 'md5sum': 'e573e8f5c93c6c00783f20c7a170aa6c', - 'path': 'test1.image' + 'md5sum': 'b0d5aa897d937aced5a6b1046e8f7e2e', + 'path': 'ios_image_1.image' }, { - 'filename': 'test2.image', + 'filename': 'ios_image_2.image', 'filesize': 7, - 'md5sum': 'e573e8f5c93c6c00783f20c7a170aa6c', - 'path': str(path2) + 'md5sum': 'b0d5aa897d937aced5a6b1046e8f7e2e', + 'path': str(ios_image_2) } ] if sys.platform.startswith("linux"): assert list_images("iou") == [ { - 'filename': 'test3.bin', + 'filename': 'iou64.bin', 'filesize': 7, 'md5sum': 'c73626d23469519894d58bc98bee9655', - 'path': 'test3.bin' + 'path': 'iou64.bin' + }, + { + 'filename': 'iou32.bin', + 'filesize': 7, + 'md5sum': 'e573e8f5c93c6c00783f20c7a170aa6c', + 'path': 'iou32.bin' } ] assert list_images("qemu") == [ { - 'filename': 'test4.qcow2', - 'filesize': 1, - 'md5sum': 'c4ca4238a0b923820dcc509a6f75849b', - 'path': 'test4.qcow2' + 'filename': 'qemu_image.qcow2', + 'filesize': 7, + 'md5sum': 'fcea920f7412b5da7be0cf42b8c93759', + 'path': 'qemu_image.qcow2' } ]