Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not immediately verify latest root when rotating #885

Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 49 additions & 18 deletions tuf/client/updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
import securesystemslib.util
import six
import iso8601
import requests.exceptions

# The Timestamp role does not have signed metadata about it; otherwise we
# would need an infinite regress of metadata. Therefore, we use some
Expand Down Expand Up @@ -1125,28 +1126,58 @@ def _update_root_metadata(self, current_root_metadata):
None.
"""

# Retrieve the latest, remote root.json.
latest_root_metadata_file = self._get_metadata_file(
'root', 'root.json', DEFAULT_ROOT_UPPERLENGTH, None)
def neither_403_nor_404(mirror_error):
if isinstance(mirror_error, requests.exceptions.HTTPError):
if mirror_error.response.status_code in {403, 404}:
return False
return True

latest_root_metadata = securesystemslib.util.load_json_string(
latest_root_metadata_file.read().decode('utf-8'))
# Temporarily set consistent snapshot. Will be updated to whatever is set
# in the latest root.json after running through the intermediates with
# _update_metadata().
self.consistent_snapshot = True

# Following the spec, try downloading the N+1th root for a certain maximum
# number of times.
lower_bound = current_root_metadata['version'] + 1
upper_bound = lower_bound + tuf.settings.MAX_NUMBER_ROOT_ROTATIONS

# Try downloading the next root.
for next_version in range(lower_bound, upper_bound):
try:
# Thoroughly verify it.
self._update_metadata('root', DEFAULT_ROOT_UPPERLENGTH,
version=next_version)
# When we run into HTTP 403 /404 error from ALL mirrors, break out of
trishankatdatadog marked this conversation as resolved.
Show resolved Hide resolved
# loop, because the next root metadata file is most likely missing.
except tuf.exceptions.NoWorkingMirrorError as exception:
for mirror_error in exception.mirror_errors.values():
# Otherwise, reraise the error, because it is not a simple HTTP
# error.
if neither_403_nor_404(mirror_error):
logging.exception('Misc error for root version '+str(next_version))
raise
else:
# Calling this function should give us a detailed stack trace
# including an HTTP error code, if any.
logging.exception('HTTP error for root version '+str(next_version))
# If we are here, then we ran into only 403 / 404 errors, which are
# good reasons to suspect that the next root metadata file does not
# exist.
break

next_version = current_root_metadata['version'] + 1
latest_version = latest_root_metadata['signed']['version']
# Ensure that the role and key information of the top-level roles is the
# latest. We do this whether or not Root needed to be updated, in order
# to ensure that, e.g., the entries in roledb for top-level roles are
# populated with expected keyid info so that roles can be validated. In
# certain circumstances, top-level metadata might be missing because it
# was marked obsolete and deleted after a failed attempt, and thus we
# should refresh them here as a protective measure. See Issue #736.
self._rebuild_key_and_role_db()

# update from the next version of root up to (and including) the latest
# version. For example:
# current = version 1
# latest = version 3
# update from 1.root.json to 3.root.json.
for version in range(next_version, latest_version + 1):
# Temporarily set consistent snapshot. Will be updated to whatever is set
# in the latest root.json after running through the intermediates with
# _update_metadata().
self.consistent_snapshot = True
self._update_metadata('root', DEFAULT_ROOT_UPPERLENGTH, version=version)
# Set our consistent snapshot property to what the latest root has said.
self.consistent_snapshot = \
self.metadata['current']['root']['consistent_snapshot']



Expand Down
4 changes: 4 additions & 0 deletions tuf/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,7 @@
# to suspend execution for a specified amount of time. See
# theupdateframework/tuf/issue#338.
SLEEP_BEFORE_ROUND = None

# Maximum number of root metadata file rotations we should perform in order to
# prevent a denial-of-service (DoS) attack.
MAX_NUMBER_ROOT_ROTATIONS = 2**5