| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package security |
| |
| import ( |
| "bytes" |
| "fmt" |
| "sort" |
| "sync" |
| |
| "v.io/v23/security" |
| "v.io/v23/verror" |
| "v.io/x/lib/vlog" |
| "v.io/x/ref/lib/security/serialization" |
| ) |
| |
| var errRootsAddPattern = verror.Register(pkgPath+".errRootsAddPattern", verror.NoRetry, "{1:}{2:} a root cannot be recognized for all blessing names (i.e., the pattern '...')") |
| |
| // blessingRoots implements security.BlessingRoots. |
| type blessingRoots struct { |
| persistedData SerializerReaderWriter |
| signer serialization.Signer |
| mu sync.RWMutex |
| state blessingRootsState // GUARDED_BY(mu) |
| } |
| |
| func stateMapKey(root security.PublicKey) (string, error) { |
| rootBytes, err := root.MarshalBinary() |
| if err != nil { |
| return "", err |
| } |
| return string(rootBytes), nil |
| } |
| |
| func (br *blessingRoots) Add(root security.PublicKey, pattern security.BlessingPattern) error { |
| if pattern == security.AllPrincipals { |
| return verror.New(errRootsAddPattern, nil) |
| } |
| key, err := stateMapKey(root) |
| if err != nil { |
| return err |
| } |
| |
| br.mu.Lock() |
| defer br.mu.Unlock() |
| patterns := br.state[key] |
| for _, p := range patterns { |
| if p == pattern { |
| return nil |
| } |
| } |
| br.state[key] = append(patterns, pattern) |
| |
| if err := br.save(); err != nil { |
| br.state[key] = patterns[:len(patterns)-1] |
| return err |
| } |
| |
| return nil |
| } |
| |
| func (br *blessingRoots) Recognized(root security.PublicKey, blessing string) error { |
| key, err := stateMapKey(root) |
| if err != nil { |
| return err |
| } |
| |
| br.mu.RLock() |
| defer br.mu.RUnlock() |
| for _, p := range br.state[key] { |
| if p.MatchedBy(blessing) { |
| return nil |
| } |
| } |
| return security.NewErrUnrecognizedRoot(nil, root.String(), nil) |
| } |
| |
| func (br *blessingRoots) Dump() map[security.BlessingPattern][]security.PublicKey { |
| dump := make(map[security.BlessingPattern][]security.PublicKey) |
| br.mu.RLock() |
| defer br.mu.RUnlock() |
| for keyStr, patterns := range br.state { |
| key, err := security.UnmarshalPublicKey([]byte(keyStr)) |
| if err != nil { |
| vlog.Errorf("security.UnmarshalPublicKey(%v) returned error: %v", []byte(keyStr), err) |
| return nil |
| } |
| for _, p := range patterns { |
| dump[p] = append(dump[p], key) |
| } |
| } |
| return dump |
| } |
| |
| // DebugString return a human-readable string encoding of the roots |
| // DebugString encodes all roots into a string in the following |
| // format |
| // |
| // Public key Pattern |
| // <public key> <patterns> |
| // ... |
| // <public key> <patterns> |
| func (br *blessingRoots) DebugString() string { |
| const format = "%-47s %s\n" |
| b := bytes.NewBufferString(fmt.Sprintf(format, "Public key", "Pattern")) |
| var s rootSorter |
| for keyBytes, patterns := range br.state { |
| key, err := security.UnmarshalPublicKey([]byte(keyBytes)) |
| if err != nil { |
| return fmt.Sprintf("failed to decode public key: %v", err) |
| } |
| s = append(s, &root{key, fmt.Sprintf("%v", patterns)}) |
| } |
| sort.Sort(s) |
| for _, r := range s { |
| b.WriteString(fmt.Sprintf(format, r.key, r.patterns)) |
| } |
| return b.String() |
| } |
| |
| type root struct { |
| key security.PublicKey |
| patterns string |
| } |
| |
| type rootSorter []*root |
| |
| func (s rootSorter) Len() int { return len(s) } |
| func (s rootSorter) Less(i, j int) bool { return s[i].patterns < s[j].patterns } |
| func (s rootSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } |
| |
| func (br *blessingRoots) save() error { |
| if (br.signer == nil) && (br.persistedData == nil) { |
| return nil |
| } |
| data, signature, err := br.persistedData.Writers() |
| if err != nil { |
| return err |
| } |
| return encodeAndStore(br.state, data, signature, br.signer) |
| } |
| |
| // newInMemoryBlessingRoots returns an in-memory security.BlessingRoots. |
| // |
| // The returned BlessingRoots is initialized with an empty set of keys. |
| func newInMemoryBlessingRoots() security.BlessingRoots { |
| return &blessingRoots{ |
| state: make(blessingRootsState), |
| } |
| } |
| |
| // newPersistingBlessingRoots returns a security.BlessingRoots for a principal |
| // that is initialized with the persisted data. The returned security.BlessingRoots |
| // also persists any updates to its state. |
| func newPersistingBlessingRoots(persistedData SerializerReaderWriter, signer serialization.Signer) (security.BlessingRoots, error) { |
| if persistedData == nil || signer == nil { |
| return nil, verror.New(errDataOrSignerUnspecified, nil) |
| } |
| br := &blessingRoots{ |
| state: make(blessingRootsState), |
| persistedData: persistedData, |
| signer: signer, |
| } |
| data, signature, err := br.persistedData.Readers() |
| if err != nil { |
| return nil, err |
| } |
| if (data != nil) && (signature != nil) { |
| if err := decodeFromStorage(&br.state, data, signature, br.signer.PublicKey()); err != nil { |
| return nil, err |
| } |
| } |
| return br, nil |
| } |