Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-15-SP2:Update
podman
CVE-2024-3727.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File CVE-2024-3727.patch of Package podman
From 4079fb13d7bd002c5f87ed435cd4233ffe537136 Mon Sep 17 00:00:00 2001 From: Danish Prakash <contact@danishpraka.sh> Date: Tue, 2 Jul 2024 13:22:15 +0530 Subject: [PATCH 1/4] Validate the tags returned by a registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Miloslav Trmač <mitr@redhat.com> Signed-off-by: Danish Prakash <contact@danishpraka.sh> --- .../github.com/containers/image/v5/docker/docker_image.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/vendor/github.com/containers/image/v5/docker/docker_image.go b/vendor/github.com/containers/image/v5/docker/docker_image.go index 479effa593d9..2f3f4fad9915 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image.go @@ -80,7 +80,12 @@ func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types. if err = json.NewDecoder(res.Body).Decode(&tagsHolder); err != nil { return nil, err } - tags = append(tags, tagsHolder.Tags...) + for _, tag := range tagsHolder.Tags { + if _, err := reference.WithTag(dr.ref, tag); err != nil { // Ensure the tag does not contain unexpected values + return nil, fmt.Errorf("registry returned invalid tag %q: %w", tag, err) + } + tags = append(tags, tag) + } link := res.Header.Get("Link") if link == "" { -- 2.45.2 From 70109ddc29a3e7f7551e2086b3a07f5e41aaf9ff Mon Sep 17 00:00:00 2001 From: Danish Prakash <contact@danishpraka.sh> Date: Tue, 2 Jul 2024 13:24:52 +0530 Subject: [PATCH 2/4] Call .Validate() before digest.Digest.String() if necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit to prevent unexpected behavior on invalid values. Signed-off-by: Miloslav Trmač <mitr@redhat.com> Signed-off-by: Danish Prakash <contact@danishpraka.sh> --- .../image/v5/docker/docker_client.go | 3 + .../image/v5/docker/docker_image_dest.go | 18 ++++-- .../image/v5/docker/docker_image_src.go | 16 +++++- .../image/v5/docker/internal/tarfile/dest.go | 12 +++- .../v5/docker/internal/tarfile/writer.go | 34 +++++++++--- .../containers/image/v5/docker/lookaside.go | 9 ++- .../image/v5/openshift/openshift.go | 3 + .../image/v5/storage/storage_image.go | 55 ++++++++++++++----- .../image/v5/storage/storage_reference.go | 16 +++++- 9 files changed, 132 insertions(+), 34 deletions(-) diff --git a/vendor/github.com/containers/image/v5/docker/docker_client.go b/vendor/github.com/containers/image/v5/docker/docker_client.go index e4308def1f33..e3bc5ce92a58 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_client.go +++ b/vendor/github.com/containers/image/v5/docker/docker_client.go @@ -786,6 +786,9 @@ func (c *dockerClient) detectProperties(ctx context.Context) error { // getExtensionsSignatures returns signatures from the X-Registry-Supports-Signatures API extension, // using the original data structures. func (c *dockerClient) getExtensionsSignatures(ctx context.Context, ref dockerReference, manifestDigest digest.Digest) (*extensionSignatureList, error) { + if err := manifestDigest.Validate(); err != nil { // Make sure manifestDigest.String() does not contain any unexpected characters + return nil, err + } path := fmt.Sprintf(extensionsSignaturePath, reference.Path(ref.ref), manifestDigest) res, err := c.makeRequest(ctx, "GET", path, nil, nil, v2Auth, nil) if err != nil { diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go index 576dec4955b5..bf1da220449b 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go @@ -212,6 +212,9 @@ func (d *dockerImageDestination) PutBlob(ctx context.Context, stream io.Reader, // If the destination does not contain the blob, or it is unknown, blobExists ordinarily returns (false, -1, nil); // it returns a non-nil error only on an unexpected failure. func (d *dockerImageDestination) blobExists(ctx context.Context, repo reference.Named, digest digest.Digest, extraScope *authScope) (bool, int64, error) { + if err := digest.Validate(); err != nil { // Make sure digest.String() does not contain any unexpected characters + return false, -1, err + } checkPath := fmt.Sprintf(blobsPath, reference.Path(repo), digest.String()) logrus.Debugf("Checking %s", checkPath) res, err := d.c.makeRequest(ctx, "HEAD", checkPath, nil, nil, v2Auth, extraScope) @@ -373,6 +376,7 @@ func (d *dockerImageDestination) PutManifest(ctx context.Context, m []byte, inst // particular instance. refTail = instanceDigest.String() // Double-check that the manifest we've been given matches the digest we've been given. + // This also validates the format of instanceDigest. matches, err := manifest.MatchesDigest(m, *instanceDigest) if err != nil { return errors.Wrapf(err, "error digesting manifest in PutManifest") @@ -501,12 +505,14 @@ func (d *dockerImageDestination) putSignaturesToLookaside(signatures [][]byte, m // NOTE: Keep this in sync with docs/signature-protocols.md! for i, signature := range signatures { - url := signatureStorageURL(d.c.signatureBase, manifestDigest, i) + url, err := signatureStorageURL(d.c.signatureBase, manifestDigest, i) + if err != nil { + return err + } if url == nil { return errors.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") } - err := d.putOneSignature(url, signature) - if err != nil { + if err := d.putOneSignature(url, signature); err != nil { return err } } @@ -516,7 +522,10 @@ func (d *dockerImageDestination) putSignaturesToLookaside(signatures [][]byte, m // is enough for dockerImageSource to stop looking for other signatures, so that // is sufficient. for i := len(signatures); ; i++ { - url := signatureStorageURL(d.c.signatureBase, manifestDigest, i) + url, err := signatureStorageURL(d.c.signatureBase, manifestDigest, i) + if err != nil { + return err + } if url == nil { return errors.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") } @@ -628,6 +637,7 @@ sigExists: return err } + // manifestDigest is known to be valid because it was not rejected by getExtensionsSignatures above. path := fmt.Sprintf(extensionsSignaturePath, reference.Path(d.ref.ref), manifestDigest.String()) res, err := d.c.makeRequest(ctx, "PUT", path, nil, bytes.NewReader(body), v2Auth, nil) if err != nil { diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_src.go b/vendor/github.com/containers/image/v5/docker/docker_image_src.go index 4d2a9ed6c404..47e52aa10df1 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image_src.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image_src.go @@ -171,6 +171,9 @@ func simplifyContentType(contentType string) string { // this never happens if the primary manifest is not a manifest list (e.g. if the source never returns manifest lists). func (s *dockerImageSource) GetManifest(ctx context.Context, instanceDigest *digest.Digest) ([]byte, string, error) { if instanceDigest != nil { + if err := instanceDigest.Validate(); err != nil { // Make sure instanceDigest.String() does not contain any unexpected characters + return nil, "", err + } return s.fetchManifest(ctx, instanceDigest.String()) } err := s.ensureManifestIsLoaded(ctx) @@ -275,6 +278,9 @@ func (s *dockerImageSource) GetBlob(ctx context.Context, info types.BlobInfo, ca return s.getExternalBlob(ctx, info.URLs) } + if err := info.Digest.Validate(); err != nil { // Make sure info.Digest.String() does not contain any unexpected characters + return nil, 0, err + } path := fmt.Sprintf(blobsPath, reference.Path(s.physicalRef.ref), info.Digest.String()) logrus.Debugf("Downloading %s", path) res, err := s.c.makeRequest(ctx, "GET", path, nil, nil, v2Auth, nil) @@ -335,7 +341,10 @@ func (s *dockerImageSource) getSignaturesFromLookaside(ctx context.Context, inst // NOTE: Keep this in sync with docs/signature-protocols.md! signatures := [][]byte{} for i := 0; ; i++ { - url := signatureStorageURL(s.c.signatureBase, manifestDigest, i) + url, err := signatureStorageURL(s.c.signatureBase, manifestDigest, i) + if err != nil { + return err + } if url == nil { return nil, errors.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") } @@ -481,7 +490,10 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere } for i := 0; ; i++ { - url := signatureStorageURL(c.signatureBase, manifestDigest, i) + url, err := signatureStorageURL(c.signatureBase, manifestDigest, i) + if err != nil { + return err + } if url == nil { return errors.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") } diff --git a/vendor/github.com/containers/image/v5/docker/internal/tarfile/dest.go b/vendor/github.com/containers/image/v5/docker/internal/tarfile/dest.go index 8c38094cfab2..c151d4970687 100644 --- a/vendor/github.com/containers/image/v5/docker/internal/tarfile/dest.go +++ b/vendor/github.com/containers/image/v5/docker/internal/tarfile/dest.go @@ -143,11 +143,19 @@ func (d *Destination) PutBlob(ctx context.Context, stream io.Reader, inputInfo t return types.BlobInfo{}, errors.Wrap(err, "Error reading Config file stream") } d.config = buf - if err := d.archive.sendFileLocked(d.archive.configPath(inputInfo.Digest), inputInfo.Size, bytes.NewReader(buf)); err != nil { + configPath, err := d.archive.configPath(inputInfo.Digest) + if err != nil { + return types.BlobInfo{}, err + } + if err := d.archive.sendFileLocked(configPath, inputInfo.Size, bytes.NewReader(buf)); err != nil { return types.BlobInfo{}, errors.Wrap(err, "Error writing Config file") } } else { - if err := d.archive.sendFileLocked(d.archive.physicalLayerPath(inputInfo.Digest), inputInfo.Size, stream); err != nil { + layerPath, err := d.archive.physicalLayerPath(inputInfo.Digest) + if err != nil { + return types.BlobInfo{}, err + } + if err := d.archive.sendFileLocked(layerPath, inputInfo.Size, stream); err != nil { return types.BlobInfo{}, err } } diff --git a/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go b/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go index fd2c461d0761..ef73fe04b44a 100644 --- a/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go +++ b/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go @@ -92,7 +92,10 @@ func (w *Writer) ensureSingleLegacyLayerLocked(layerID string, layerDigest diges if _, ok := w.legacyLayers[layerID]; !ok { // Create a symlink for the legacy format, where there is one subdirectory per layer ("image"). // See also the comment in physicalLayerPath. - physicalLayerPath := w.physicalLayerPath(layerDigest) + physicalLayerPath, err := w.physicalLayerPath(layerDigest) + if err != nil { + return err + } if err := w.sendSymlinkLocked(filepath.Join(layerID, legacyLayerFileName), filepath.Join("..", physicalLayerPath)); err != nil { return errors.Wrap(err, "Error creating layer symbolic link") } @@ -136,6 +139,9 @@ func (w *Writer) writeLegacyMetadataLocked(layerDescriptors []manifest.Schema2De } // This chainID value matches the computation in docker/docker/layer.CreateChainID … + if err := l.Digest.Validate(); err != nil { // This should never fail on this code path, still: make sure the chainID computation is unambiguous. + return err + } if chainID == "" { chainID = l.Digest } else { @@ -206,12 +212,20 @@ func checkManifestItemsMatch(a, b *ManifestItem) error { func (w *Writer) ensureManifestItemLocked(layerDescriptors []manifest.Schema2Descriptor, configDigest digest.Digest, repoTags []reference.NamedTagged) error { layerPaths := []string{} for _, l := range layerDescriptors { - layerPaths = append(layerPaths, w.physicalLayerPath(l.Digest)) + p, err := w.physicalLayerPath(l.Digest) + if err != nil { + return err + } + layerPaths = append(layerPaths, p) } var item *ManifestItem + configPath, err := w.configPath(configDigest) + if err != nil { + return err + } newItem := ManifestItem{ - Config: w.configPath(configDigest), + Config: configPath, RepoTags: []string{}, Layers: layerPaths, Parent: "", // We don’t have this information @@ -296,21 +310,27 @@ func (w *Writer) Close() error { // configPath returns a path we choose for storing a config with the specified digest. // NOTE: This is an internal implementation detail, not a format property, and can change // any time. -func (w *Writer) configPath(configDigest digest.Digest) string { - return configDigest.Hex() + ".json" +func (w *Writer) configPath(configDigest digest.Digest) (string, error) { + if err := configDigest.Validate(); err != nil { // digest.Digest.Hex() panics on failure, and could possibly result in unexpected paths, so validate explicitly. + return "", err + } + return configDigest.Hex() + ".json", nil } // physicalLayerPath returns a path we choose for storing a layer with the specified digest // (the actual path, i.e. a regular file, not a symlink that may be used in the legacy format). // NOTE: This is an internal implementation detail, not a format property, and can change // any time. -func (w *Writer) physicalLayerPath(layerDigest digest.Digest) string { +func (w *Writer) physicalLayerPath(layerDigest digest.Digest) (string, error) { + if err := layerDigest.Validate(); err != nil { // digest.Digest.Hex() panics on failure, and could possibly result in unexpected paths, so validate explicitly. + return "", err + } // Note that this can't be e.g. filepath.Join(l.Digest.Hex(), legacyLayerFileName); due to the way // writeLegacyMetadata constructs layer IDs differently from inputinfo.Digest values (as described // inside it), most of the layers would end up in subdirectories alone without any metadata; (docker load) // tries to load every subdirectory as an image and fails if the config is missing. So, keep the layers // in the root of the tarball. - return layerDigest.Hex() + ".tar" + return layerDigest.Hex() + ".tar", nil } type tarFI struct { diff --git a/vendor/github.com/containers/image/v5/docker/lookaside.go b/vendor/github.com/containers/image/v5/docker/lookaside.go index 6931fd07baed..07a8f31dd9e7 100644 --- a/vendor/github.com/containers/image/v5/docker/lookaside.go +++ b/vendor/github.com/containers/image/v5/docker/lookaside.go @@ -199,11 +199,14 @@ func (ns registryNamespace) signatureTopLevel(write bool) string { // signatureStorageURL returns an URL usable for acessing signature index in base with known manifestDigest, or nil if not applicable. // Returns nil iff base == nil. // NOTE: Keep this in sync with docs/signature-protocols.md! -func signatureStorageURL(base signatureStorageBase, manifestDigest digest.Digest, index int) *url.URL { +func signatureStorageURL(base signatureStorageBase, manifestDigest digest.Digest, index int) (*url.URL, error) { + if err := manifestDigest.Validate(); err != nil { // digest.Digest.Hex() panics on failure, and could possibly result in a path with ../, so validate explicitly. + return nil, err + } if base == nil { - return nil + return nil, nil } url := *base url.Path = fmt.Sprintf("%s@%s=%s/signature-%d", url.Path, manifestDigest.Algorithm(), manifestDigest.Hex(), index+1) - return &url + return &url, nil } diff --git a/vendor/github.com/containers/image/v5/openshift/openshift.go b/vendor/github.com/containers/image/v5/openshift/openshift.go index 28bfc456d565..a3ae4467890d 100644 --- a/vendor/github.com/containers/image/v5/openshift/openshift.go +++ b/vendor/github.com/containers/image/v5/openshift/openshift.go @@ -238,6 +238,9 @@ func (s *openshiftImageSource) GetSignatures(ctx context.Context, instanceDigest } imageStreamImageName = s.imageStreamImageName } else { + if err := instanceDigest.Validate(); err != nil { // Make sure instanceDigest.String() does not contain any unexpected characters + return nil, err + } imageStreamImageName = instanceDigest.String() } image, err := s.client.getImage(ctx, imageStreamImageName) diff --git a/vendor/github.com/containers/image/v5/storage/storage_image.go b/vendor/github.com/containers/image/v5/storage/storage_image.go index df4b67c7a747..18bdd78a99bd 100644 --- a/vendor/github.com/containers/image/v5/storage/storage_image.go +++ b/vendor/github.com/containers/image/v5/storage/storage_image.go @@ -1,3 +1,4 @@ +//go:build !containers_image_storage_stub // +build !containers_image_storage_stub package storage @@ -75,14 +76,20 @@ type storageImageCloser struct { // manifestBigDataKey returns a key suitable for recording a manifest with the specified digest using storage.Store.ImageBigData and related functions. // If a specific manifest digest is explicitly requested by the user, the key returned by this function should be used preferably; // for compatibility, if a manifest is not available under this key, check also storage.ImageDigestBigDataKey -func manifestBigDataKey(digest digest.Digest) string { - return storage.ImageDigestManifestBigDataNamePrefix + "-" + digest.String() +func manifestBigDataKey(digest digest.Digest) (string, error) { + if err := digest.Validate(); err != nil { // Make sure info.Digest.String() uses the expected format and does not collide with other BigData keys. + return "", err + } + return storage.ImageDigestManifestBigDataNamePrefix + "-" + digest.String(), nil } // signatureBigDataKey returns a key suitable for recording the signatures associated with the manifest with the specified digest using storage.Store.ImageBigData and related functions. // If a specific manifest digest is explicitly requested by the user, the key returned by this function should be used preferably; -func signatureBigDataKey(digest digest.Digest) string { - return "signature-" + digest.Encoded() +func signatureBigDataKey(digest digest.Digest) (string, error) { + if err := digest.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, so validate explicitly. + return "", err + } + return "signature-" + digest.Encoded(), nil } // newImageSource sets up an image for reading. @@ -191,7 +198,10 @@ func (s *storageImageSource) getBlobAndLayerID(info types.BlobInfo) (rc io.ReadC // GetManifest() reads the image's manifest. func (s *storageImageSource) GetManifest(ctx context.Context, instanceDigest *digest.Digest) (manifestBlob []byte, MIMEType string, err error) { if instanceDigest != nil { - key := manifestBigDataKey(*instanceDigest) + key, err := manifestBigDataKey(*instanceDigest) + if err != nil { + return nil, "", err + } blob, err := s.imageRef.transport.store.ImageBigData(s.image.ID, key) if err != nil { return nil, "", errors.Wrapf(err, "error reading manifest for image instance %q", *instanceDigest) @@ -203,7 +213,10 @@ func (s *storageImageSource) GetManifest(ctx context.Context, instanceDigest *di // Prefer the manifest corresponding to the user-specified digest, if available. if s.imageRef.named != nil { if digested, ok := s.imageRef.named.(reference.Digested); ok { - key := manifestBigDataKey(digested.Digest()) + key, err := manifestBigDataKey(digested.Digest()) + if err != nil { + return nil, "", err + } blob, err := s.imageRef.transport.store.ImageBigData(s.image.ID, key) if err != nil && !os.IsNotExist(err) { // os.IsNotExist is true if the image exists but there is no data corresponding to key return nil, "", err @@ -317,7 +330,14 @@ func (s *storageImageSource) GetSignatures(ctx context.Context, instanceDigest * instance := "default instance" if instanceDigest != nil { signatureSizes = s.SignaturesSizes[*instanceDigest] - key = signatureBigDataKey(*instanceDigest) + k, err := signatureBigDataKey(*instanceDigest) + if err != nil { + return nil, err + } + key = k + if err := instanceDigest.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, so validate explicitly. + return nil, err + } instance = instanceDigest.Encoded() } if len(signatureSizes) > 0 { @@ -467,15 +487,15 @@ func (s *storageImageDestination) PutBlob(ctx context.Context, stream io.Reader, // If the transport can not reuse the requested blob, TryReusingBlob returns (false, {}, nil); it returns a non-nil error only on an unexpected failure. // May use and/or update cache. func (s *storageImageDestination) TryReusingBlob(ctx context.Context, blobinfo types.BlobInfo, cache types.BlobInfoCache, canSubstitute bool) (bool, types.BlobInfo, error) { - // lock the entire method as it executes fairly quickly - s.putBlobMutex.Lock() - defer s.putBlobMutex.Unlock() if blobinfo.Digest == "" { return false, types.BlobInfo{}, errors.Errorf(`Can not check for a blob with unknown digest`) } if err := blobinfo.Digest.Validate(); err != nil { return false, types.BlobInfo{}, errors.Wrapf(err, `Can not check for a blob with invalid digest`) } + // lock the entire method as it executes fairly quickly + s.putBlobMutex.Lock() + defer s.putBlobMutex.Unlock() // Check if we've already cached it in a file. if size, ok := s.fileSizes[blobinfo.Digest]; ok { @@ -826,7 +846,10 @@ func (s *storageImageDestination) Commit(ctx context.Context, unparsedToplevel t if err != nil { return errors.Wrapf(err, "error digesting top-level manifest") } - key := manifestBigDataKey(manifestDigest) + key, err := manifestBigDataKey(manifestDigest) + if err != nil { + return err + } if err := s.imageRef.transport.store.SetImageBigData(img.ID, key, toplevelManifest, manifest.Digest); err != nil { if _, err2 := s.imageRef.transport.store.DeleteImage(img.ID, true); err2 != nil { logrus.Debugf("error deleting incomplete image %q: %v", img.ID, err2) @@ -842,7 +865,10 @@ func (s *storageImageDestination) Commit(ctx context.Context, unparsedToplevel t if err != nil { return errors.Wrapf(err, "error computing manifest digest") } - key := manifestBigDataKey(manifestDigest) + key, err := manifestBigDataKey(manifestDigest) + if err != nil { + return err + } if err := s.imageRef.transport.store.SetImageBigData(img.ID, key, s.manifest, manifest.Digest); err != nil { if _, err2 := s.imageRef.transport.store.DeleteImage(img.ID, true); err2 != nil { logrus.Debugf("error deleting incomplete image %q: %v", img.ID, err2) @@ -869,7 +895,10 @@ func (s *storageImageDestination) Commit(ctx context.Context, unparsedToplevel t } } for instanceDigest, signatures := range s.signatureses { - key := signatureBigDataKey(instanceDigest) + key, err := signatureBigDataKey(instanceDigest) + if err != nil { + return err + } if err := s.imageRef.transport.store.SetImageBigData(img.ID, key, signatures, manifest.Digest); err != nil { if _, err2 := s.imageRef.transport.store.DeleteImage(img.ID, true); err2 != nil { logrus.Debugf("error deleting incomplete image %q: %v", img.ID, err2) diff --git a/vendor/github.com/containers/image/v5/storage/storage_reference.go b/vendor/github.com/containers/image/v5/storage/storage_reference.go index 394557f39a39..2cd4cfcde3ce 100644 --- a/vendor/github.com/containers/image/v5/storage/storage_reference.go +++ b/vendor/github.com/containers/image/v5/storage/storage_reference.go @@ -1,3 +1,4 @@ +//go:build !containers_image_storage_stub // +build !containers_image_storage_stub package storage @@ -74,7 +75,10 @@ func imageMatchesRepo(image *storage.Image, ref reference.Named) bool { func imageMatchesSystemContext(store storage.Store, img *storage.Image, manifestDigest digest.Digest, sys *types.SystemContext) bool { // First, check if the image record has a manifest that matches the // specified digest. - key := manifestBigDataKey(manifestDigest) + key, err := manifestBigDataKey(manifestDigest) + if err != nil { + return false // This should never happen, manifestDigest comes from a reference.Digested, and that validates the format. + } manifestBytes, err := store.ImageBigData(img.ID, key) if err != nil { return false @@ -92,7 +96,10 @@ func imageMatchesSystemContext(store storage.Store, img *storage.Image, manifest if err != nil { return false } - key = manifestBigDataKey(manifestDigest) + key, err := manifestBigDataKey(manifestDigest) + if err != nil { + return false // This should never happen, manifestDigest comes from a reference.Digested, and that validates the format. + } manifestBytes, err = store.ImageBigData(img.ID, key) if err != nil { return false @@ -130,7 +137,10 @@ func imageMatchesSystemContext(store storage.Store, img *storage.Image, manifest } // Double-check that we can read the runnable image's manifest from the // image record. - key = manifestBigDataKey(instanceDigest) + key, err := manifestBigDataKey(instanceDigest) + if err != nil { + return false // This should never happen, manifestDigest comes from a reference.Digested, and that validates the format. + } _, err = store.ImageBigData(img.ID, key) return err == nil } -- 2.45.2 From ae5b155320dbef382d699d3d43b1eb962239a228 Mon Sep 17 00:00:00 2001 From: Danish Prakash <contact@danishpraka.sh> Date: Tue, 2 Jul 2024 13:26:51 +0530 Subject: [PATCH 3/4] Call .Validate() before digest.Hex() / digest.Encoded() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit to prevent panics if the value does not contain a :, or other unexpected values (e.g. a path traversal). Don't bother on paths where we computed the digest ourselves, or it is already trusted for other reasons. Signed-off-by: Miloslav Trmač <mitr@redhat.com> Signed-off-by: Danish Prakash <contact@danishpraka.sh> --- .../containers/image/v5/copy/copy.go | 23 +++++++++++++---- .../image/v5/directory/directory_dest.go | 22 +++++++++++++--- .../image/v5/directory/directory_src.go | 18 ++++++++++--- .../image/v5/directory/directory_transport.go | 25 +++++++++++++------ .../containers/image/v5/ostree/ostree_dest.go | 10 ++++++++ .../containers/image/v5/ostree/ostree_src.go | 5 +++- 6 files changed, 82 insertions(+), 21 deletions(-) diff --git a/vendor/github.com/containers/image/v5/copy/copy.go b/vendor/github.com/containers/image/v5/copy/copy.go index 873bdc67f4ba..5a5b98c52f09 100644 --- a/vendor/github.com/containers/image/v5/copy/copy.go +++ b/vendor/github.com/containers/image/v5/copy/copy.go @@ -963,10 +963,14 @@ func (c *copier) newProgressPool(ctx context.Context) (*mpb.Progress, func()) { // createProgressBar creates a mpb.Bar in pool. Note that if the copier's reportWriter // is ioutil.Discard, the progress bar's output will be discarded -func (c *copier) createProgressBar(pool *mpb.Progress, info types.BlobInfo, kind string, onComplete string) *mpb.Bar { +func (c *copier) createProgressBar(pool *mpb.Progress, info types.BlobInfo, kind string, onComplete string) (*mpb.Bar, error) { // shortDigestLen is the length of the digest used for blobs. const shortDigestLen = 12 + if err := info.Digest.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, so validate explicitly. + return nil, err + } + prefix := fmt.Sprintf("Copying %s %s", kind, info.Digest.Encoded()) // Truncate the prefix (chopping of some part of the digest) to make all progress bars aligned in a column. maxPrefixLen := len("Copying blob ") + shortDigestLen @@ -1003,7 +1007,7 @@ func (c *copier) createProgressBar(pool *mpb.Progress, info types.BlobInfo, kind if c.progressOutput == ioutil.Discard { c.Printf("Copying %s %s\n", kind, info.Digest) } - return bar + return bar, nil } // copyConfig copies config.json, if any, from src to dest. @@ -1018,7 +1022,10 @@ func (c *copier) copyConfig(ctx context.Context, src types.Image) error { destInfo, err := func() (types.BlobInfo, error) { // A scope for defer progressPool, progressCleanup := c.newProgressPool(ctx) defer progressCleanup() - bar := c.createProgressBar(progressPool, srcInfo, "config", "done") + bar, err := c.createProgressBar(progressPool, srcInfo, "config", "done") + if err != nil { + return types.BlobInfo{}, err + } destInfo, err := c.copyBlobFromStream(ctx, bytes.NewReader(configBlob), srcInfo, nil, false, true, false, bar) if err != nil { return types.BlobInfo{}, err @@ -1058,7 +1065,10 @@ func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, to } if reused { logrus.Debugf("Skipping blob %s (already present):", srcInfo.Digest) - bar := ic.c.createProgressBar(pool, srcInfo, "blob", "skipped: already exists") + bar, err := ic.c.createProgressBar(pool, srcInfo, "blob", "skipped: already exists") + if err != nil { + return types.BlobInfo{}, "", err + } bar.SetTotal(0, true) // Throw an event that the layer has been skipped @@ -1079,7 +1089,10 @@ func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, to } defer srcStream.Close() - bar := ic.c.createProgressBar(pool, srcInfo, "blob", "done") + bar, err := ic.c.createProgressBar(pool, srcInfo, "blob", "done") + if err != nil { + return types.BlobInfo{}, "", err + } blobInfo, diffIDChan, err := ic.copyLayerFromStream(ctx, srcStream, types.BlobInfo{Digest: srcInfo.Digest, Size: srcBlobSize, MediaType: srcInfo.MediaType, Annotations: srcInfo.Annotations}, diffIDIsNeeded, toEncrypt, bar) if err != nil { diff --git a/vendor/github.com/containers/image/v5/directory/directory_dest.go b/vendor/github.com/containers/image/v5/directory/directory_dest.go index d70b6c07fbca..46ca7b12528b 100644 --- a/vendor/github.com/containers/image/v5/directory/directory_dest.go +++ b/vendor/github.com/containers/image/v5/directory/directory_dest.go @@ -179,7 +179,10 @@ func (d *dirImageDestination) PutBlob(ctx context.Context, stream io.Reader, inp } } - blobPath := d.ref.layerPath(computedDigest) + blobPath, err := d.ref.layerPath(computedDigest) + if err != nil { + return types.BlobInfo{}, err + } // need to explicitly close the file, since a rename won't otherwise not work on Windows blobFile.Close() explicitClosed = true @@ -201,7 +204,10 @@ func (d *dirImageDestination) TryReusingBlob(ctx context.Context, info types.Blo if info.Digest == "" { return false, types.BlobInfo{}, errors.Errorf(`"Can not check for a blob with unknown digest`) } - blobPath := d.ref.layerPath(info.Digest) + blobPath, err := d.ref.layerPath(info.Digest) + if err != nil { + return false, types.BlobInfo{}, err + } finfo, err := os.Stat(blobPath) if err != nil && os.IsNotExist(err) { return false, types.BlobInfo{}, nil @@ -222,7 +228,11 @@ func (d *dirImageDestination) TryReusingBlob(ctx context.Context, info types.Blo // If the destination is in principle available, refuses this manifest type (e.g. it does not recognize the schema), // but may accept a different manifest type, the returned error must be an ManifestTypeRejectedError. func (d *dirImageDestination) PutManifest(ctx context.Context, manifest []byte, instanceDigest *digest.Digest) error { - return ioutil.WriteFile(d.ref.manifestPath(instanceDigest), manifest, 0644) + path, err := d.ref.manifestPath(instanceDigest) + if err != nil { + return err + } + return ioutil.WriteFile(path, manifest, 0644) } // PutSignatures writes a set of signatures to the destination. @@ -230,7 +240,11 @@ func (d *dirImageDestination) PutManifest(ctx context.Context, manifest []byte, // (when the primary manifest is a manifest list); this should always be nil if the primary manifest is not a manifest list. func (d *dirImageDestination) PutSignatures(ctx context.Context, signatures [][]byte, instanceDigest *digest.Digest) error { for i, sig := range signatures { - if err := ioutil.WriteFile(d.ref.signaturePath(i, instanceDigest), sig, 0644); err != nil { + path, err := d.ref.signaturePath(i, instanceDigest) + if err != nil { + return err + } + if err := ioutil.WriteFile(path, sig, 0644); err != nil { return err } } diff --git a/vendor/github.com/containers/image/v5/directory/directory_src.go b/vendor/github.com/containers/image/v5/directory/directory_src.go index ad9129d4012b..420cee2fda54 100644 --- a/vendor/github.com/containers/image/v5/directory/directory_src.go +++ b/vendor/github.com/containers/image/v5/directory/directory_src.go @@ -37,7 +37,11 @@ func (s *dirImageSource) Close() error { // If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve (when the primary manifest is a manifest list); // this never happens if the primary manifest is not a manifest list (e.g. if the source never returns manifest lists). func (s *dirImageSource) GetManifest(ctx context.Context, instanceDigest *digest.Digest) ([]byte, string, error) { - m, err := ioutil.ReadFile(s.ref.manifestPath(instanceDigest)) + path, err := s.ref.manifestPath(instanceDigest) + if err != nil { + return nil, "", err + } + m, err := ioutil.ReadFile(path) if err != nil { return nil, "", err } @@ -53,7 +57,11 @@ func (s *dirImageSource) HasThreadSafeGetBlob() bool { // The Digest field in BlobInfo is guaranteed to be provided, Size may be -1 and MediaType may be optionally provided. // May update BlobInfoCache, preferably after it knows for certain that a blob truly exists at a specific location. func (s *dirImageSource) GetBlob(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache) (io.ReadCloser, int64, error) { - r, err := os.Open(s.ref.layerPath(info.Digest)) + path, err := s.ref.layerPath(info.Digest) + if err != nil { + return nil, -1, err + } + r, err := os.Open(path) if err != nil { return nil, -1, err } @@ -71,7 +79,11 @@ func (s *dirImageSource) GetBlob(ctx context.Context, info types.BlobInfo, cache func (s *dirImageSource) GetSignatures(ctx context.Context, instanceDigest *digest.Digest) ([][]byte, error) { signatures := [][]byte{} for i := 0; ; i++ { - signature, err := ioutil.ReadFile(s.ref.signaturePath(i, instanceDigest)) + path, err := s.ref.signaturePath(i, instanceDigest) + if err != nil { + return nil, err + } + signature, err := ioutil.ReadFile(path) if err != nil { if os.IsNotExist(err) { break diff --git a/vendor/github.com/containers/image/v5/directory/directory_transport.go b/vendor/github.com/containers/image/v5/directory/directory_transport.go index adfec6ef3737..d1300b867c09 100644 --- a/vendor/github.com/containers/image/v5/directory/directory_transport.go +++ b/vendor/github.com/containers/image/v5/directory/directory_transport.go @@ -166,25 +166,34 @@ func (ref dirReference) DeleteImage(ctx context.Context, sys *types.SystemContex } // manifestPath returns a path for the manifest within a directory using our conventions. -func (ref dirReference) manifestPath(instanceDigest *digest.Digest) string { +func (ref dirReference) manifestPath(instanceDigest *digest.Digest) (string, error) { if instanceDigest != nil { - return filepath.Join(ref.path, instanceDigest.Encoded()+".manifest.json") + if err := instanceDigest.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, and could possibly result in a path with ../, so validate explicitly. + return "", err + } + return filepath.Join(ref.path, instanceDigest.Encoded()+".manifest.json"), nil } - return filepath.Join(ref.path, "manifest.json") + return filepath.Join(ref.path, "manifest.json"), nil } // layerPath returns a path for a layer tarball within a directory using our conventions. -func (ref dirReference) layerPath(digest digest.Digest) string { +func (ref dirReference) layerPath(digest digest.Digest) (string, error) { + if err := digest.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, and could possibly result in a path with ../, so validate explicitly. + return "", err + } // FIXME: Should we keep the digest identification? - return filepath.Join(ref.path, digest.Encoded()) + return filepath.Join(ref.path, digest.Encoded()), nil } // signaturePath returns a path for a signature within a directory using our conventions. -func (ref dirReference) signaturePath(index int, instanceDigest *digest.Digest) string { +func (ref dirReference) signaturePath(index int, instanceDigest *digest.Digest) (string, error) { if instanceDigest != nil { - return filepath.Join(ref.path, fmt.Sprintf(instanceDigest.Encoded()+".signature-%d", index+1)) + if err := instanceDigest.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, and could possibly result in a path with ../, so validate explicitly. + return "", err + } + return filepath.Join(ref.path, fmt.Sprintf(instanceDigest.Encoded()+".signature-%d", index+1)), nil } - return filepath.Join(ref.path, fmt.Sprintf("signature-%d", index+1)) + return filepath.Join(ref.path, fmt.Sprintf("signature-%d", index+1)), nil } // versionPath returns a path for the version file within a directory using our conventions. diff --git a/vendor/github.com/containers/image/v5/ostree/ostree_dest.go b/vendor/github.com/containers/image/v5/ostree/ostree_dest.go index 11509705592a..aa708c581278 100644 --- a/vendor/github.com/containers/image/v5/ostree/ostree_dest.go +++ b/vendor/github.com/containers/image/v5/ostree/ostree_dest.go @@ -1,3 +1,4 @@ +//go:build containers_image_ostree // +build containers_image_ostree package ostree @@ -350,6 +351,9 @@ func (d *ostreeImageDestination) TryReusingBlob(ctx context.Context, info types. } d.repo = repo } + if err := info.Digest.Validate(); err != nil { // digest.Digest.Hex() panics on failure, so validate explicitly. + return false, types.BlobInfo{}, err + } branch := fmt.Sprintf("ociimage/%s", info.Digest.Hex()) found, data, err := readMetadata(d.repo, branch, "docker.uncompressed_digest") @@ -470,12 +474,18 @@ func (d *ostreeImageDestination) Commit(context.Context, types.UnparsedImage) er return nil } for _, layer := range d.schema.LayersDescriptors { + if err := layer.Digest.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, so validate explicitly. + return err + } hash := layer.Digest.Hex() if err = checkLayer(hash); err != nil { return err } } for _, layer := range d.schema.FSLayers { + if err := layer.BlobSum.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, so validate explicitly. + return err + } hash := layer.BlobSum.Hex() if err = checkLayer(hash); err != nil { return err diff --git a/vendor/github.com/containers/image/v5/ostree/ostree_src.go b/vendor/github.com/containers/image/v5/ostree/ostree_src.go index 4948ec664161..f60cbcf7c149 100644 --- a/vendor/github.com/containers/image/v5/ostree/ostree_src.go +++ b/vendor/github.com/containers/image/v5/ostree/ostree_src.go @@ -1,3 +1,4 @@ +//go:build containers_image_ostree // +build containers_image_ostree package ostree @@ -272,7 +273,9 @@ func (s *ostreeImageSource) HasThreadSafeGetBlob() bool { // The Digest field in BlobInfo is guaranteed to be provided, Size may be -1 and MediaType may be optionally provided. // May update BlobInfoCache, preferably after it knows for certain that a blob truly exists at a specific location. func (s *ostreeImageSource) GetBlob(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache) (io.ReadCloser, int64, error) { - + if err := info.Digest.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, so validate explicitly. + return nil, -1, err + } blob := info.Digest.Hex() // Ensure s.compressed is initialized. It is build by LayerInfosForCopy. -- 2.45.2 From d916bec0f8f7882e3148922b3bed2d7281c1ee66 Mon Sep 17 00:00:00 2001 From: Danish Prakash <contact@danishpraka.sh> Date: Tue, 2 Jul 2024 13:25:47 +0530 Subject: [PATCH 4/4] rename makeFilename to blobPath MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Miloslav Trmač <mitr@redhat.com> Signed-off-by: Danish Prakash <contact@danishpraka.sh> --- .../buildah/pkg/blobcache/blobcache.go | 46 +++++++++++++++++-- .../image/v5/docker/docker_image_src.go | 2 +- .../image/v5/storage/storage_reference.go | 2 +- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/vendor/github.com/containers/buildah/pkg/blobcache/blobcache.go b/vendor/github.com/containers/buildah/pkg/blobcache/blobcache.go index 3f0177226322..e91e3c73b02e 100644 --- a/vendor/github.com/containers/buildah/pkg/blobcache/blobcache.go +++ b/vendor/github.com/containers/buildah/pkg/blobcache/blobcache.go @@ -81,6 +81,18 @@ func makeFilename(blobSum digest.Digest, isConfig bool) string { return blobSum.String() } +// blobPath returns the path appropriate for storing a blob with digest. +func (b *blobCacheReference) blobPath(digest digest.Digest, isConfig bool) (string, error) { + if err := digest.Validate(); err != nil { // Make sure digest.String() does not contain any unexpected characters + return "", err + } + baseName := digest.String() + if isConfig { + baseName += ".config" + } + return filepath.Join(b.directory, baseName), nil +} + // NewBlobCache creates a new blob cache that wraps an image reference. Any blobs which are // written to the destination image created from the resulting reference will also be stored // as-is to the specified directory or a temporary directory. The cache directory's contents @@ -134,7 +146,10 @@ func (r *blobCacheReference) HasBlob(blobinfo types.BlobInfo) (bool, int64, erro } for _, isConfig := range []bool{false, true} { - filename := filepath.Join(r.directory, makeFilename(blobinfo.Digest, isConfig)) + filename, err := r.blobPath(blobinfo.Digest, isConfig) + if err != nil { + return false, -1, err + } fileInfo, err := os.Stat(filename) if err == nil && (blobinfo.Size == -1 || blobinfo.Size == fileInfo.Size()) { return true, fileInfo.Size(), nil @@ -207,7 +222,10 @@ func (s *blobCacheSource) Close() error { func (s *blobCacheSource) GetManifest(ctx context.Context, instanceDigest *digest.Digest) ([]byte, string, error) { if instanceDigest != nil { - filename := filepath.Join(s.reference.directory, makeFilename(*instanceDigest, false)) + filename, err := s.reference.blobPath(*instanceDigest, false) + if err != nil { + return nil, "", err + } manifestBytes, err := ioutil.ReadFile(filename) if err == nil { s.cacheHits++ @@ -287,7 +305,10 @@ func (s *blobCacheSource) LayerInfosForCopy(ctx context.Context, instanceDigest for _, info := range infos { var replaceDigest []byte var err error - blobFile := filepath.Join(s.reference.directory, makeFilename(info.Digest, false)) + blobFile, err2 := s.reference.blobPath(info.Digest, false) + if err != nil { + return nil, err2 + } alternate := "" switch s.reference.compress { case types.Compress: @@ -298,7 +319,10 @@ func (s *blobCacheSource) LayerInfosForCopy(ctx context.Context, instanceDigest replaceDigest, err = ioutil.ReadFile(alternate) } if err == nil && digest.Digest(replaceDigest).Validate() == nil { - alternate = filepath.Join(filepath.Dir(alternate), makeFilename(digest.Digest(replaceDigest), false)) + alternate, err := s.reference.blobPath(digest.Digest(replaceDigest), false) + if err != nil { + return nil, err + } fileInfo, err := os.Stat(alternate) if err == nil { logrus.Debugf("suggesting cached blob with digest %q and compression %v in place of blob with digest %q", string(replaceDigest), s.reference.compress, info.Digest.String()) @@ -371,6 +395,7 @@ func saveStream(wg *sync.WaitGroup, decompressReader io.ReadCloser, tempFile *os defer wg.Done() // Decompress from and digest the reading end of that pipe. decompressed, err3 := archive.DecompressStream(decompressReader) + digester := digest.Canonical.Digester() if err3 == nil { // Read the decompressed data through the filter over the pipe, blocking until the @@ -382,9 +407,13 @@ func saveStream(wg *sync.WaitGroup, decompressReader io.ReadCloser, tempFile *os logrus.Debugf("error draining the pipe: %v", err) } } + decompressReader.Close() decompressed.Close() tempFile.Close() + if err := digester.Digest().Validate(); err != nil { + return + } // Determine the name that we should give to the uncompressed copy of the blob. decompressedFilename := filepath.Join(filepath.Dir(tempFile.Name()), makeFilename(digester.Digest(), isConfig)) if err3 == nil { @@ -427,6 +456,10 @@ func (d *blobCacheDestination) PutBlob(ctx context.Context, stream io.Reader, in compression := archive.Uncompressed if inputInfo.Digest != "" { filename := filepath.Join(d.reference.directory, makeFilename(inputInfo.Digest, isConfig)) + filename, err2 := d.reference.blobPath(inputInfo.Digest, isConfig) + if err2 != nil { + return types.BlobInfo{}, err2 + } tempfile, err = ioutil.TempFile(d.reference.directory, makeFilename(inputInfo.Digest, isConfig)) if err == nil { stream = io.TeeReader(stream, tempfile) @@ -519,7 +552,10 @@ func (d *blobCacheDestination) PutManifest(ctx context.Context, manifestBytes [] if err != nil { logrus.Warnf("error digesting manifest %q: %v", string(manifestBytes), err) } else { - filename := filepath.Join(d.reference.directory, makeFilename(manifestDigest, false)) + filename, err := d.reference.blobPath(manifestDigest, false) + if err != nil { + return err + } if err = ioutils.AtomicWriteFile(filename, manifestBytes, 0600); err != nil { logrus.Warnf("error saving manifest as %q: %v", filename, err) } diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_src.go b/vendor/github.com/containers/image/v5/docker/docker_image_src.go index 47e52aa10df1..234949673ba0 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image_src.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image_src.go @@ -343,7 +343,7 @@ func (s *dockerImageSource) getSignaturesFromLookaside(ctx context.Context, inst for i := 0; ; i++ { url, err := signatureStorageURL(s.c.signatureBase, manifestDigest, i) if err != nil { - return err + return nil, err } if url == nil { return nil, errors.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") diff --git a/vendor/github.com/containers/image/v5/storage/storage_reference.go b/vendor/github.com/containers/image/v5/storage/storage_reference.go index 2cd4cfcde3ce..c3f89962fd3f 100644 --- a/vendor/github.com/containers/image/v5/storage/storage_reference.go +++ b/vendor/github.com/containers/image/v5/storage/storage_reference.go @@ -137,7 +137,7 @@ func imageMatchesSystemContext(store storage.Store, img *storage.Image, manifest } // Double-check that we can read the runnable image's manifest from the // image record. - key, err := manifestBigDataKey(instanceDigest) + key, err = manifestBigDataKey(instanceDigest) if err != nil { return false // This should never happen, manifestDigest comes from a reference.Digested, and that validates the format. } -- 2.45.2
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor