Merge pull request #20523 from cyli/vendor-notary-version-for-docker-1.10.2

Bump the notary version to one that fixes a bug with delegation path traversal
diff --git a/Dockerfile b/Dockerfile
index 4d2c63d..0015136 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -88,7 +88,7 @@
 
 # Install Go
 # IMPORTANT: If the version of Go is updated, the Windows to Linux CI machines
-#            will need updating, to avoid errors. Ping #docker-maintainers on IRC 
+#            will need updating, to avoid errors. Ping #docker-maintainers on IRC
 #            with a heads-up.
 ENV GO_VERSION 1.5.3
 RUN curl -fsSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" \
@@ -168,7 +168,7 @@
 	&& rm -rf "$GOPATH"
 
 # Install notary server
-ENV NOTARY_VERSION docker-v1.10-5
+ENV NOTARY_VERSION docker-v1.10.2-1
 RUN set -x \
 	&& export GOPATH="$(mktemp -d)" \
 	&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
diff --git a/Dockerfile.armhf b/Dockerfile.armhf
index 9fb485b..95212e1 100644
--- a/Dockerfile.armhf
+++ b/Dockerfile.armhf
@@ -145,7 +145,7 @@
 	&& rm -rf "$GOPATH"
 
 # Install notary server
-ENV NOTARY_VERSION docker-v1.10-5
+ENV NOTARY_VERSION docker-v1.10.2-1
 RUN set -x \
 	&& export GOPATH="$(mktemp -d)" \
 	&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
diff --git a/Dockerfile.ppc64le b/Dockerfile.ppc64le
index 1c843b3..e31352f 100644
--- a/Dockerfile.ppc64le
+++ b/Dockerfile.ppc64le
@@ -116,7 +116,7 @@
 	&& rm -rf "$GOPATH"
 
 # Install notary server
-#ENV NOTARY_VERSION docker-v1.10-5
+#ENV NOTARY_VERSION docker-v1.10.2-1
 #RUN set -x \
 #	&& export GOPATH="$(mktemp -d)" \
 #	&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
diff --git a/Dockerfile.s390x b/Dockerfile.s390x
index b58d908..b627ad3 100644
--- a/Dockerfile.s390x
+++ b/Dockerfile.s390x
@@ -116,7 +116,7 @@
 	&& rm -rf "$GOPATH"
 
 # Install notary server
-ENV NOTARY_VERSION docker-v1.10-5
+ENV NOTARY_VERSION docker-v1.10.2-1
 RUN set -x \
 	&& export GOPATH="$(mktemp -d)" \
 	&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
diff --git a/hack/vendor.sh b/hack/vendor.sh
index b8af035..1cd1a38 100755
--- a/hack/vendor.sh
+++ b/hack/vendor.sh
@@ -50,7 +50,7 @@
 clone git github.com/vbatts/tar-split v0.9.11
 
 # get desired notary commit, might also need to be updated in Dockerfile
-clone git github.com/docker/notary docker-v1.10-5
+clone git github.com/docker/notary docker-v1.10.2-1
 
 clone git google.golang.org/grpc 174192fc93efcb188fc8f46ca447f0da606b6885 https://github.com/grpc/grpc-go.git
 clone git github.com/miekg/pkcs11 80f102b5cac759de406949c47f0928b99bd64cdf
diff --git a/vendor/src/github.com/docker/notary/client/client.go b/vendor/src/github.com/docker/notary/client/client.go
index b383c94..a8ff637 100644
--- a/vendor/src/github.com/docker/notary/client/client.go
+++ b/vendor/src/github.com/docker/notary/client/client.go
@@ -9,6 +9,7 @@
 	"net/url"
 	"os"
 	"path/filepath"
+	"strings"
 	"time"
 
 	"github.com/Sirupsen/logrus"
@@ -451,11 +452,48 @@
 		roles = []string{data.CanonicalTargetsRole}
 	}
 	targets := make(map[string]*TargetWithRole)
