github.com/blixtra/rkt@v0.8.1-0.20160204105720-ab0d1add1a43/pkg/keystore/keystore.go (about)

     1  // Copyright 2014 The rkt Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package keystore implements the ACI keystore.
    16  package keystore
    17  
    18  import (
    19  	"bytes"
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"io/ioutil"
    24  	"os"
    25  	"path"
    26  	"path/filepath"
    27  	"strings"
    28  
    29  	"github.com/appc/spec/schema/types"
    30  	"github.com/coreos/rkt/common"
    31  	"github.com/hashicorp/errwrap"
    32  	"golang.org/x/crypto/openpgp"
    33  )
    34  
    35  // A Config structure is used to configure a Keystore.
    36  type Config struct {
    37  	LocalRootPath    string
    38  	LocalPrefixPath  string
    39  	SystemRootPath   string
    40  	SystemPrefixPath string
    41  }
    42  
    43  // A Keystore represents a repository of trusted public keys which can be
    44  // used to verify PGP signatures.
    45  type Keystore struct {
    46  	*Config
    47  }
    48  
    49  // New returns a new Keystore based on config.
    50  func New(config *Config) *Keystore {
    51  	if config == nil {
    52  		config = defaultConfig
    53  	}
    54  	return &Keystore{config}
    55  }
    56  
    57  func NewConfig(systemPath, localPath string) *Config {
    58  	return &Config{
    59  		LocalRootPath:    filepath.Join(localPath, "trustedkeys", "root.d"),
    60  		LocalPrefixPath:  filepath.Join(localPath, "trustedkeys", "prefix.d"),
    61  		SystemRootPath:   filepath.Join(systemPath, "trustedkeys", "root.d"),
    62  		SystemPrefixPath: filepath.Join(systemPath, "trustedkeys", "prefix.d"),
    63  	}
    64  }
    65  
    66  var defaultConfig = NewConfig(common.DefaultSystemConfigDir, common.DefaultLocalConfigDir)
    67  
    68  // CheckSignature is a convenience method for creating a Keystore with a default
    69  // configuration and invoking CheckSignature.
    70  func CheckSignature(prefix string, signed, signature io.ReadSeeker) (*openpgp.Entity, error) {
    71  	ks := New(defaultConfig)
    72  	return checkSignature(ks, prefix, signed, signature)
    73  }
    74  
    75  // CheckSignature takes a signed file and a detached signature and returns the signer
    76  // if the signature is signed by a trusted signer.
    77  // If the signer is unknown or not trusted, opengpg.ErrUnknownIssuer is returned.
    78  func (ks *Keystore) CheckSignature(prefix string, signed, signature io.ReadSeeker) (*openpgp.Entity, error) {
    79  	return checkSignature(ks, prefix, signed, signature)
    80  }
    81  
    82  func checkSignature(ks *Keystore, prefix string, signed, signature io.ReadSeeker) (*openpgp.Entity, error) {
    83  	acidentifier, err := types.NewACIdentifier(prefix)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	keyring, err := ks.loadKeyring(acidentifier.String())
    88  	if err != nil {
    89  		return nil, errwrap.Wrap(errors.New("keystore: error loading keyring"), err)
    90  	}
    91  	entities, err := openpgp.CheckArmoredDetachedSignature(keyring, signed, signature)
    92  	if err == io.EOF {
    93  		// When the signature is binary instead of armored, the error is io.EOF.
    94  		// Let's try with binary signatures as well
    95  		if _, err := signed.Seek(0, 0); err != nil {
    96  			return nil, errwrap.Wrap(errors.New("error seeking ACI file"), err)
    97  		}
    98  		if _, err := signature.Seek(0, 0); err != nil {
    99  			return nil, errwrap.Wrap(errors.New("error seeking signature file"), err)
   100  		}
   101  		entities, err = openpgp.CheckDetachedSignature(keyring, signed, signature)
   102  	}
   103  	if err == io.EOF {
   104  		// otherwise, the client failure is just "EOF", which is not helpful
   105  		return nil, fmt.Errorf("keystore: no valid signatures found in signature file")
   106  	}
   107  	return entities, err
   108  }
   109  
   110  // DeleteTrustedKeyPrefix deletes the prefix trusted key identified by fingerprint.
   111  func (ks *Keystore) DeleteTrustedKeyPrefix(prefix, fingerprint string) error {
   112  	acidentifier, err := types.NewACIdentifier(prefix)
   113  	if err != nil {
   114  		return err
   115  	}
   116  	return os.Remove(path.Join(ks.LocalPrefixPath, acidentifier.String(), fingerprint))
   117  }
   118  
   119  // MaskTrustedKeySystemPrefix masks the system prefix trusted key identified by fingerprint.
   120  func (ks *Keystore) MaskTrustedKeySystemPrefix(prefix, fingerprint string) (string, error) {
   121  	acidentifier, err := types.NewACIdentifier(prefix)
   122  	if err != nil {
   123  		return "", err
   124  	}
   125  	dst := path.Join(ks.LocalPrefixPath, acidentifier.String(), fingerprint)
   126  	return dst, ioutil.WriteFile(dst, []byte(""), 0644)
   127  }
   128  
   129  // DeleteTrustedKeyRoot deletes the root trusted key identified by fingerprint.
   130  func (ks *Keystore) DeleteTrustedKeyRoot(fingerprint string) error {
   131  	return os.Remove(path.Join(ks.LocalRootPath, fingerprint))
   132  }
   133  
   134  // MaskTrustedKeySystemRoot masks the system root trusted key identified by fingerprint.
   135  func (ks *Keystore) MaskTrustedKeySystemRoot(fingerprint string) (string, error) {
   136  	dst := path.Join(ks.LocalRootPath, fingerprint)
   137  	return dst, ioutil.WriteFile(dst, []byte(""), 0644)
   138  }
   139  
   140  // TrustKeyPrefixExists returns whether or not there exists 1 or more trusted
   141  // keys for a given prefix, or for any parent prefix.
   142  func (ks *Keystore) TrustedKeyPrefixExists(prefix string) (bool, error) {
   143  	acidentifier, err := types.NewACIdentifier(prefix)
   144  	if err != nil {
   145  		return false, err
   146  	}
   147  
   148  	pathNamesPrefix := []string{
   149  		// example: /etc/rkt/trustedkeys/prefix.d/coreos.com/etcd
   150  		path.Join(ks.LocalPrefixPath, acidentifier.String()),
   151  		// example: /usr/lib/rkt/trustedkeys/prefix.d/coreos.com/etcd
   152  		path.Join(ks.SystemPrefixPath, acidentifier.String()),
   153  	}
   154  
   155  	for _, p := range pathNamesPrefix {
   156  		_, err := os.Stat(p)
   157  		if os.IsNotExist(err) {
   158  			continue
   159  		}
   160  		if err != nil {
   161  			return false, errwrap.Wrap(fmt.Errorf("cannot check dir %q", p), err)
   162  		}
   163  		files, err := ioutil.ReadDir(p)
   164  		if err != nil {
   165  			return false, errwrap.Wrap(fmt.Errorf("cannot list files in dir %q", p), err)
   166  		}
   167  		for _, f := range files {
   168  			if !f.IsDir() && f.Size() > 0 {
   169  				return true, nil
   170  			}
   171  		}
   172  	}
   173  
   174  	parentPrefix, _ := path.Split(prefix)
   175  	parentPrefix = strings.Trim(parentPrefix, "/")
   176  
   177  	if parentPrefix != "" {
   178  		return ks.TrustedKeyPrefixExists(parentPrefix)
   179  	}
   180  
   181  	return false, nil
   182  }
   183  
   184  // TrustedKeyPrefixWithFingerprintExists returns whether or not a trusted key with the fingerprint of the key accessible through r exists for the given prefix.
   185  func (ks *Keystore) TrustedKeyPrefixWithFingerprintExists(prefix string, r io.ReadSeeker) (bool, error) {
   186  	defer r.Seek(0, os.SEEK_SET)
   187  
   188  	entityList, err := openpgp.ReadArmoredKeyRing(r)
   189  	if err != nil {
   190  		return false, err
   191  	}
   192  	if len(entityList) < 1 {
   193  		return false, errors.New("missing opengpg entity")
   194  	}
   195  	pubKey := entityList[0].PrimaryKey
   196  	fileName := fingerprintToFilename(pubKey.Fingerprint)
   197  
   198  	pathNamesRoot := []string{
   199  		// example: /etc/rkt/trustedkeys/root.d/8b86de38890ddb7291867b025210bd8888182190
   200  		path.Join(ks.LocalRootPath, fileName),
   201  		// example: /usr/lib/rkt/trustedkeys/root.d/8b86de38890ddb7291867b025210bd8888182190
   202  		path.Join(ks.SystemRootPath, fileName),
   203  	}
   204  
   205  	var pathNamesPrefix []string
   206  	if prefix != "" {
   207  		acidentifier, err := types.NewACIdentifier(prefix)
   208  		if err != nil {
   209  			return false, err
   210  		}
   211  		pathNamesPrefix = []string{
   212  			// example: /etc/rkt/trustedkeys/prefix.d/coreos.com/etcd/8b86de38890ddb7291867b025210bd8888182190
   213  			path.Join(ks.LocalPrefixPath, acidentifier.String(), fileName),
   214  			// example: /usr/lib/rkt/trustedkeys/prefix.d/coreos.com/etcd/8b86de38890ddb7291867b025210bd8888182190
   215  			path.Join(ks.SystemPrefixPath, acidentifier.String(), fileName),
   216  		}
   217  	}
   218  
   219  	pathNames := append(pathNamesRoot, pathNamesPrefix...)
   220  	for _, p := range pathNames {
   221  		_, err := os.Stat(p)
   222  		if err == nil {
   223  			return true, nil
   224  		} else if !os.IsNotExist(err) {
   225  			return false, errwrap.Wrap(fmt.Errorf("cannot check file %q", p), err)
   226  		}
   227  	}
   228  
   229  	return false, nil
   230  }
   231  
   232  // StoreTrustedKeyPrefix stores the contents of public key r as a prefix trusted key.
   233  func (ks *Keystore) StoreTrustedKeyPrefix(prefix string, r io.Reader) (string, error) {
   234  	acidentifier, err := types.NewACIdentifier(prefix)
   235  	if err != nil {
   236  		return "", err
   237  	}
   238  	return storeTrustedKey(path.Join(ks.LocalPrefixPath, acidentifier.String()), r)
   239  }
   240  
   241  // StoreTrustedKeyRoot stores the contents of public key r as a root trusted key.
   242  func (ks *Keystore) StoreTrustedKeyRoot(r io.Reader) (string, error) {
   243  	return storeTrustedKey(ks.LocalRootPath, r)
   244  }
   245  
   246  func storeTrustedKey(dir string, r io.Reader) (string, error) {
   247  	pubkeyBytes, err := ioutil.ReadAll(r)
   248  	if err != nil {
   249  		return "", err
   250  	}
   251  	if err := os.MkdirAll(dir, 0755); err != nil {
   252  		return "", err
   253  	}
   254  	entityList, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(pubkeyBytes))
   255  	if err != nil {
   256  		return "", err
   257  	}
   258  	if len(entityList) < 1 {
   259  		return "", errors.New("missing opengpg entity")
   260  	}
   261  	pubKey := entityList[0].PrimaryKey
   262  	trustedKeyPath := path.Join(dir, fingerprintToFilename(pubKey.Fingerprint))
   263  	if err := ioutil.WriteFile(trustedKeyPath, pubkeyBytes, 0644); err != nil {
   264  		return "", err
   265  	}
   266  	return trustedKeyPath, nil
   267  }
   268  
   269  func entityFromFile(path string) (*openpgp.Entity, error) {
   270  	trustedKey, err := os.Open(path)
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  	defer trustedKey.Close()
   275  	entityList, err := openpgp.ReadArmoredKeyRing(trustedKey)
   276  	if err != nil {
   277  		return nil, err
   278  	}
   279  	if len(entityList) < 1 {
   280  		return nil, errors.New("missing opengpg entity")
   281  	}
   282  	fingerprint := fingerprintToFilename(entityList[0].PrimaryKey.Fingerprint)
   283  	keyFile := filepath.Base(trustedKey.Name())
   284  	if fingerprint != keyFile {
   285  		return nil, fmt.Errorf("fingerprint mismatch: %q:%q", keyFile, fingerprint)
   286  	}
   287  	return entityList[0], nil
   288  }
   289  
   290  func (ks *Keystore) loadKeyring(prefix string) (openpgp.KeyRing, error) {
   291  	acidentifier, err := types.NewACIdentifier(prefix)
   292  	if err != nil {
   293  		return nil, err
   294  	}
   295  	var keyring openpgp.EntityList
   296  	trustedKeys := make(map[string]*openpgp.Entity)
   297  
   298  	prefixRoot := strings.Split(acidentifier.String(), "/")[0]
   299  	paths := []struct {
   300  		root     string
   301  		fullPath string
   302  	}{
   303  		{ks.SystemRootPath, ks.SystemRootPath},
   304  		{ks.LocalRootPath, ks.LocalRootPath},
   305  		{path.Join(ks.SystemPrefixPath, prefixRoot), path.Join(ks.SystemPrefixPath, acidentifier.String())},
   306  		{path.Join(ks.LocalPrefixPath, prefixRoot), path.Join(ks.LocalPrefixPath, acidentifier.String())},
   307  	}
   308  	for _, p := range paths {
   309  		err := filepath.Walk(p.root, func(path string, info os.FileInfo, err error) error {
   310  			if err != nil && !os.IsNotExist(err) {
   311  				return err
   312  			}
   313  			if info == nil {
   314  				return nil
   315  			}
   316  			if info.IsDir() {
   317  				switch {
   318  				case strings.HasPrefix(p.fullPath, path):
   319  					return nil
   320  				default:
   321  					return filepath.SkipDir
   322  				}
   323  			}
   324  			// Remove trust for default keys.
   325  			if info.Size() == 0 {
   326  				delete(trustedKeys, info.Name())
   327  				return nil
   328  			}
   329  			entity, err := entityFromFile(path)
   330  			if err != nil {
   331  				return err
   332  			}
   333  			trustedKeys[fingerprintToFilename(entity.PrimaryKey.Fingerprint)] = entity
   334  			return nil
   335  		})
   336  		if err != nil {
   337  			return nil, err
   338  		}
   339  	}
   340  
   341  	for _, v := range trustedKeys {
   342  		keyring = append(keyring, v)
   343  	}
   344  	return keyring, nil
   345  }
   346  
   347  func fingerprintToFilename(fp [20]byte) string {
   348  	return fmt.Sprintf("%x", fp)
   349  }
   350  
   351  // NewTestKeystore creates a new KeyStore backed by a temp directory.
   352  // NewTestKeystore returns a KeyStore, the path to the temp directory, and
   353  // an error if any.
   354  func NewTestKeystore() (*Keystore, string, error) {
   355  	dir, err := ioutil.TempDir("", "keystore-test")
   356  	if err != nil {
   357  		return nil, "", err
   358  	}
   359  	systemDir := filepath.Join(dir, common.DefaultSystemConfigDir)
   360  	localDir := filepath.Join(dir, common.DefaultLocalConfigDir)
   361  	c := NewConfig(systemDir, localDir)
   362  	for _, path := range []string{c.LocalRootPath, c.SystemRootPath, c.LocalPrefixPath, c.SystemPrefixPath} {
   363  		if err := os.MkdirAll(path, 0755); err != nil {
   364  			return nil, "", err
   365  		}
   366  	}
   367  	return New(c), dir, nil
   368  }