From 3d340de4e8265199a2e51d2d4cf02dbb2853ee4c Mon Sep 17 00:00:00 2001 From: dachshund Date: Sun, 8 Sep 2013 02:31:50 -0400 Subject: [PATCH] Recognize a possible endless data attack in the form of invalid JSON metadata. --- tuf/__init__.py | 15 +++++++ tuf/client/updater.py | 17 ++++--- .../system_tests/test_endless_data_attack.py | 45 +++++++++++++------ 3 files changed, 58 insertions(+), 19 deletions(-) diff --git a/tuf/__init__.py b/tuf/__init__.py index f1ec70ec..55c07137 100755 --- a/tuf/__init__.py +++ b/tuf/__init__.py @@ -53,6 +53,21 @@ class FormatError(Error): +class InvalidMetadataJSONError(FormatError): + """Indicate that a metadata file is not valid JSON.""" + + def __init__(self, exception): + # Store the original exception. + self.exception = exception + + def __str__(self): + # Show the original exception. + return str(self.exception) + + + + + class UnsupportedAlgorithmError(Error): """Indicate an error while trying to identify a user-specified algorithm.""" pass diff --git a/tuf/client/updater.py b/tuf/client/updater.py index 15dc1000..ddbcf277 100755 --- a/tuf/client/updater.py +++ b/tuf/client/updater.py @@ -695,7 +695,10 @@ def __verify_metadata_file(self, metadata_file_object, metadata_role): In case a targets role has signed for a target it was not delegated to. tuf.FormatError: - In case the metadata file is somehow not valid. + In case the metadata file is valid JSON, but not valid TUF metadata. + + tuf.InvalidMetadataJSONError: + In case the metadata file is not valid JSON. tuf.ReplayedMetadataError: In case the downloaded metadata file is older than the current one. @@ -715,10 +718,14 @@ def __verify_metadata_file(self, metadata_file_object, metadata_role): """ - # Ensure the loaded 'metadata_signable' is properly formatted. - metadata_signable = \ - tuf.util.load_json_string(metadata_file_object.read()) - tuf.formats.check_signable_object_format(metadata_signable) + metadata = metadata_file_object.read() + try: + metadata_signable = tuf.util.load_json_string(metadata) + except Exception, exception: + raise tuf.InvalidMetadataJSONError(exception) + else: + # Ensure the loaded 'metadata_signable' is properly formatted. + tuf.formats.check_signable_object_format(metadata_signable) # Is 'metadata_signable' newer than the currently installed # version? diff --git a/tuf/tests/system_tests/test_endless_data_attack.py b/tuf/tests/system_tests/test_endless_data_attack.py index e2472425..0fa49e1b 100755 --- a/tuf/tests/system_tests/test_endless_data_attack.py +++ b/tuf/tests/system_tests/test_endless_data_attack.py @@ -31,6 +31,8 @@ """ +from __future__ import print_function + import os import shutil import urllib @@ -46,10 +48,9 @@ class EndlessDataAttack(Exception): -def _download(url, filename, tuf=False): - if tuf: +def _download(url, filename, TUF=False): + if TUF: urllib_tuf.urlretrieve(url, filename) - else: urllib.urlretrieve(url, filename) @@ -115,13 +116,28 @@ def test_arbitrary_package_attack(TUF=False, TIMESTAMP=False): try: # Client downloads (tries to download) the file. - _download(url=url_to_repo, filename=downloaded_file, tuf=TUF) + _download(url=url_to_repo, filename=downloaded_file, TUF=TUF) - except (tuf.DownloadError, tuf.RepositoryError), e: - # If tuf.DownloadError or tuf.RepositoryError is raised, this means - # that TUF has prevented the download of an unrecognized file. Enable - # logging to see what actually happened. - logger.warn('Download failed: '+repr(e)) + except tuf.NoWorkingMirrorError, exception: + endless_data_attack = False + + for mirror_url, mirror_error in exception.mirror_errors.iteritems(): + # We would get a bad hash error if the file was actually larger than + # the metadata said it was. + if isinstance(mirror_error, tuf.BadHashError): + endless_data_attack = True + break + # We would get invalid metadata JSON if the server deliberately sent + # malformed JSON as part of an endless data attack. + elif isinstance(mirror_error, tuf.InvalidMetadataJSONError): + endless_data_attack = True + break + + # In case we did not detect what was likely an endless data attack, we + # reraise the exception to indicate that endless data attack detection + # failed. + if not endless_data_attack: + raise else: # Check whether the attack succeeded by inspecting the content of the @@ -156,11 +172,12 @@ def test_arbitrary_package_attack(TUF=False, TIMESTAMP=False): try: - # FIXME: This test passes, but not yet because we avoided an endless data - # attack with timestamp metadata, but rather because the timestamp metadata - # is invalid. + # This test fails because the timestamp metadata has been extended with + # random data from its true length, thereby resulting in invalid JSON. test_arbitrary_package_attack(TUF=True, TIMESTAMP=True) - raise EndlessDataAttack('Timestamp metadata is not yet immune from the endless data attack!') except EndlessDataAttack, error: - print('With TUF: '+str(error)) + print('With TUF: '+str(error)) + + +