+
 	for _, role := range roles {
+		var foundRole *data.Role
+		walkRoles := []*data.Role{}
+		if role == data.CanonicalTargetsRole {
+			foundRole = &data.Role{
+				Name:             data.CanonicalTargetsRole,
+				Paths:            []string{""},
+				PathHashPrefixes: []string{""},
+			}
+		}
+
+		walkRoles = append(walkRoles, r.tufRepo.Targets[data.CanonicalTargetsRole].Signed.Delegations.Roles...)
+		for len(walkRoles) > 0 && foundRole == nil {
+			currRole := walkRoles[0]
+			walkRoles = walkRoles[1:]
+			if currRole.Name == role {
+				foundRole = currRole
+				break
+			}
+			if strings.HasPrefix(role, currRole.Name+"/") {
+				targetMeta, ok := r.tufRepo.Targets[currRole.Name]
+				if !ok {
+					continue
+				}
+				for _, childRole := range targetMeta.Signed.Delegations.Roles {
+					restricted, err := data.Restrict(*currRole, *childRole)
+					if err == nil {
+						walkRoles = append(walkRoles, restricted)
+					}
+				}
+			}
+		}
+
+		if foundRole == nil {
+			continue
+		}
+
 		// we don't need to do anything special with removing role from
 		// roles because listSubtree always processes role and only excludes
 		// descendant delegations that appear in roles.
-		r.listSubtree(targets, role, roles...)
+		r.listSubtree(targets, foundRole, roles...)
 	}
 
 	var targetList []*TargetWithRole
@@ -466,29 +504,32 @@
 	return targetList, nil
 }
 
