Skip to content

Conversation

@awwad
Copy link
Contributor

@awwad awwad commented May 30, 2018

In order to avoid freezes if role info is deleted due to prior
validation failures, make sure that roledb is rebuilt during
client tuf.client.updater.refresh(), even if root did not need
to be updated.

WIP: I may want to add some tests and wrap a line.

See Issue 736 for more details.

Fixes issue #736

Please verify and check that the pull request fulfills the following
requirements
:

  • The code follows the Code Style Guidelines
  • Tests have been added for the bug fix or new feature
  • Docs have been added for the bug fix or new feature

In order to avoid freezes if role info is deleted due to prior
validation failures, make sure that roledb is rebuilt during
client tuf.client.updater.refresh(), even if root did not need
to be updated.

See Issue 736 for more details.

Signed-off-by: Sebastien Awwad <[email protected]>
@vladimir-v-diaz
Copy link
Contributor

The relocation of the call to _rebuild_key_and_role_db() looks good to me. Another thing that we should do is document, in the relevant docstrings, the assumption that the client has the latest info for the top-level roles before attempting to update any specific metadata.

Note: Travis failed, so we probably need to update 1 or 2 test conditions.

@vladimir-v-diaz
Copy link
Contributor

@awwad I'd like to include this important fix in our next scheduled release, which is approaching soon.

Is it okay if I resolve the Travis/Appveyor failures and finish this pull request?

@awwad
Copy link
Contributor Author

awwad commented Jun 8, 2018 via email

@vladimir-v-diaz
Copy link
Contributor

vladimir-v-diaz commented Jun 11, 2018

@awwad I went ahead and (1) added a test condition for the persistent freeze attack (2) wrapped the line in refresh() that sets consistent snapshot (3) revised a bunch of comments and docstring to document the new behavior and any assumptions (4) fixed the failing unit tests.

I think the pull request now just needs to re-sync with the develop branch.

Thanks for reporting the issue and submitting the initial fix!

@vladimir-v-diaz
Copy link
Contributor

@trishankatdatadog
Heads up: This is an important change that addresses a persistent freeze attack. I plant to add it to the next stable release and v0.12.dev1 for testing.

Copy link
Contributor Author

@awwad awwad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM but for a comment improvement and two questions.

# expired Snapshot and the locally trusted one, which should now be out of
# date. After the attack, attempt to re-issue a valid Snapshot to verify
# that the client is able to recover from the indefinite freeze attack via
# the snapshot metadata.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it will be a bit confusing to future readers to refer to this as an indefinite freeze attack here without context (since there is nothing that obviously represents an attack in the test below). Perhaps:

# Serve the client expired Snapshot.  The client should reject the given,
# expired Snapshot and the locally trusted one, which should now be out of
# date.
# After the attack, attempt to re-issue a valid Snapshot to verify that
# the client is still able to update. (A bug previously caused snapshot
# expiration or replay to result in an indefinite freeze.)

You could also point to the particular issue, #736, in the comment, though that's probably less important. If that looks good, I can add the commit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the comment that you added, but we should also point to #736 so the reader can understand how the bug was caused.

I'd appreciate you adding the commit, if you are not busy.

sorted(self.repository_updater.metadata['current']))



Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test looks good.

self.assertEqual(sorted(['role1', 'root', 'snapshot', 'targets', 'timestamp']),
self.assertEqual(sorted(['root', 'snapshot', 'targets', 'timestamp']),
sorted(tuf.roledb.get_rolenames('test_repository1')))
self.assertEqual(sorted(['role1', 'root', 'snapshot', 'targets', 'timestamp']),
Copy link
Contributor Author

@awwad awwad Jun 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused by this change. How did this result from the PR?

Edit: ah, probably from the targets_of_role change...?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refresh() rebuilds the roledb and only refreshes top-level metadata. See line 212.

securesystemslib.formats.RELPATH_SCHEMA.check_match(rolename)

self._refresh_targets_metadata(refresh_all_delegated_roles=True)

Copy link
Contributor Author

@awwad awwad Jun 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is refresh_all_delegated_roles necessary? This could be quite a few roles.

Next point is a bit complex:
targets_of_role remains a strange function to me if called on anything other than the Targets role itself. If the indicated role is a delegated role, it just isn't clear what it means to obtain trustworthy target info for whatever files are listed by a trustworthy version of that role. (Whose word are we taking on the latter?)
Perhaps the best we can do is to: obtain some not-validated or partially-validated version of the delegated role (because delegation pathways depend on file paths and we haven't specified one), get the list of targets specified by that not-validated role file, and then properly obtain trustworthy target info for those file paths (whether or not they are ultimately provided by the same role?).

This more complex point plays into the correct way to run targets_of_role on a delegated role.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thinking is that all_targets() and targets_of_roles() should be for clients that do not know ahead of time which targets are available on the repo, or provided by a particular role.

Community repos will certainly use get_one_valid_targetinfo(), but we might want to provide all_targets() and targets_of_roles() for simpler use cases / adopters.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After an offline discussion, @awwad and I came to the conclusion that it's better to not support all_targets() and targets_of_role(). We'll open a separate issue to remove these two functions.

@trishankatdatadog
Copy link
Contributor

Does this break backwards-compatibility?

@vladimir-v-diaz
Copy link
Contributor

Does this break backwards-compatibility?

I don't think this pull request breaks backwards-compatibility. @awwad Can you confirm?

The change makes sure that the client has the latest metadata loaded into memory after a updater.refresh(), even if one of the top-level metadata hasn't been updated by the repository.

@awwad
Copy link
Contributor Author

awwad commented Jun 11, 2018

No, I can't think of how it would break backward compatibility. (If it causes a problem, of course, LMK.)

BTW, @trishankatdatadog, do you use targets_of_role or all_targets?

to indicate the source of the freeze issue

Signed-off-by: Sebastien Awwad <[email protected]>
@trishankatdatadog
Copy link
Contributor

@awwad
Copy link
Contributor Author

awwad commented Jun 11, 2018

(And from there, it looks like you get target info via get_one_valid_targetinfo)

Okay, good. (:

@trishankatdatadog
Copy link
Contributor

Thanks for checking and verifying, @awwad, much appreciated 🙇

@trishankatdatadog
Copy link
Contributor

Hi, any idea when v0.12.dev1 will be released on PyPI for testing? Thanks!

@vladimir-v-diaz
Copy link
Contributor

I can do it sometime today, will keep you posted here.

@vladimir-v-diaz
Copy link
Contributor

@trishankatdatadog
update: v0.12.dev1 was released to PyPI for testing.

@vladimir-v-diaz vladimir-v-diaz deleted the fix_736_freeze branch June 13, 2018 21:22
@trishankatdatadog
Copy link
Contributor

I'll test it, thanks!

@awwad awwad changed the title WIP: Fix 736: Rebuild roledb in refresh() even if root unchanged Fix 736: Rebuild roledb in refresh() even if root unchanged Jun 20, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants