From: Philipp Gesang Date: Thu, 10 Aug 2017 15:01:42 +0000 (+0200) Subject: add header corruption tests X-Git-Tag: v2.2~7^2~83 X-Git-Url: http://developer.intra2net.com/git/?a=commitdiff_plain;h=00b8c150153165ff5eeb8eaee3c65160df8baefd;p=python-delta-tar add header corruption tests We hit them where it hurts: * for compressed backups, flip a bit in the magic; * for encrypted backups, flip a bit in the tag. In either case, normal restore must fail, and disaster recovery will be incomplete. --- diff --git a/runtests.py b/runtests.py index 3605964..d1ed888 100755 --- a/runtests.py +++ b/runtests.py @@ -22,7 +22,12 @@ import unittest from testing.test_crypto import HeaderTest, AESGCMTest from testing.test_multivol import MultivolGnuFormatTest, MultivolPaxFormatTest from testing.test_concat_compress import ConcatCompressTest -from testing.test_recover import RecoverTest, RecoverGZTest, RecoverGZAESTest +from testing.test_recover import \ + RecoverCorruptPayloadTest \ + , RecoverCorruptPayloadGZTest \ + , RecoverCorruptPayloadGZAESTest \ + , RecoverCorruptHeaderGZTest \ + , RecoverCorruptHeaderGZAESTest from testing.test_rescue_tar import RescueTarTest from testing.test_encryption import EncryptionTest from testing.test_deltatar import (DeltaTarTest, DeltaTar2Test, @@ -57,7 +62,11 @@ if __name__ == "__main__": , DeltaTarAes128ConcatTest , HeaderTest, AESGCMTest # testing.test_recover - , RecoverTest, RecoverGZTest, RecoverGZAESTest + , RecoverCorruptPayloadTest + , RecoverCorruptPayloadGZTest + , RecoverCorruptPayloadGZAESTest + , RecoverCorruptHeaderGZTest + , RecoverCorruptHeaderGZAESTest ]: try: t = group (n) diff --git a/testing/test_recover.py b/testing/test_recover.py index 52dcbee..5ecfb1f 100644 --- a/testing/test_recover.py +++ b/testing/test_recover.py @@ -69,6 +69,41 @@ def is_pdt_encrypted (fname): return True +def corrupt_header (_, fname, compress, encrypt): + """ + Modify a significant byte in the object header of the format. + """ + if encrypt is True: # damage GCM tag + flip_bits (fname, crypto.HDR_OFF_TAG + 1) + elif compress is True: # invalidate magic + flip_bits (fname, 1) + else: # Fudge checksum. From tar(5): + # + # struct header_gnu_tar { + # char name[100]; + # char mode[8]; + # char uid[8]; + # char gid[8]; + # char size[12]; + # char mtime[12]; + # char checksum[8]; + # … + flip_bits (fname, 100 + 8 + 8 + 8 + 12 + 12 + 1) + + +def corrupt_payload_start (_, fname, compress, encrypt): + """ + Modify the byte following the object header structure of the format. + """ + if encrypt is True: + flip_bits (fname, crypto.PDTCRYPT_HDR_SIZE + 1) + elif compress is True: + flip_bits (fname, gz_header_size (fname) + 1) + else: + flip_bits (fname, tarfile.BLOCKSIZE + 1) + + + ############################################################################### ## tests ## ############################################################################### @@ -81,6 +116,7 @@ class RecoverTest (BaseTest): COMPRESSION = None PASSWORD = None FAILURES = 0 + CORRUPT = corrupt_payload_start def setUp(self): @@ -159,12 +195,9 @@ class RecoverTest (BaseTest): shutil.rmtree (self.dst_path) shutil.rmtree (self.src_path) - if self.PASSWORD is not None: - flip_bits (backup_full, crypto.PDTCRYPT_HDR_SIZE + 1) - elif self.COMPRESSION is not None: - flip_bits (backup_full, gz_header_size (backup_full) + 1) - else: - flip_bits (backup_full, tarfile.BLOCKSIZE + 1) + self.CORRUPT (backup_full, + self.COMPRESSION is not None, + self.PASSWORD is not None) # normal restore must fail try: @@ -173,9 +206,16 @@ class RecoverTest (BaseTest): except tarfile.CompressionError: if self.PASSWORD is not None or self.COMPRESSION is not None: pass + else: + raise except tarfile.ReadError: - if self.PASSWORD is not None or self.COMPRESSION is not None: + # can happen with all three modes + pass + except tarfile.DecryptionError: + if self.PASSWORD is not None: pass + else: + raise os.chdir (self.pwd) # not restored due to the error above # but recover will succeed @@ -184,6 +224,7 @@ class RecoverTest (BaseTest): "%s/%s" % (bak_path, index_file) ]) + print ("¤¤¤ failed", failed) assert len (failed) == self.FAILURES # with one file missing @@ -199,14 +240,34 @@ class RecoverTest (BaseTest): shutil.rmtree (self.dst_path) -class RecoverGZTest (RecoverTest): +class RecoverCorruptPayloadTest (RecoverTest): + COMPRESSION = None + PASSWORD = None + FAILURES = 0 + + +class RecoverCorruptPayloadGZTest (RecoverTest): + COMPRESSION = "#gz" + PASSWORD = None + FAILURES = 1 + + +class RecoverCorruptPayloadGZAESTest (RecoverTest): + COMPRESSION = "#gz" + PASSWORD = TEST_PASSWORD + FAILURES = 1 + + +class RecoverCorruptHeaderGZTest (RecoverTest): COMPRESSION = "#gz" PASSWORD = None FAILURES = 1 + CORRUPT = corrupt_header -class RecoverGZAESTest (RecoverTest): +class RecoverCorruptHeaderGZAESTest (RecoverTest): COMPRESSION = "#gz" PASSWORD = TEST_PASSWORD FAILURES = 1 + CORRUPT = corrupt_header