From 02f214ce8200dd1e6a1b4a21762a7d4a422802af Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Wed, 5 Feb 2020 14:45:06 +0100 Subject: [PATCH] [v1.4.2-stable] update containers/image Note that this includes fixes for https://access.redhat.com/security/cve/CVE-2020-1702. Signed-off-by: Valentin Rothberg --- vendor.conf | 2 +- .../containers/image/docker/docker_client.go | 7 ++- .../image/docker/docker_image_dest.go | 3 +- .../image/docker/docker_image_src.go | 10 ++-- .../containers/image/docker/tarfile/dest.go | 3 +- .../containers/image/docker/tarfile/src.go | 9 +-- .../containers/image/image/docker_schema2.go | 4 +- .../github.com/containers/image/image/oci.go | 4 +- .../image/internal/iolimits/iolimits.go | 60 +++++++++++++++++++ .../containers/image/openshift/openshift.go | 4 +- 10 files changed, 86 insertions(+), 20 deletions(-) create mode 100644 vendor/github.com/containers/image/internal/iolimits/iolimits.go diff --git a/vendor.conf b/vendor.conf index eba52c0d20..faf5cdcf22 100644 --- a/vendor.conf +++ b/vendor.conf @@ -15,7 +15,7 @@ github.com/containerd/cgroups 4994991857f9b0ae8dc439551e8bebdbb4bf66c1 github.com/containerd/continuity 004b46473808b3e7a4a3049c20e4376c91eb966d github.com/containernetworking/cni v0.7.0-rc2 github.com/containernetworking/plugins v0.7.4 -github.com/containers/image v2.0.0 +github.com/containers/image podman-1.4.2 github.com/vbauerster/mpb v3.3.4 github.com/mattn/go-isatty v0.0.4 github.com/VividCortex/ewma v1.1.1 diff --git a/vendor/github.com/containers/image/docker/docker_client.go b/vendor/github.com/containers/image/docker/docker_client.go index 81a43e0fab..31697a443b 100644 --- a/vendor/github.com/containers/image/docker/docker_client.go +++ b/vendor/github.com/containers/image/docker/docker_client.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "net/url" "os" @@ -17,6 +16,7 @@ import ( "time" "github.com/containers/image/docker/reference" + "github.com/containers/image/internal/iolimits" "github.com/containers/image/pkg/docker/config" "github.com/containers/image/pkg/sysregistriesv2" "github.com/containers/image/pkg/tlsclientconfig" @@ -540,7 +540,7 @@ func (c *dockerClient) getBearerToken(ctx context.Context, challenge challenge, default: return nil, errors.Errorf("unexpected http code: %d (%s), URL: %s", res.StatusCode, http.StatusText(res.StatusCode), authReq.URL) } - tokenBlob, err := ioutil.ReadAll(res.Body) + tokenBlob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxAuthTokenBodySize) if err != nil { return nil, err } @@ -631,7 +631,8 @@ func (c *dockerClient) getExtensionsSignatures(ctx context.Context, ref dockerRe if res.StatusCode != http.StatusOK { return nil, errors.Wrapf(client.HandleErrorResponse(res), "Error downloading signatures for %s in %s", manifestDigest, ref.ref.Name()) } - body, err := ioutil.ReadAll(res.Body) + + body, err := iolimits.ReadAtMost(res.Body, iolimits.MaxSignatureListBodySize) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/docker/docker_image_dest.go b/vendor/github.com/containers/image/docker/docker_image_dest.go index c116cbec32..f2794ee8e2 100644 --- a/vendor/github.com/containers/image/docker/docker_image_dest.go +++ b/vendor/github.com/containers/image/docker/docker_image_dest.go @@ -15,6 +15,7 @@ import ( "strings" "github.com/containers/image/docker/reference" + "github.com/containers/image/internal/iolimits" "github.com/containers/image/manifest" "github.com/containers/image/pkg/blobinfocache/none" "github.com/containers/image/types" @@ -590,7 +591,7 @@ sigExists: } defer res.Body.Close() if res.StatusCode != http.StatusCreated { - body, err := ioutil.ReadAll(res.Body) + body, err := iolimits.ReadAtMost(res.Body, iolimits.MaxErrorBodySize) if err == nil { logrus.Debugf("Error body %s", string(body)) } diff --git a/vendor/github.com/containers/image/docker/docker_image_src.go b/vendor/github.com/containers/image/docker/docker_image_src.go index c43e6e7ca2..619446df44 100644 --- a/vendor/github.com/containers/image/docker/docker_image_src.go +++ b/vendor/github.com/containers/image/docker/docker_image_src.go @@ -12,6 +12,7 @@ import ( "strconv" "github.com/containers/image/docker/reference" + "github.com/containers/image/internal/iolimits" "github.com/containers/image/manifest" "github.com/containers/image/pkg/sysregistriesv2" "github.com/containers/image/types" @@ -148,7 +149,8 @@ func (s *dockerImageSource) fetchManifest(ctx context.Context, tagOrDigest strin if res.StatusCode != http.StatusOK { return nil, "", errors.Wrapf(client.HandleErrorResponse(res), "Error reading manifest %s in %s", tagOrDigest, s.ref.ref.Name()) } - manblob, err := ioutil.ReadAll(res.Body) + + manblob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxManifestBodySize) if err != nil { return nil, "", err } @@ -335,7 +337,7 @@ func (s *dockerImageSource) getOneSignature(ctx context.Context, url *url.URL) ( } else if res.StatusCode != http.StatusOK { return nil, false, errors.Errorf("Error reading signature from %s: status %d (%s)", url.String(), res.StatusCode, http.StatusText(res.StatusCode)) } - sig, err := ioutil.ReadAll(res.Body) + sig, err := iolimits.ReadAtMost(res.Body, iolimits.MaxSignatureBodySize) if err != nil { return nil, false, err } @@ -396,7 +398,7 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere return err } defer get.Body.Close() - manifestBody, err := ioutil.ReadAll(get.Body) + manifestBody, err := iolimits.ReadAtMost(get.Body, iolimits.MaxManifestBodySize) if err != nil { return err } @@ -419,7 +421,7 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere } defer delete.Body.Close() - body, err := ioutil.ReadAll(delete.Body) + body, err := iolimits.ReadAtMost(delete.Body, iolimits.MaxErrorBodySize) if err != nil { return err } diff --git a/vendor/github.com/containers/image/docker/tarfile/dest.go b/vendor/github.com/containers/image/docker/tarfile/dest.go index 5f30eddbc7..3557b0f9e0 100644 --- a/vendor/github.com/containers/image/docker/tarfile/dest.go +++ b/vendor/github.com/containers/image/docker/tarfile/dest.go @@ -13,6 +13,7 @@ import ( "time" "github.com/containers/image/docker/reference" + "github.com/containers/image/internal/iolimits" "github.com/containers/image/internal/tmpdir" "github.com/containers/image/manifest" "github.com/containers/image/types" @@ -135,7 +136,7 @@ func (d *Destination) PutBlob(ctx context.Context, stream io.Reader, inputInfo t } if isConfig { - buf, err := ioutil.ReadAll(stream) + buf, err := iolimits.ReadAtMost(stream, iolimits.MaxConfigBodySize) if err != nil { return types.BlobInfo{}, errors.Wrap(err, "Error reading Config file stream") } diff --git a/vendor/github.com/containers/image/docker/tarfile/src.go b/vendor/github.com/containers/image/docker/tarfile/src.go index 03735f8a4f..6e0f3570f8 100644 --- a/vendor/github.com/containers/image/docker/tarfile/src.go +++ b/vendor/github.com/containers/image/docker/tarfile/src.go @@ -12,6 +12,7 @@ import ( "sync" "github.com/containers/image/internal/tmpdir" + "github.com/containers/image/internal/iolimits" "github.com/containers/image/manifest" "github.com/containers/image/pkg/compression" "github.com/containers/image/types" @@ -187,13 +188,13 @@ func findTarComponent(inputFile io.Reader, path string) (*tar.Reader, *tar.Heade } // readTarComponent returns full contents of componentPath. -func (s *Source) readTarComponent(path string) ([]byte, error) { +func (s *Source) readTarComponent(path string, limit int) ([]byte, error) { file, err := s.openTarComponent(path) if err != nil { return nil, errors.Wrapf(err, "Error loading tar component %s", path) } defer file.Close() - bytes, err := ioutil.ReadAll(file) + bytes, err := iolimits.ReadAtMost(file, limit) if err != nil { return nil, err } @@ -217,7 +218,7 @@ func (s *Source) ensureCachedDataIsPresent() error { } // Read and parse config. - configBytes, err := s.readTarComponent(tarManifest[0].Config) + configBytes, err := s.readTarComponent(tarManifest[0].Config, iolimits.MaxConfigBodySize) if err != nil { s.cacheDataResult = err return @@ -247,7 +248,7 @@ func (s *Source) ensureCachedDataIsPresent() error { // loadTarManifest loads and decodes the manifest.json. func (s *Source) loadTarManifest() ([]ManifestItem, error) { // FIXME? Do we need to deal with the legacy format? - bytes, err := s.readTarComponent(manifestFileName) + bytes, err := s.readTarComponent(manifestFileName, iolimits.MaxTarFileManifestSize) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/image/docker_schema2.go b/vendor/github.com/containers/image/image/docker_schema2.go index 351e73ea1d..d5f9656d2f 100644 --- a/vendor/github.com/containers/image/image/docker_schema2.go +++ b/vendor/github.com/containers/image/image/docker_schema2.go @@ -6,10 +6,10 @@ import ( "crypto/sha256" "encoding/hex" "encoding/json" - "io/ioutil" "strings" "github.com/containers/image/docker/reference" + "github.com/containers/image/internal/iolimits" "github.com/containers/image/manifest" "github.com/containers/image/pkg/blobinfocache/none" "github.com/containers/image/types" @@ -101,7 +101,7 @@ func (m *manifestSchema2) ConfigBlob(ctx context.Context) ([]byte, error) { return nil, err } defer stream.Close() - blob, err := ioutil.ReadAll(stream) + blob, err := iolimits.ReadAtMost(stream, iolimits.MaxConfigBodySize) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/image/oci.go b/vendor/github.com/containers/image/image/oci.go index cdff26e06a..0c3cb18750 100644 --- a/vendor/github.com/containers/image/image/oci.go +++ b/vendor/github.com/containers/image/image/oci.go @@ -3,9 +3,9 @@ package image import ( "context" "encoding/json" - "io/ioutil" "github.com/containers/image/docker/reference" + "github.com/containers/image/internal/iolimits" "github.com/containers/image/manifest" "github.com/containers/image/pkg/blobinfocache/none" "github.com/containers/image/types" @@ -66,7 +66,7 @@ func (m *manifestOCI1) ConfigBlob(ctx context.Context) ([]byte, error) { return nil, err } defer stream.Close() - blob, err := ioutil.ReadAll(stream) + blob, err := iolimits.ReadAtMost(stream, iolimits.MaxConfigBodySize) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/internal/iolimits/iolimits.go b/vendor/github.com/containers/image/internal/iolimits/iolimits.go new file mode 100644 index 0000000000..3fed1995cb --- /dev/null +++ b/vendor/github.com/containers/image/internal/iolimits/iolimits.go @@ -0,0 +1,60 @@ +package iolimits + +import ( + "io" + "io/ioutil" + + "github.com/pkg/errors" +) + +// All constants below are intended to be used as limits for `ReadAtMost`. The +// immediate use-case for limiting the size of in-memory copied data is to +// protect against OOM DOS attacks as described inCVE-2020-1702. Instead of +// copying data until running out of memory, we error out after hitting the +// specified limit. +const ( + // megaByte denotes one megabyte and is intended to be used as a limit in + // `ReadAtMost`. + megaByte = 1 << 20 + // MaxManifestBodySize is the maximum allowed size of a manifest. The limit + // of 4 MB aligns with the one of a Docker registry: + // https://github.com/docker/distribution/blob/a8371794149d1d95f1e846744b05c87f2f825e5a/registry/handlers/manifests.go#L30 + MaxManifestBodySize = 4 * megaByte + // MaxAuthTokenBodySize is the maximum allowed size of an auth token. + // The limit of 1 MB is considered to be greatly sufficient. + MaxAuthTokenBodySize = megaByte + // MaxSignatureListBodySize is the maximum allowed size of a signature list. + // The limit of 4 MB is considered to be greatly sufficient. + MaxSignatureListBodySize = 4 * megaByte + // MaxSignatureBodySize is the maximum allowed size of a signature. + // The limit of 4 MB is considered to be greatly sufficient. + MaxSignatureBodySize = 4 * megaByte + // MaxErrorBodySize is the maximum allowed size of an error-response body. + // The limit of 1 MB is considered to be greatly sufficient. + MaxErrorBodySize = megaByte + // MaxConfigBodySize is the maximum allowed size of a config blob. + // The limit of 4 MB is considered to be greatly sufficient. + MaxConfigBodySize = 4 * megaByte + // MaxOpenShiftStatusBody is the maximum allowed size of an OpenShift status body. + // The limit of 4 MB is considered to be greatly sufficient. + MaxOpenShiftStatusBody = 4 * megaByte + // MaxTarFileManifestSize is the maximum allowed size of a (docker save)-like manifest (which may contain multiple images) + // The limit of 1 MB is considered to be greatly sufficient. + MaxTarFileManifestSize = megaByte +) + +// ReadAtMost reads from reader and errors out if the specified limit (in bytes) is exceeded. +func ReadAtMost(reader io.Reader, limit int) ([]byte, error) { + limitedReader := io.LimitReader(reader, int64(limit+1)) + + res, err := ioutil.ReadAll(limitedReader) + if err != nil { + return nil, err + } + + if len(res) > limit { + return nil, errors.Errorf("exceeded maximum allowed size of %d bytes", limit) + } + + return res, nil +} diff --git a/vendor/github.com/containers/image/openshift/openshift.go b/vendor/github.com/containers/image/openshift/openshift.go index 814c3eea1d..78c8ae4db7 100644 --- a/vendor/github.com/containers/image/openshift/openshift.go +++ b/vendor/github.com/containers/image/openshift/openshift.go @@ -7,13 +7,13 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "net/url" "strings" "github.com/containers/image/docker" "github.com/containers/image/docker/reference" + "github.com/containers/image/internal/iolimits" "github.com/containers/image/manifest" "github.com/containers/image/types" "github.com/containers/image/version" @@ -102,7 +102,7 @@ func (c *openshiftClient) doRequest(ctx context.Context, method, path string, re return nil, err } defer res.Body.Close() - body, err := ioutil.ReadAll(res.Body) + body, err := iolimits.ReadAtMost(res.Body, iolimits.MaxOpenShiftStatusBody) if err != nil { return nil, err }