Browse Source

Merge pull request from GHSA-xmmx-7jpf-fx42

[20.10] vendor: update github.com/docker/distribution and github.com/containerd/containerd
Sebastiaan van Stijn 3 years ago
parent
commit
10106a0f66

+ 35 - 8
distribution/manifest.go

@@ -3,6 +3,7 @@ package distribution
 import (
 import (
 	"context"
 	"context"
 	"encoding/json"
 	"encoding/json"
+	"fmt"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
 
 
@@ -11,7 +12,9 @@ import (
 	"github.com/containerd/containerd/log"
 	"github.com/containerd/containerd/log"
 	"github.com/containerd/containerd/remotes"
 	"github.com/containerd/containerd/remotes"
 	"github.com/docker/distribution"
 	"github.com/docker/distribution"
+	"github.com/docker/distribution/manifest/manifestlist"
 	"github.com/docker/distribution/manifest/schema1"
 	"github.com/docker/distribution/manifest/schema1"
+	"github.com/docker/distribution/manifest/schema2"
 	digest "github.com/opencontainers/go-digest"
 	digest "github.com/opencontainers/go-digest"
 	specs "github.com/opencontainers/image-spec/specs-go/v1"
 	specs "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
@@ -166,8 +169,10 @@ func detectManifestMediaType(ra content.ReaderAt) (string, error) {
 func detectManifestBlobMediaType(dt []byte) (string, error) {
 func detectManifestBlobMediaType(dt []byte) (string, error) {
 	var mfst struct {
 	var mfst struct {
 		MediaType string          `json:"mediaType"`
 		MediaType string          `json:"mediaType"`
-		Config    json.RawMessage `json:"config"`   // schema2 Manifest
-		FSLayers  json.RawMessage `json:"fsLayers"` // schema1 Manifest
+		Manifests json.RawMessage `json:"manifests"` // oci index, manifest list
+		Config    json.RawMessage `json:"config"`    // schema2 Manifest
+		Layers    json.RawMessage `json:"layers"`    // schema2 Manifest
+		FSLayers  json.RawMessage `json:"fsLayers"`  // schema1 Manifest
 	}
 	}
 
 
 	if err := json.Unmarshal(dt, &mfst); err != nil {
 	if err := json.Unmarshal(dt, &mfst); err != nil {
@@ -178,18 +183,40 @@ func detectManifestBlobMediaType(dt []byte) (string, error) {
 	// Docker types should generally have a media type set.
 	// Docker types should generally have a media type set.
 	// OCI (golang) types do not have a `mediaType` defined, and it is optional in the spec.
 	// OCI (golang) types do not have a `mediaType` defined, and it is optional in the spec.
 	//
 	//
-	// `distrubtion.UnmarshalManifest`, which is used to unmarshal this for real, checks these media type values.
+	// `distribution.UnmarshalManifest`, which is used to unmarshal this for real, checks these media type values.
 	// If the specified media type does not match it will error, and in some cases (docker media types) it is required.
 	// If the specified media type does not match it will error, and in some cases (docker media types) it is required.
 	// So pretty much if we don't have a media type we can fall back to OCI.
 	// So pretty much if we don't have a media type we can fall back to OCI.
 	// This does have a special fallback for schema1 manifests just because it is easy to detect.
 	// This does have a special fallback for schema1 manifests just because it is easy to detect.
-	switch {
-	case mfst.MediaType != "":
+	switch mfst.MediaType {
+	case schema2.MediaTypeManifest, specs.MediaTypeImageManifest:
+		if mfst.Manifests != nil || mfst.FSLayers != nil {
+			return "", fmt.Errorf(`media-type: %q should not have "manifests" or "fsLayers"`, mfst.MediaType)
+		}
+		return mfst.MediaType, nil
+	case manifestlist.MediaTypeManifestList, specs.MediaTypeImageIndex:
+		if mfst.Config != nil || mfst.Layers != nil || mfst.FSLayers != nil {
+			return "", fmt.Errorf(`media-type: %q should not have "config", "layers", or "fsLayers"`, mfst.MediaType)
+		}
+		return mfst.MediaType, nil
+	case schema1.MediaTypeManifest:
+		if mfst.Manifests != nil || mfst.Layers != nil {
+			return "", fmt.Errorf(`media-type: %q should not have "manifests" or "layers"`, mfst.MediaType)
+		}
 		return mfst.MediaType, nil
 		return mfst.MediaType, nil
-	case mfst.FSLayers != nil:
+	default:
+		if mfst.MediaType != "" {
+			return mfst.MediaType, nil
+		}
+	}
+	switch {
+	case mfst.FSLayers != nil && mfst.Manifests == nil && mfst.Layers == nil && mfst.Config == nil:
 		return schema1.MediaTypeManifest, nil
 		return schema1.MediaTypeManifest, nil
-	case mfst.Config != nil:
+	case mfst.Config != nil && mfst.Manifests == nil && mfst.FSLayers == nil,
+		mfst.Layers != nil && mfst.Manifests == nil && mfst.FSLayers == nil:
 		return specs.MediaTypeImageManifest, nil
 		return specs.MediaTypeImageManifest, nil
-	default:
+	case mfst.Config == nil && mfst.Layers == nil && mfst.FSLayers == nil:
+		// fallback to index
 		return specs.MediaTypeImageIndex, nil
 		return specs.MediaTypeImageIndex, nil
 	}
 	}
+	return "", errors.New("media-type: cannot determine")
 }
 }

+ 72 - 0
distribution/manifest_test.go

@@ -14,8 +14,10 @@ import (
 	"github.com/containerd/containerd/errdefs"
 	"github.com/containerd/containerd/errdefs"
 	"github.com/containerd/containerd/remotes"
 	"github.com/containerd/containerd/remotes"
 	"github.com/docker/distribution"
 	"github.com/docker/distribution"
+	"github.com/docker/distribution/manifest/manifestlist"
 	"github.com/docker/distribution/manifest/ocischema"
 	"github.com/docker/distribution/manifest/ocischema"
 	"github.com/docker/distribution/manifest/schema1"
 	"github.com/docker/distribution/manifest/schema1"
+	"github.com/docker/distribution/manifest/schema2"
 	"github.com/google/go-cmp/cmp/cmpopts"
 	"github.com/google/go-cmp/cmp/cmpopts"
 	digest "github.com/opencontainers/go-digest"
 	digest "github.com/opencontainers/go-digest"
 	specs "github.com/opencontainers/image-spec/specs-go/v1"
 	specs "github.com/opencontainers/image-spec/specs-go/v1"
@@ -349,3 +351,73 @@ func TestDetectManifestBlobMediaType(t *testing.T) {
 	}
 	}
 
 
 }
 }
+
+func TestDetectManifestBlobMediaTypeInvalid(t *testing.T) {
+	type testCase struct {
+		json     []byte
+		expected string
+	}
+	cases := map[string]testCase{
+		"schema 1 mediaType with manifests": {
+			[]byte(`{"mediaType": "` + schema1.MediaTypeManifest + `","manifests":[]}`),
+			`media-type: "application/vnd.docker.distribution.manifest.v1+json" should not have "manifests" or "layers"`,
+		},
+		"schema 1 mediaType with layers": {
+			[]byte(`{"mediaType": "` + schema1.MediaTypeManifest + `","layers":[]}`),
+			`media-type: "application/vnd.docker.distribution.manifest.v1+json" should not have "manifests" or "layers"`,
+		},
+		"schema 2 mediaType with manifests": {
+			[]byte(`{"mediaType": "` + schema2.MediaTypeManifest + `","manifests":[]}`),
+			`media-type: "application/vnd.docker.distribution.manifest.v2+json" should not have "manifests" or "fsLayers"`,
+		},
+		"schema 2 mediaType with fsLayers": {
+			[]byte(`{"mediaType": "` + schema2.MediaTypeManifest + `","fsLayers":[]}`),
+			`media-type: "application/vnd.docker.distribution.manifest.v2+json" should not have "manifests" or "fsLayers"`,
+		},
+		"oci manifest mediaType with manifests": {
+			[]byte(`{"mediaType": "` + specs.MediaTypeImageManifest + `","manifests":[]}`),
+			`media-type: "application/vnd.oci.image.manifest.v1+json" should not have "manifests" or "fsLayers"`,
+		},
+		"manifest list mediaType with fsLayers": {
+			[]byte(`{"mediaType": "` + manifestlist.MediaTypeManifestList + `","fsLayers":[]}`),
+			`media-type: "application/vnd.docker.distribution.manifest.list.v2+json" should not have "config", "layers", or "fsLayers"`,
+		},
+		"index mediaType with layers": {
+			[]byte(`{"mediaType": "` + specs.MediaTypeImageIndex + `","layers":[]}`),
+			`media-type: "application/vnd.oci.image.index.v1+json" should not have "config", "layers", or "fsLayers"`,
+		},
+		"index mediaType with config": {
+			[]byte(`{"mediaType": "` + specs.MediaTypeImageIndex + `","config":{}}`),
+			`media-type: "application/vnd.oci.image.index.v1+json" should not have "config", "layers", or "fsLayers"`,
+		},
+		"config and manifests": {
+			[]byte(`{"config":{}, "manifests":[]}`),
+			`media-type: cannot determine`,
+		},
+		"layers and manifests": {
+			[]byte(`{"layers":[], "manifests":[]}`),
+			`media-type: cannot determine`,
+		},
+		"layers and fsLayers": {
+			[]byte(`{"layers":[], "fsLayers":[]}`),
+			`media-type: cannot determine`,
+		},
+		"fsLayers and manifests": {
+			[]byte(`{"fsLayers":[], "manifests":[]}`),
+			`media-type: cannot determine`,
+		},
+		"config and fsLayers": {
+			[]byte(`{"config":{}, "fsLayers":[]}`),
+			`media-type: cannot determine`,
+		},
+	}
+
+	for name, tc := range cases {
+		t.Run(name, func(t *testing.T) {
+			mt, err := detectManifestBlobMediaType(tc.json)
+			assert.Error(t, err, tc.expected)
+			assert.Equal(t, mt, "")
+		})
+	}
+
+}

+ 2 - 2
vendor.conf

@@ -76,7 +76,7 @@ github.com/ishidawataru/sctp                        f2269e66cdee387bd321445d5d30
 go.etcd.io/bbolt                                    232d8fc87f50244f9c808f4745759e08a304c029 # v1.3.5
 go.etcd.io/bbolt                                    232d8fc87f50244f9c808f4745759e08a304c029 # v1.3.5
 
 
 # get graph and distribution packages
 # get graph and distribution packages
-github.com/docker/distribution                      0d3efadf0154c2b8a4e7b6621fff9809655cc580
+github.com/docker/distribution                      58f99e93b767ebacbf8e62a9074844712d31a177 github.com/samuelkarp/docker-distribution
 github.com/vbatts/tar-split                         620714a4c508c880ac1bdda9c8370a2b19af1a55 # v0.11.1
 github.com/vbatts/tar-split                         620714a4c508c880ac1bdda9c8370a2b19af1a55 # v0.11.1
 github.com/opencontainers/go-digest                 ea51bea511f75cfa3ef6098cc253c5c3609b037a # v1.0.0
 github.com/opencontainers/go-digest                 ea51bea511f75cfa3ef6098cc253c5c3609b037a # v1.0.0
 
 
@@ -130,7 +130,7 @@ github.com/googleapis/gax-go                        bd5b16380fd03dc758d11cef74ba
 google.golang.org/genproto                          3f1135a288c9a07e340ae8ba4cc6c7065a3160e8
 google.golang.org/genproto                          3f1135a288c9a07e340ae8ba4cc6c7065a3160e8
 
 
 # containerd
 # containerd
-github.com/containerd/containerd                    0edc412565dcc6e3d6125ff9e4b009ad4b89c638 # master (v1.5.0-dev)
+github.com/containerd/containerd                    e048c115a3a89caf63941d363858e207c28bccd6 github.com/moby/containerd # master (v1.5.0-dev) + patch for CVE-2021-41190
 github.com/containerd/fifo                          0724c46b320cf96bb172a0550c19a4b1fca4dacb
 github.com/containerd/fifo                          0724c46b320cf96bb172a0550c19a4b1fca4dacb
 github.com/containerd/continuity                    efbc4488d8fe1bdc16bde3b2d2990d9b3a899165
 github.com/containerd/continuity                    efbc4488d8fe1bdc16bde3b2d2990d9b3a899165
 github.com/containerd/cgroups                       0b889c03f102012f1d93a97ddd3ef71cd6f4f510
 github.com/containerd/cgroups                       0b889c03f102012f1d93a97ddd3ef71cd6f4f510

+ 55 - 0
vendor/github.com/containerd/containerd/images/image.go

@@ -19,6 +19,7 @@ package images
 import (
 import (
 	"context"
 	"context"
 	"encoding/json"
 	"encoding/json"
+	"fmt"
 	"sort"
 	"sort"
 	"time"
 	"time"
 
 
@@ -154,6 +155,10 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc
 				return nil, err
 				return nil, err
 			}
 			}
 
 
+			if err := validateMediaType(p, desc.MediaType); err != nil {
+				return nil, errors.Wrapf(err, "manifest: invalid desc %s", desc.Digest)
+			}
+
 			var manifest ocispec.Manifest
 			var manifest ocispec.Manifest
 			if err := json.Unmarshal(p, &manifest); err != nil {
 			if err := json.Unmarshal(p, &manifest); err != nil {
 				return nil, err
 				return nil, err
@@ -194,6 +199,10 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc
 				return nil, err
 				return nil, err
 			}
 			}
 
 
+			if err := validateMediaType(p, desc.MediaType); err != nil {
+				return nil, errors.Wrapf(err, "manifest: invalid desc %s", desc.Digest)
+			}
+
 			var idx ocispec.Index
 			var idx ocispec.Index
 			if err := json.Unmarshal(p, &idx); err != nil {
 			if err := json.Unmarshal(p, &idx); err != nil {
 				return nil, err
 				return nil, err
@@ -336,6 +345,10 @@ func Children(ctx context.Context, provider content.Provider, desc ocispec.Descr
 			return nil, err
 			return nil, err
 		}
 		}
 
 
+		if err := validateMediaType(p, desc.MediaType); err != nil {
+			return nil, errors.Wrapf(err, "children: invalid desc %s", desc.Digest)
+		}
+
 		// TODO(stevvooe): We just assume oci manifest, for now. There may be
 		// TODO(stevvooe): We just assume oci manifest, for now. There may be
 		// subtle differences from the docker version.
 		// subtle differences from the docker version.
 		var manifest ocispec.Manifest
 		var manifest ocispec.Manifest
@@ -351,6 +364,10 @@ func Children(ctx context.Context, provider content.Provider, desc ocispec.Descr
 			return nil, err
 			return nil, err
 		}
 		}
 
 
+		if err := validateMediaType(p, desc.MediaType); err != nil {
+			return nil, errors.Wrapf(err, "children: invalid desc %s", desc.Digest)
+		}
+
 		var index ocispec.Index
 		var index ocispec.Index
 		if err := json.Unmarshal(p, &index); err != nil {
 		if err := json.Unmarshal(p, &index); err != nil {
 			return nil, err
 			return nil, err
@@ -368,6 +385,44 @@ func Children(ctx context.Context, provider content.Provider, desc ocispec.Descr
 	return descs, nil
 	return descs, nil
 }
 }
 
 
+// unknownDocument represents a manifest, manifest list, or index that has not
+// yet been validated.
+type unknownDocument struct {
+	MediaType string          `json:"mediaType,omitempty"`
+	Config    json.RawMessage `json:"config,omitempty"`
+	Layers    json.RawMessage `json:"layers,omitempty"`
+	Manifests json.RawMessage `json:"manifests,omitempty"`
+	FSLayers  json.RawMessage `json:"fsLayers,omitempty"` // schema 1
+}
+
+// validateMediaType returns an error if the byte slice is invalid JSON or if
+// the media type identifies the blob as one format but it contains elements of
+// another format.
+func validateMediaType(b []byte, mt string) error {
+	var doc unknownDocument
+	if err := json.Unmarshal(b, &doc); err != nil {
+		return err
+	}
+	if len(doc.FSLayers) != 0 {
+		return fmt.Errorf("media-type: schema 1 not supported")
+	}
+	switch mt {
+	case MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
+		if len(doc.Manifests) != 0 ||
+			doc.MediaType == MediaTypeDockerSchema2ManifestList ||
+			doc.MediaType == ocispec.MediaTypeImageIndex {
+			return fmt.Errorf("media-type: expected manifest but found index (%s)", mt)
+		}
+	case MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
+		if len(doc.Config) != 0 || len(doc.Layers) != 0 ||
+			doc.MediaType == MediaTypeDockerSchema2Manifest ||
+			doc.MediaType == ocispec.MediaTypeImageManifest {
+			return fmt.Errorf("media-type: expected index but found manifest (%s)", mt)
+		}
+	}
+	return nil
+}
+
 // RootFS returns the unpacked diffids that make up and images rootfs.
 // RootFS returns the unpacked diffids that make up and images rootfs.
 //
 //
 // These are used to verify that a set of layers unpacked to the expected
 // These are used to verify that a set of layers unpacked to the expected

+ 7 - 2
vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go

@@ -256,6 +256,9 @@ func (c *Converter) fetchManifest(ctx context.Context, desc ocispec.Descriptor)
 	if err := json.Unmarshal(b, &m); err != nil {
 	if err := json.Unmarshal(b, &m); err != nil {
 		return err
 		return err
 	}
 	}
+	if len(m.Manifests) != 0 || len(m.Layers) != 0 {
+		return errors.New("converter: expected schema1 document but found extra keys")
+	}
 	c.pulledManifest = &m
 	c.pulledManifest = &m
 
 
 	return nil
 	return nil
@@ -472,8 +475,10 @@ type history struct {
 }
 }
 
 
 type manifest struct {
 type manifest struct {
-	FSLayers []fsLayer `json:"fsLayers"`
-	History  []history `json:"history"`
+	FSLayers  []fsLayer       `json:"fsLayers"`
+	History   []history       `json:"history"`
+	Layers    json.RawMessage `json:"layers,omitempty"`    // OCI manifest
+	Manifests json.RawMessage `json:"manifests,omitempty"` // OCI index
 }
 }
 
 
 type v1History struct {
 type v1History struct {

+ 23 - 0
vendor/github.com/docker/distribution/manifest/manifestlist/manifestlist.go

@@ -54,6 +54,9 @@ func init() {
 	}
 	}
 
 
 	imageIndexFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
 	imageIndexFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
+		if err := validateIndex(b); err != nil {
+			return nil, distribution.Descriptor{}, err
+		}
 		m := new(DeserializedManifestList)
 		m := new(DeserializedManifestList)
 		err := m.UnmarshalJSON(b)
 		err := m.UnmarshalJSON(b)
 		if err != nil {
 		if err != nil {
@@ -214,3 +217,23 @@ func (m DeserializedManifestList) Payload() (string, []byte, error) {
 
 
 	return mediaType, m.canonical, nil
 	return mediaType, m.canonical, nil
 }
 }
+
+// unknownDocument represents a manifest, manifest list, or index that has not
+// yet been validated
+type unknownDocument struct {
+	Config interface{} `json:"config,omitempty"`
+	Layers interface{} `json:"layers,omitempty"`
+}
+
+// validateIndex returns an error if the byte slice is invalid JSON or if it
+// contains fields that belong to a manifest
+func validateIndex(b []byte) error {
+	var doc unknownDocument
+	if err := json.Unmarshal(b, &doc); err != nil {
+		return err
+	}
+	if doc.Config != nil || doc.Layers != nil {
+		return errors.New("index: expected index but found manifest")
+	}
+	return nil
+}

+ 22 - 0
vendor/github.com/docker/distribution/manifest/ocischema/manifest.go

@@ -22,6 +22,9 @@ var (
 
 
 func init() {
 func init() {
 	ocischemaFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
 	ocischemaFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
+		if err := validateManifest(b); err != nil {
+			return nil, distribution.Descriptor{}, err
+		}
 		m := new(DeserializedManifest)
 		m := new(DeserializedManifest)
 		err := m.UnmarshalJSON(b)
 		err := m.UnmarshalJSON(b)
 		if err != nil {
 		if err != nil {
@@ -122,3 +125,22 @@ func (m *DeserializedManifest) MarshalJSON() ([]byte, error) {
 func (m DeserializedManifest) Payload() (string, []byte, error) {
 func (m DeserializedManifest) Payload() (string, []byte, error) {
 	return v1.MediaTypeImageManifest, m.canonical, nil
 	return v1.MediaTypeImageManifest, m.canonical, nil
 }
 }
+
+// unknownDocument represents a manifest, manifest list, or index that has not
+// yet been validated
+type unknownDocument struct {
+	Manifests interface{} `json:"manifests,omitempty"`
+}
+
+// validateManifest returns an error if the byte slice is invalid JSON or if it
+// contains fields that belong to a index
+func validateManifest(b []byte) error {
+	var doc unknownDocument
+	if err := json.Unmarshal(b, &doc); err != nil {
+		return err
+	}
+	if doc.Manifests != nil {
+		return errors.New("ocimanifest: expected manifest but found index")
+	}
+	return nil
+}