2014-09-30 20:03:57 -04:00
|
|
|
package graph
|
|
|
|
|
|
|
|
import (
|
2015-01-02 14:13:11 -05:00
|
|
|
"bytes"
|
2014-09-30 20:03:57 -04:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
|
2015-01-02 14:13:11 -05:00
|
|
|
log "github.com/Sirupsen/logrus"
|
2015-03-18 02:45:30 -04:00
|
|
|
"github.com/docker/distribution/digest"
|
2014-09-30 20:03:57 -04:00
|
|
|
"github.com/docker/docker/engine"
|
|
|
|
"github.com/docker/docker/registry"
|
2015-03-18 02:45:30 -04:00
|
|
|
"github.com/docker/docker/utils"
|
2015-01-02 14:13:11 -05:00
|
|
|
"github.com/docker/libtrust"
|
2014-09-30 20:03:57 -04:00
|
|
|
)
|
|
|
|
|
2015-01-30 17:20:32 -05:00
|
|
|
// loadManifest loads a manifest from a byte array and verifies its content.
|
|
|
|
// The signature must be verified or an error is returned. If the manifest
|
|
|
|
// contains no signatures by a trusted key for the name in the manifest, the
|
|
|
|
// image is not considered verified. The parsed manifest object and a boolean
|
|
|
|
// for whether the manifest is verified is returned.
|
2015-03-18 02:45:30 -04:00
|
|
|
func (s *TagStore) loadManifest(eng *engine.Engine, manifestBytes []byte, dgst, ref string) (*registry.ManifestData, bool, error) {
|
2015-01-02 14:13:11 -05:00
|
|
|
sig, err := libtrust.ParsePrettySignature(manifestBytes, "signatures")
|
|
|
|
if err != nil {
|
|
|
|
return nil, false, fmt.Errorf("error parsing payload: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
keys, err := sig.Verify()
|
|
|
|
if err != nil {
|
|
|
|
return nil, false, fmt.Errorf("error verifying payload: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
payload, err := sig.Payload()
|
|
|
|
if err != nil {
|
|
|
|
return nil, false, fmt.Errorf("error retrieving payload: %s", err)
|
|
|
|
}
|
|
|
|
|
2015-03-18 02:45:30 -04:00
|
|
|
var manifestDigest digest.Digest
|
|
|
|
|
|
|
|
if dgst != "" {
|
|
|
|
manifestDigest, err = digest.ParseDigest(dgst)
|
|
|
|
if err != nil {
|
|
|
|
return nil, false, fmt.Errorf("invalid manifest digest from registry: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
dgstVerifier, err := digest.NewDigestVerifier(manifestDigest)
|
|
|
|
if err != nil {
|
|
|
|
return nil, false, fmt.Errorf("unable to verify manifest digest from registry: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
dgstVerifier.Write(payload)
|
|
|
|
|
|
|
|
if !dgstVerifier.Verified() {
|
|
|
|
computedDigest, _ := digest.FromBytes(payload)
|
|
|
|
return nil, false, fmt.Errorf("unable to verify manifest digest: registry has %q, computed %q", manifestDigest, computedDigest)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if utils.DigestReference(ref) && ref != manifestDigest.String() {
|
|
|
|
return nil, false, fmt.Errorf("mismatching image manifest digest: got %q, expected %q", manifestDigest, ref)
|
|
|
|
}
|
|
|
|
|
2015-01-02 14:13:11 -05:00
|
|
|
var manifest registry.ManifestData
|
|
|
|
if err := json.Unmarshal(payload, &manifest); err != nil {
|
|
|
|
return nil, false, fmt.Errorf("error unmarshalling manifest: %s", err)
|
|
|
|
}
|
|
|
|
if manifest.SchemaVersion != 1 {
|
|
|
|
return nil, false, fmt.Errorf("unsupported schema version: %d", manifest.SchemaVersion)
|
|
|
|
}
|
|
|
|
|
|
|
|
var verified bool
|
|
|
|
for _, key := range keys {
|
|
|
|
job := eng.Job("trust_key_check")
|
|
|
|
b, err := key.MarshalJSON()
|
|
|
|
if err != nil {
|
|
|
|
return nil, false, fmt.Errorf("error marshalling public key: %s", err)
|
|
|
|
}
|
|
|
|
namespace := manifest.Name
|
|
|
|
if namespace[0] != '/' {
|
|
|
|
namespace = "/" + namespace
|
|
|
|
}
|
|
|
|
stdoutBuffer := bytes.NewBuffer(nil)
|
|
|
|
|
|
|
|
job.Args = append(job.Args, namespace)
|
|
|
|
job.Setenv("PublicKey", string(b))
|
|
|
|
// Check key has read/write permission (0x03)
|
|
|
|
job.SetenvInt("Permission", 0x03)
|
|
|
|
job.Stdout.Add(stdoutBuffer)
|
|
|
|
if err = job.Run(); err != nil {
|
|
|
|
return nil, false, fmt.Errorf("error running key check: %s", err)
|
|
|
|
}
|
|
|
|
result := engine.Tail(stdoutBuffer, 1)
|
|
|
|
log.Debugf("Key check result: %q", result)
|
|
|
|
if result == "verified" {
|
|
|
|
verified = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &manifest, verified, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkValidManifest(manifest *registry.ManifestData) error {
|
|
|
|
if len(manifest.FSLayers) != len(manifest.History) {
|
|
|
|
return fmt.Errorf("length of history not equal to number of layers")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(manifest.FSLayers) == 0 {
|
|
|
|
return fmt.Errorf("no FSLayers in manifest")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|