File: policy_reference_match.go

package info (click to toggle)
golang-github-containers-image 5.28.0-4
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 5,104 kB
  • sloc: sh: 194; makefile: 73
file content (154 lines) | stat: -rw-r--r-- 6,230 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// 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 don’t 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 "%s" to "%s": %v`, 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)
}