terraform/internal/getproviders/package_authentication.go

94 lines
3.1 KiB
Go
Raw Normal View History

package getproviders
import (
"bytes"
"crypto/sha256"
"fmt"
"io"
"os"
)
// PackageAuthentication is an interface implemented by the optional package
// authentication implementations a source may include on its PackageMeta
// objects.
//
// A PackageAuthentication implementation is responsible for authenticating
// that a package is what its distributor intended to distribute and that it
// has not been tampered with.
type PackageAuthentication interface {
// AuthenticatePackage takes the metadata about the package as returned
// by its original source, and also the "localLocation" where it has
// been staged for local inspection (which may or may not be the same
// as the original source location) and returns an error if the
// authentication checks fail.
//
// The localLocation is guaranteed not to be a PackageHTTPURL: a
// remote package will always be staged locally for inspection first.
AuthenticatePackage(meta PackageMeta, localLocation PackageLocation) error
}
type packageAuthenticationAll []PackageAuthentication
// PackageAuthenticationAll combines several authentications together into a
// single check value, which passes only if all of the given ones pass.
//
// The checks are processed in the order given, so a failure of an earlier
// check will prevent execution of a later one.
func PackageAuthenticationAll(checks ...PackageAuthentication) PackageAuthentication {
return packageAuthenticationAll(checks)
}
func (checks packageAuthenticationAll) AuthenticatePackage(meta PackageMeta, localLocation PackageLocation) error {
for _, check := range checks {
err := check.AuthenticatePackage(meta, localLocation)
if err != nil {
return err
}
}
return nil
}
type archiveHashAuthentication struct {
WantSHA256Sum [sha256.Size]byte
}
// NewArchiveChecksumAuthentication returns a PackageAuthentication
// implementation that checks that the original distribution archive matches
// the given hash.
//
// This authentication is suitable only for PackageHTTPURL and
// PackageLocalArchive source locations, because the unpacked layout
// (represented by PackageLocalDir) does not retain access to the original
// source archive. Therefore this authenticator will return an error if its
// given localLocation is not PackageLocalArchive.
func NewArchiveChecksumAuthentication(wantSHA256Sum [sha256.Size]byte) PackageAuthentication {
return archiveHashAuthentication{wantSHA256Sum}
}
func (a archiveHashAuthentication) AuthenticatePackage(meta PackageMeta, localLocation PackageLocation) error {
archiveLocation, ok := localLocation.(PackageLocalArchive)
if !ok {
// A source should not use this authentication type for non-archive
// locations.
return fmt.Errorf("cannot check archive hash for non-archive location %s", localLocation)
}
f, err := os.Open(string(archiveLocation))
if err != nil {
return err
}
defer f.Close()
h := sha256.New()
_, err = io.Copy(h, f)
if err != nil {
return err
}
gotHash := h.Sum(nil)
if !bytes.Equal(gotHash, a.WantSHA256Sum[:]) {
return fmt.Errorf("archive has incorrect SHA-256 checksum %x (expected %x)", gotHash, a.WantSHA256Sum[:])
}
return nil
}