image/signature/policy_reference_match.go

155 lines
6.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// PolicyReferenceMatch implementations.
package signature
import (
"fmt"
"strings"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/internal/private"
"github.com/containers/image/v5/transports"
)
// parseImageAndDockerReference converts an image and a reference string into two parsed entities, failing on any error and handling unidentified images.
func parseImageAndDockerReference(image private.UnparsedImage, s2 string) (reference.Named, reference.Named, error) {
r1 := image.Reference().DockerReference()
if r1 == nil {
return nil, nil, PolicyRequirementError(fmt.Sprintf("Docker reference match attempted on image %s with no known Docker reference identity",
transports.ImageName(image.Reference())))
}
r2, err := reference.ParseNormalizedNamed(s2)
if err != nil {
return nil, nil, err
}
return r1, r2, nil
}
func (prm *prmMatchExact) matchesDockerReference(image private.UnparsedImage, signatureDockerReference string) bool {
intended, signature, err := parseImageAndDockerReference(image, signatureDockerReference)
if err != nil {
return false
}
// Do not add default tags: image.Reference().DockerReference() should contain it already, and signatureDockerReference should be exact; so, verify that now.
if reference.IsNameOnly(intended) || reference.IsNameOnly(signature) {
return false
}
return signature.String() == intended.String()
}
// matchRepoDigestOrExactReferenceValues implements prmMatchRepoDigestOrExact.matchesDockerReference
// using reference.Named values.
func matchRepoDigestOrExactReferenceValues(intended, signature reference.Named) bool {
// Do not add default tags: image.Reference().DockerReference() should contain it already, and signatureDockerReference should be exact; so, verify that now.
if reference.IsNameOnly(signature) {
return false
}
switch intended.(type) {
case reference.NamedTagged: // Includes the case when intended has both a tag and a digest.
return signature.String() == intended.String()
case reference.Canonical:
// We dont actually compare the manifest digest against the signature here; that happens prSignedBy.in UnparsedImage.Manifest.
// Because UnparsedImage.Manifest verifies the intended.Digest() against the manifest, and prSignedBy verifies the signature digest against the manifest,
// we know that signature digest matches intended.Digest() (but intended.Digest() and signature digest may use different algorithms)
return signature.Name() == intended.Name()
default: // !reference.IsNameOnly(intended)
return false
}
}
func (prm *prmMatchRepoDigestOrExact) matchesDockerReference(image private.UnparsedImage, signatureDockerReference string) bool {
intended, signature, err := parseImageAndDockerReference(image, signatureDockerReference)
if err != nil {
return false
}
return matchRepoDigestOrExactReferenceValues(intended, signature)
}
func (prm *prmMatchRepository) matchesDockerReference(image private.UnparsedImage, signatureDockerReference string) bool {
intended, signature, err := parseImageAndDockerReference(image, signatureDockerReference)
if err != nil {
return false
}
return signature.Name() == intended.Name()
}
// parseDockerReferences converts two reference strings into parsed entities, failing on any error
func parseDockerReferences(s1, s2 string) (reference.Named, reference.Named, error) {
r1, err := reference.ParseNormalizedNamed(s1)
if err != nil {
return nil, nil, err
}
r2, err := reference.ParseNormalizedNamed(s2)
if err != nil {
return nil, nil, err
}
return r1, r2, nil
}
func (prm *prmExactReference) matchesDockerReference(image private.UnparsedImage, signatureDockerReference string) bool {
intended, signature, err := parseDockerReferences(prm.DockerReference, signatureDockerReference)
if err != nil {
return false
}
// prm.DockerReference and signatureDockerReference should be exact; so, verify that now.
if reference.IsNameOnly(intended) || reference.IsNameOnly(signature) {
return false
}
return signature.String() == intended.String()
}
func (prm *prmExactRepository) matchesDockerReference(image private.UnparsedImage, signatureDockerReference string) bool {
intended, signature, err := parseDockerReferences(prm.DockerRepository, signatureDockerReference)
if err != nil {
return false
}
return signature.Name() == intended.Name()
}
// refMatchesPrefix returns true if ref matches prm.Prefix.
func (prm *prmRemapIdentity) refMatchesPrefix(ref reference.Named) bool {
name := ref.Name()
switch {
case len(name) < len(prm.Prefix):
return false
case len(name) == len(prm.Prefix):
return name == prm.Prefix
case len(name) > len(prm.Prefix):
// We are matching only ref.Name(), not ref.String(), so the only separator we are
// expecting is '/':
// - '@' is only valid to separate a digest, i.e. not a part of ref.Name()
// - similarly ':' to mark a tag would not be a part of ref.Name(); it can be a part of a
// host:port domain syntax, but we don't treat that specially and require an exact match
// of the domain.
return strings.HasPrefix(name, prm.Prefix) && name[len(prm.Prefix)] == '/'
default:
panic("Internal error: impossible comparison outcome")
}
}
// remapReferencePrefix returns the result of remapping ref, if it matches prm.Prefix
// or the original ref if it does not.
func (prm *prmRemapIdentity) remapReferencePrefix(ref reference.Named) (reference.Named, error) {
if !prm.refMatchesPrefix(ref) {
return ref, nil
}
refString := ref.String()
newNamedRef := strings.Replace(refString, prm.Prefix, prm.SignedPrefix, 1)
newParsedRef, err := reference.ParseNamed(newNamedRef)
if err != nil {
return nil, fmt.Errorf(`error rewriting reference from %q to %q: %w`, refString, newNamedRef, err)
}
return newParsedRef, nil
}
func (prm *prmRemapIdentity) matchesDockerReference(image private.UnparsedImage, signatureDockerReference string) bool {
intended, signature, err := parseImageAndDockerReference(image, signatureDockerReference)
if err != nil {
return false
}
intended, err = prm.remapReferencePrefix(intended)
if err != nil {
return false
}
return matchRepoDigestOrExactReferenceValues(intended, signature)
}