From 33d39afdf5c3023b4460bfa83d98df37c1950a58 Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Tue, 15 Dec 2015 15:15:37 -0800 Subject: [PATCH] download all delegated roles when doing downloadTargets Signed-off-by: David Lawrence (github: endophage) --- tuf/client/client.go | 65 ++++++++++++++++++++++++++------------------ tuf/utils/stack.go | 57 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 26 deletions(-) create mode 100644 tuf/utils/stack.go diff --git a/tuf/client/client.go b/tuf/client/client.go index 7da9785f84..861ebb628f 100644 --- a/tuf/client/client.go +++ b/tuf/client/client.go @@ -369,34 +369,47 @@ func (c *Client) downloadSnapshot() error { return nil } -// downloadTargets is responsible for downloading any targets file -// including delegates roles. +// downloadTargets downloads all targets and delegated targets for the repository. +// It uses a pre-order tree traversal as it's necessary to download parents first +// to obtain the keys to validate children. func (c *Client) downloadTargets(role string) error { - role = data.RoleName(role) // this will really only do something for base targets role - if c.local.Snapshot == nil { - return ErrMissingMeta{role: role} - } - snap := c.local.Snapshot.Signed - root := c.local.Root.Signed - r := c.keysDB.GetRole(role) - if r == nil { - return fmt.Errorf("Invalid role: %s", role) - } - keyIDs := r.KeyIDs - s, err := c.getTargetsFile(role, keyIDs, snap.Meta, root.ConsistentSnapshot, r.Threshold) - if err != nil { - logrus.Error("Error getting targets file:", err) - return err - } - t, err := data.TargetsFromSigned(s) - if err != nil { - return err - } - err = c.local.SetTargets(role, t) - if err != nil { - return err - } + stack := utils.NewStack() + stack.Push(role) + for !stack.Empty() { + role, err := stack.PopString() + if err != nil { + return err + } + role = data.RoleName(role) // this will really only do something for base targets role + if c.local.Snapshot == nil { + return ErrMissingMeta{role: role} + } + snap := c.local.Snapshot.Signed + root := c.local.Root.Signed + r := c.keysDB.GetRole(role) + if r == nil { + return fmt.Errorf("Invalid role: %s", role) + } + keyIDs := r.KeyIDs + s, err := c.getTargetsFile(role, keyIDs, snap.Meta, root.ConsistentSnapshot, r.Threshold) + if err != nil { + logrus.Error("Error getting targets file:", err) + return err + } + t, err := data.TargetsFromSigned(s) + if err != nil { + return err + } + err = c.local.SetTargets(role, t) + if err != nil { + return err + } + // push delegated roles contained in the targets file onto the stack + for _, r := range t.Signed.Delegations.Roles { + stack.Push(r.Name) + } + } return nil } diff --git a/tuf/utils/stack.go b/tuf/utils/stack.go new file mode 100644 index 0000000000..71c953d473 --- /dev/null +++ b/tuf/utils/stack.go @@ -0,0 +1,57 @@ +package utils + +import ( + "fmt" +) + +type ErrEmptyStack struct { + action string +} + +func (err ErrEmptyStack) Error() string { + return fmt.Sprintf("attempted to %s with empty stack", err.action) +} + +type ErrBadTypeCast struct{} + +func (err ErrBadTypeCast) Error() string { + return "attempted to do a typed pop and item was not of type" +} + +type Stack []interface{} + +func NewStack() *Stack { + s := make(Stack, 0) + return &s +} + +func (s *Stack) Push(item interface{}) { + *s = append(*s, item) +} + +func (s *Stack) Pop() (interface{}, error) { + l := len(*s) + if l > 0 { + item := (*s)[l-1] + *s = (*s)[:l-1] + return item, nil + } + return nil, ErrEmptyStack{action: "pop"} +} + +func (s *Stack) PopString() (string, error) { + l := len(*s) + if l > 0 { + item := (*s)[l-1] + if item, ok := item.(string); ok { + *s = (*s)[:l-1] + return item, nil + } + return "", ErrBadTypeCast{} + } + return "", ErrEmptyStack{action: "pop"} +} + +func (s Stack) Empty() bool { + return len(s) == 0 +}