github.com/ld86/docker@v1.7.1-rc3/graph/manifest.go (about)

     1  package graph
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  
     7  	"github.com/Sirupsen/logrus"
     8  	"github.com/docker/distribution/digest"
     9  	"github.com/docker/docker/registry"
    10  	"github.com/docker/docker/trust"
    11  	"github.com/docker/libtrust"
    12  )
    13  
    14  // loadManifest loads a manifest from a byte array and verifies its content,
    15  // returning the local digest, the manifest itself, whether or not it was
    16  // verified. If ref is a digest, rather than a tag, this will be treated as
    17  // the local digest. An error will be returned if the signature verification
    18  // fails, local digest verification fails and, if provided, the remote digest
    19  // verification fails. The boolean return will only be false without error on
    20  // the failure of signatures trust check.
    21  func (s *TagStore) loadManifest(manifestBytes []byte, ref string, remoteDigest digest.Digest) (digest.Digest, *registry.ManifestData, bool, error) {
    22  	payload, keys, err := unpackSignedManifest(manifestBytes)
    23  	if err != nil {
    24  		return "", nil, false, fmt.Errorf("error unpacking manifest: %v", err)
    25  	}
    26  
    27  	// TODO(stevvooe): It would be a lot better here to build up a stack of
    28  	// verifiers, then push the bytes one time for signatures and digests, but
    29  	// the manifests are typically small, so this optimization is not worth
    30  	// hacking this code without further refactoring.
    31  
    32  	var localDigest digest.Digest
    33  
    34  	// Verify the local digest, if present in ref. ParseDigest will validate
    35  	// that the ref is a digest and verify against that if present. Otherwize
    36  	// (on error), we simply compute the localDigest and proceed.
    37  	if dgst, err := digest.ParseDigest(ref); err == nil {
    38  		// verify the manifest against local ref
    39  		if err := verifyDigest(dgst, payload); err != nil {
    40  			return "", nil, false, fmt.Errorf("verifying local digest: %v", err)
    41  		}
    42  
    43  		localDigest = dgst
    44  	} else {
    45  		// We don't have a local digest, since we are working from a tag.
    46  		// Compute the digest of the payload and return that.
    47  		logrus.Debugf("provided manifest reference %q is not a digest: %v", ref, err)
    48  		localDigest, err = digest.FromBytes(payload)
    49  		if err != nil {
    50  			// near impossible
    51  			logrus.Errorf("error calculating local digest during tag pull: %v", err)
    52  			return "", nil, false, err
    53  		}
    54  	}
    55  
    56  	// verify against the remote digest, if available
    57  	if remoteDigest != "" {
    58  		if err := verifyDigest(remoteDigest, payload); err != nil {
    59  			return "", nil, false, fmt.Errorf("verifying remote digest: %v", err)
    60  		}
    61  	}
    62  
    63  	var manifest registry.ManifestData
    64  	if err := json.Unmarshal(payload, &manifest); err != nil {
    65  		return "", nil, false, fmt.Errorf("error unmarshalling manifest: %s", err)
    66  	}
    67  
    68  	// validate the contents of the manifest
    69  	if err := validateManifest(&manifest); err != nil {
    70  		return "", nil, false, err
    71  	}
    72  
    73  	var verified bool
    74  	verified, err = s.verifyTrustedKeys(manifest.Name, keys)
    75  	if err != nil {
    76  		return "", nil, false, fmt.Errorf("error verifying trusted keys: %v", err)
    77  	}
    78  
    79  	return localDigest, &manifest, verified, nil
    80  }
    81  
    82  // unpackSignedManifest takes the raw, signed manifest bytes, unpacks the jws
    83  // and returns the payload and public keys used to signed the manifest.
    84  // Signatures are verified for authenticity but not against the trust store.
    85  func unpackSignedManifest(p []byte) ([]byte, []libtrust.PublicKey, error) {
    86  	sig, err := libtrust.ParsePrettySignature(p, "signatures")
    87  	if err != nil {
    88  		return nil, nil, fmt.Errorf("error parsing payload: %s", err)
    89  	}
    90  
    91  	keys, err := sig.Verify()
    92  	if err != nil {
    93  		return nil, nil, fmt.Errorf("error verifying payload: %s", err)
    94  	}
    95  
    96  	payload, err := sig.Payload()
    97  	if err != nil {
    98  		return nil, nil, fmt.Errorf("error retrieving payload: %s", err)
    99  	}
   100  
   101  	return payload, keys, nil
   102  }
   103  
   104  // verifyTrustedKeys checks the keys provided against the trust store,
   105  // ensuring that the provided keys are trusted for the namespace. The keys
   106  // provided from this method must come from the signatures provided as part of
   107  // the manifest JWS package, obtained from unpackSignedManifest or libtrust.
   108  func (s *TagStore) verifyTrustedKeys(namespace string, keys []libtrust.PublicKey) (verified bool, err error) {
   109  	if namespace[0] != '/' {
   110  		namespace = "/" + namespace
   111  	}
   112  
   113  	for _, key := range keys {
   114  		b, err := key.MarshalJSON()
   115  		if err != nil {
   116  			return false, fmt.Errorf("error marshalling public key: %s", err)
   117  		}
   118  		// Check key has read/write permission (0x03)
   119  		v, err := s.trustService.CheckKey(namespace, b, 0x03)
   120  		if err != nil {
   121  			vErr, ok := err.(trust.NotVerifiedError)
   122  			if !ok {
   123  				return false, fmt.Errorf("error running key check: %s", err)
   124  			}
   125  			logrus.Debugf("Key check result: %v", vErr)
   126  		}
   127  		verified = v
   128  	}
   129  
   130  	if verified {
   131  		logrus.Debug("Key check result: verified")
   132  	}
   133  
   134  	return
   135  }
   136  
   137  // verifyDigest checks the contents of p against the provided digest. Note
   138  // that for manifests, this is the signed payload and not the raw bytes with
   139  // signatures.
   140  func verifyDigest(dgst digest.Digest, p []byte) error {
   141  	if err := dgst.Validate(); err != nil {
   142  		return fmt.Errorf("error validating  digest %q: %v", dgst, err)
   143  	}
   144  
   145  	verifier, err := digest.NewDigestVerifier(dgst)
   146  	if err != nil {
   147  		// There are not many ways this can go wrong: if it does, its
   148  		// fatal. Likley, the cause would be poor validation of the
   149  		// incoming reference.
   150  		return fmt.Errorf("error creating verifier for digest %q: %v", dgst, err)
   151  	}
   152  
   153  	if _, err := verifier.Write(p); err != nil {
   154  		return fmt.Errorf("error writing payload to digest verifier (verifier target %q): %v", dgst, err)
   155  	}
   156  
   157  	if !verifier.Verified() {
   158  		return fmt.Errorf("verification against digest %q failed", dgst)
   159  	}
   160  
   161  	return nil
   162  }
   163  
   164  func validateManifest(manifest *registry.ManifestData) error {
   165  	if manifest.SchemaVersion != 1 {
   166  		return fmt.Errorf("unsupported schema version: %d", manifest.SchemaVersion)
   167  	}
   168  
   169  	if len(manifest.FSLayers) != len(manifest.History) {
   170  		return fmt.Errorf("length of history not equal to number of layers")
   171  	}
   172  
   173  	if len(manifest.FSLayers) == 0 {
   174  		return fmt.Errorf("no FSLayers in manifest")
   175  	}
   176  
   177  	return nil
   178  }