Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions cache/util/fsutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ func ReadFile(ctx context.Context, root string, req ReadRequest) ([]byte, error)
}
defer f.Close()

info, err := f.Stat()
if err != nil {
return nil, errors.WithStack(err)
}
if !info.Mode().IsRegular() {
return nil, errors.Errorf("%s is not a regular file", req.Filename)
}

var rdr io.Reader = f
if req.Range != nil {
rdr = io.NewSectionReader(f, int64(req.Range.Offset), int64(req.Range.Length))
Expand Down
47 changes: 34 additions & 13 deletions executor/proxyca_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"io"
"os"
"path/filepath"
"syscall"
Expand All @@ -29,6 +30,8 @@ var (
proxyCAEnd = []byte("# buildkit proxy CA end\n")
)

const maxCertBundleBytes = 10 << 20

// InjectProxyCA appends caPEM to the rootfs trust bundle used by common Linux
// TLS stacks and returns a cleanup that removes only the injected CA.
func InjectProxyCA(rootfsPath string, caPEM []byte) (func() error, error) {
Expand All @@ -47,7 +50,7 @@ func InjectProxyCA(rootfsPath string, caPEM []byte) (func() error, error) {
if err != nil {
return nil, errors.Wrapf(err, "failed to resolve certificate bundle %s", name)
}
if st, err := os.Stat(p); err == nil && !st.IsDir() {
if st, err := os.Stat(p); err == nil && st.Mode().IsRegular() {
bundle = p
break
}
Expand All @@ -56,17 +59,13 @@ func InjectProxyCA(rootfsPath string, caPEM []byte) (func() error, error) {
return func() error { return nil }, nil
}

original, err := os.ReadFile(bundle)
original, st, err := readCertBundle(bundle)
if err != nil {
return nil, errors.WithStack(err)
return nil, err
}
if containsCertificate(original, certSum) {
return func() error { return nil }, nil
}
st, err := os.Stat(bundle)
if err != nil {
return nil, errors.WithStack(err)
}
next := append([]byte{}, original...)
if len(next) > 0 && next[len(next)-1] != '\n' {
next = append(next, '\n')
Expand All @@ -82,25 +81,47 @@ func InjectProxyCA(rootfsPath string, caPEM []byte) (func() error, error) {
}

return func() error {
current, err := os.ReadFile(bundle)
current, st, err := readCertBundle(bundle)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil
}
return errors.WithStack(err)
return err
}
cleaned := removeInjectedCA(current, certSum)
if bytes.Equal(current, cleaned) {
return nil
}
st, err := os.Stat(bundle)
if err != nil {
return errors.WithStack(err)
}
return writeCertBundle(bundle, cleaned, st)
}, nil
}

func readCertBundle(path string) ([]byte, os.FileInfo, error) {
f, err := os.Open(path)
if err != nil {
return nil, nil, errors.WithStack(err)
}
defer f.Close()

st, err := f.Stat()
if err != nil {
return nil, nil, errors.WithStack(err)
}
if !st.Mode().IsRegular() {
return nil, nil, errors.Errorf("%s is not a regular file", path)
}

limited := &io.LimitedReader{R: f, N: maxCertBundleBytes + 1}
dt, err := io.ReadAll(limited)
if err != nil {
return nil, nil, errors.WithStack(err)
}
if limited.N == 0 {
return nil, nil, errors.Errorf("%s exceeds %d bytes", path, maxCertBundleBytes)
}
return dt, st, nil
}

func firstCertificate(dt []byte) (*x509.Certificate, error) {
for {
block, rest := pem.Decode(dt)
Expand Down
50 changes: 49 additions & 1 deletion exporter/attestation/make.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package attestation
import (
"context"
"encoding/json"
"io"
"os"

"github.com/containerd/continuity/fs"
Expand All @@ -16,6 +17,8 @@ import (
"golang.org/x/sync/errgroup"
)

const maxAttestationBytes int64 = 40 << 20

// ReadAll reads the content of an attestation.
func ReadAll(ctx context.Context, s session.Group, att exporter.Attestation) ([]byte, error) {
var content []byte
Expand All @@ -41,7 +44,7 @@ func ReadAll(ctx context.Context, s session.Group, att exporter.Attestation) ([]
if err != nil {
return nil, err
}
content, err = os.ReadFile(p)
content, err = readRegularFile(p)
if err != nil {
return nil, errors.Wrap(err, "cannot read in-toto attestation")
}
Expand All @@ -54,6 +57,51 @@ func ReadAll(ctx context.Context, s session.Group, att exporter.Attestation) ([]
return content, nil
}

func readRegularFile(p string) ([]byte, error) {
f, err := openRegularFile(p)
if err != nil {
return nil, err
}
defer f.Close()

dt, err := readAllLimited(f, p, maxAttestationBytes)
if err != nil {
return nil, err
}
return dt, nil
}

func readAllLimited(r io.Reader, name string, limit int64) ([]byte, error) {
limited := &io.LimitedReader{R: r, N: limit + 1}
dt, err := io.ReadAll(limited)
if err != nil {
return nil, errors.WithStack(err)
}
if limited.N == 0 {
return nil, errors.Errorf("%s exceeds %d bytes", name, limit)
}
return dt, nil
}

func openRegularFile(p string) (*os.File, error) {
f, err := os.Open(p)
if err != nil {
return nil, errors.WithStack(err)
}

st, err := f.Stat()
if err != nil {
f.Close()
return nil, errors.WithStack(err)
}
if !st.Mode().IsRegular() {
f.Close()
return nil, errors.Errorf("%s is not a regular file", p)
}

return f, nil
}

// MakeInTotoStatements iterates over all provided result attestations and
// generates intoto attestation statements.
func MakeInTotoStatements(ctx context.Context, s session.Group, attestations []exporter.Attestation, defaultSubjects []intoto.Subject) ([]intoto.Statement, error) {
Expand Down
35 changes: 28 additions & 7 deletions exporter/attestation/unbundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,17 +132,15 @@ func unbundle(root string, bundle exporter.Attestation) ([]exporter.Attestation,
if err != nil {
return nil, err
}
f, err := os.Open(p)
f, err := openRegularFile(p)
if err != nil {
return nil, err
}
dec := json.NewDecoder(f)
var stmt intoto.Statement
if err := dec.Decode(&stmt); err != nil {
return nil, errors.Wrap(err, "cannot decode in-toto statement")
}
if _, err := dec.Token(); !errors.Is(err, io.EOF) {
return nil, errors.New("in-toto statement is not a single JSON object")
stmt, err = decodeStatement(f, p)
f.Close()
if err != nil {
return nil, err
}
if bundle.InToto.PredicateType != "" && stmt.PredicateType != bundle.InToto.PredicateType {
return nil, errors.Errorf("bundle entry %s does not match required predicate type %s", stmt.PredicateType, bundle.InToto.PredicateType)
Expand Down Expand Up @@ -175,6 +173,29 @@ func unbundle(root string, bundle exporter.Attestation) ([]exporter.Attestation,
return unbundled, nil
}

func decodeStatement(r io.Reader, name string) (intoto.Statement, error) {
limited := &io.LimitedReader{R: r, N: maxAttestationBytes + 1}
dec := json.NewDecoder(limited)

var stmt intoto.Statement
if err := dec.Decode(&stmt); err != nil {
if limited.N == 0 {
return stmt, errors.Errorf("%s exceeds %d bytes", name, maxAttestationBytes)
}
return stmt, errors.Wrap(err, "cannot decode in-toto statement")
}
if _, err := dec.Token(); !errors.Is(err, io.EOF) {
if limited.N == 0 {
return stmt, errors.Errorf("%s exceeds %d bytes", name, maxAttestationBytes)
}
return stmt, errors.New("in-toto statement is not a single JSON object")
}
if limited.N == 0 {
return stmt, errors.Errorf("%s exceeds %d bytes", name, maxAttestationBytes)
}
return stmt, nil
}

func Validate(atts []exporter.Attestation) error {
for _, att := range atts {
if err := validate(att); err != nil {
Expand Down
9 changes: 9 additions & 0 deletions solver/llbsolver/ops/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@ func (b *BuildOp) Exec(ctx context.Context, job solver.JobContext, inputs []solv
if err != nil {
return nil, errors.Wrapf(err, "failed to open %s", newfn)
}
st, err := f.Stat()
if err != nil {
f.Close()
return nil, errors.WithStack(err)
}
if !st.Mode().IsRegular() {
f.Close()
return nil, errors.Errorf("%s is not a regular file", newfn)
}

def, err := llb.ReadFrom(f)
if err != nil {
Expand Down
Loading