-func (r *NotaryRepository) listSubtree(targets map[string]*TargetWithRole, role string, exclude ...string) {
+func (r *NotaryRepository) listSubtree(targets map[string]*TargetWithRole, role *data.Role, exclude ...string) {
 	excl := make(map[string]bool)
 	for _, r := range exclude {
 		excl[r] = true
 	}
-	roles := []string{role}
+	roles := []*data.Role{role}
 	for len(roles) > 0 {
 		role = roles[0]
 		roles = roles[1:]
-		tgts, ok := r.tufRepo.Targets[role]
+		tgts, ok := r.tufRepo.Targets[role.Name]
 		if !ok {
 			// not every role has to exist
 			continue
 		}
 		for name, meta := range tgts.Signed.Targets {
-			if _, ok := targets[name]; !ok {
+			if _, ok := targets[name]; !ok && role.CheckPaths(name) {
 				targets[name] = &TargetWithRole{
-					Target: Target{Name: name, Hashes: meta.Hashes, Length: meta.Length}, Role: role}
+					Target: Target{Name: name, Hashes: meta.Hashes, Length: meta.Length}, Role: role.Name}
 			}
 		}
-		for _, d := range tgts.Signed.Delegations.Roles {
-			if !excl[d.Name] {
-				roles = append(roles, d.Name)
+		for _, child := range tgts.Signed.Delegations.Roles {
+			if !excl[child.Name] {
+				child, err := data.Restrict(*role, *child)
+				if err == nil {
+					roles = append(roles, child)
+				}
 			}
 		}
 	}
@@ -511,10 +552,47 @@
 		roles = append(roles, data.CanonicalTargetsRole)
 	}
 	for _, role := range roles {
-		meta, foundRole := c.TargetMeta(role, name, roles...)
+
+		var foundRole *data.Role
+		walkRoles := []*data.Role{}
+		if role == data.CanonicalTargetsRole {
+			foundRole = &data.Role{
+				Name:             data.CanonicalTargetsRole,
+				Paths:            []string{""},
+				PathHashPrefixes: []string{""},
+			}
+		}
+
+		walkRoles = append(walkRoles, r.tufRepo.Targets[data.CanonicalTargetsRole].Signed.Delegations.Roles...)
+		for len(walkRoles) > 0 && foundRole == nil {
+			currRole := walkRoles[0]
+			walkRoles = walkRoles[1:]
+			if currRole.Name == role {
+				foundRole = currRole
+				break
+			}
+			if strings.HasPrefix(role, currRole.Name+"/") {
+				targetMeta, ok := r.tufRepo.Targets[currRole.Name]
+				if !ok {
+					continue
+				}
+				for _, childRole := range targetMeta.Signed.Delegations.Roles {
+					restricted, err := data.Restrict(*currRole, *childRole)
+					if err == nil && restricted.CheckPaths(name) {
+						walkRoles = append(walkRoles, restricted)
+					}
+				}
+			}
+		}
+
+		if foundRole == nil {
+			continue
+		}
+
+		meta, ownerName := c.TargetMeta(foundRole, name, roles...)
 		if meta != nil {
 			return &TargetWithRole{
-				Target: Target{Name: name, Hashes: meta.Hashes, Length: meta.Length}, Role: foundRole}, nil
+				Target: Target{Name: name, Hashes: meta.Hashes, Length: meta.Length}, Role: ownerName}, nil
 		}
 	}
 	return nil, fmt.Errorf("No trust data for %s", name)
diff --git a/vendor/src/github.com/docker/notary/tuf/client/client.go b/vendor/src/github.com/docker/notary/tuf/client/client.go
index 0eaa8c8..7903855 100644
--- a/vendor/src/github.com/docker/notary/tuf/client/client.go
+++ b/vendor/src/github.com/docker/notary/tuf/client/client.go
@@ -526,39 +526,44 @@
 
 // TargetMeta ensures the repo is up to date. It assumes downloadTargets
 // has already downloaded all delegated roles
-func (c Client) TargetMeta(role, path string, excludeRoles ...string) (*data.FileMeta, string) {
+func (c Client) TargetMeta(role *data.Role, path string, excludeRoles ...string) (*data.FileMeta, string) {
 	excl := make(map[string]bool)
 	for _, r := range excludeRoles {
 		excl[r] = true
 	}
 
-	pathDigest := sha256.Sum256([]byte(path))
-	pathHex := hex.EncodeToString(pathDigest[:])
-
 	// FIFO list of targets delegations to inspect for target
-	roles := []string{role}
+	roles := []*data.Role{role}
 	var (
 		meta *data.FileMeta
-		curr string
+		curr *data.Role
 	)
 	for len(roles) > 0 {
 		// have to do these lines here because of order of execution in for statement
 		curr = roles[0]
 		roles = roles[1:]
 
-		meta = c.local.TargetMeta(curr, path)
+		meta = c.local.TargetMeta(curr.Name, path)
 		if meta != nil {
 			// we found the target!
-			return meta, curr
+			return meta, curr.Name
 		}
-		delegations := c.local.TargetDelegations(curr, path, pathHex)
-		for _, d := range delegations {
-			if !excl[d.Name] {
-				roles = append(roles, d.Name)
+		tgts, ok := c.local.Targets[curr.Name]
+		if !ok {
+			// not every role has to exist
+			continue
+		}
+
+		for _, child := range tgts.Signed.Delegations.Roles {
+			if !excl[child.Name] {
+				child, err := data.Restrict(*curr, *child)
+				if err == nil && child.CheckPaths(path) {
+					roles = append(roles, child)
+				}
 			}
 		}
 	}
-	return meta, ""
+	return nil, ""
 }
 
 // DownloadTarget downloads the target to dst from the remote
diff --git a/vendor/src/github.com/docker/notary/tuf/data/roles.go b/vendor/src/github.com/docker/notary/tuf/data/roles.go
index a505c92..94fc65e 100644
--- a/vendor/src/github.com/docker/notary/tuf/data/roles.go
+++ b/vendor/src/github.com/docker/notary/tuf/data/roles.go
@@ -2,10 +2,11 @@
 
 import (
 	"fmt"
-	"github.com/Sirupsen/logrus"
 	"path"
 	"regexp"
 	"strings"
+
+	"github.com/Sirupsen/logrus"
 )
 
 // Canonical base role names
@@ -244,3 +245,40 @@
 	}
 	return keep
 }
+
+// Restrict restricts the paths and path hash prefixes for the passed in delegation role,
+// returning a copy of the role with validated paths as if it was a direct child
+func Restrict(parent, child Role) (*Role, error) {
+	if path.Dir(child.Name) != parent.Name {
+		return nil, fmt.Errorf("%s is not a parent of %s", parent.Name, child.Name)
+	}
+	return &Role{
+		RootRole: child.RootRole,
+		Name:     child.Name,
+		Paths:    RestrictDelegationPathPrefixes(parent.Paths, child.Paths),
+	}, nil
+}
+
+// RestrictDelegationPathPrefixes returns the list of valid delegationPaths that are prefixed by parentPaths
+func RestrictDelegationPathPrefixes(parentPaths, delegationPaths []string) []string {
+	validPaths := []string{}
+	if len(delegationPaths) == 0 {
+		return validPaths
+	}
+
+	// Validate each individual delegation path
+	for _, delgPath := range delegationPaths {
+		isPrefixed := false
+		for _, parentPath := range parentPaths {
+			if strings.HasPrefix(delgPath, parentPath) {
+				isPrefixed = true
+				break
+			}
+		}
+		// If the delegation path did not match prefix against any parent path, it is not valid
+		if isPrefixed {
+			validPaths = append(validPaths, delgPath)
+		}
+	}
+	return validPaths
+}
diff --git a/vendor/src/github.com/docker/notary/tuf/tuf.go b/vendor/src/github.com/docker/notary/tuf/tuf.go
index 96ab7da..109dce7 100644
--- a/vendor/src/github.com/docker/notary/tuf/tuf.go
+++ b/vendor/src/github.com/docker/notary/tuf/tuf.go
@@ -459,6 +459,9 @@
 		tr.keysDB.AddKey(k)
 	}
 	for _, r := range s.Signed.Delegations.Roles {
+		if path.Dir(r.Name) != role || tr.keysDB.GetRole(r.Name) != nil {
+			continue
+		}
 		tr.keysDB.AddRole(r)
 	}
 	tr.Targets[role] = s