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 }