blob: b135580a007a84fd0001527c6782699ad723a4c2 [file] [log] [blame]
Asim Shankarae8d4c52014-10-08 13:03:31 -07001package security
Ankur100eb272014-09-15 16:48:12 -07002
3import (
Ankur1615a7d2014-10-09 11:58:02 -07004 "bytes"
Ankur100eb272014-09-15 16:48:12 -07005 "errors"
6 "fmt"
7 "reflect"
Ankur100eb272014-09-15 16:48:12 -07008 "sync"
9
Jiri Simsa519c5072014-09-17 21:37:57 -070010 "veyron.io/veyron/veyron/security/serialization"
Ankur100eb272014-09-15 16:48:12 -070011
Jiri Simsa519c5072014-09-17 21:37:57 -070012 "veyron.io/veyron/veyron2/security"
13 "veyron.io/veyron/veyron2/vlog"
Ankur100eb272014-09-15 16:48:12 -070014)
15
16const (
17 blessingStoreDataFile = "blessingstore.data"
18 blessingStoreSigFile = "blessingstore.sig"
19)
20
21var errStoreAddMismatch = errors.New("blessing's public key does not match store's public key")
22
Ankur100eb272014-09-15 16:48:12 -070023type persistentState struct {
Asim Shankar48bf0e62014-10-03 16:27:05 -070024 // Store maps BlessingPatterns to the Blessings object that is to be shared
25 // with peers which present blessings of their own that match the pattern.
26 //
27 // All blessings bind to the same public key.
28 Store map[security.BlessingPattern]security.Blessings
Ankur100eb272014-09-15 16:48:12 -070029 // Default is the default Blessings to be shared with peers for which
30 // no other information is available to select blessings.
Ankurb4f03a12014-09-24 10:19:03 -070031 Default security.Blessings
Ankur100eb272014-09-15 16:48:12 -070032}
33
34// blessingStore implements security.BlessingStore.
35type blessingStore struct {
Ankur100eb272014-09-15 16:48:12 -070036 publicKey security.PublicKey
37 dir string
38 signer serialization.Signer
39 mu sync.RWMutex
Asim Shankarae8d4c52014-10-08 13:03:31 -070040 state persistentState // GUARDED_BY(mu)
Ankur100eb272014-09-15 16:48:12 -070041}
42
Asim Shankar48bf0e62014-10-03 16:27:05 -070043func (s *blessingStore) Set(blessings security.Blessings, forPeers security.BlessingPattern) (security.Blessings, error) {
Ankur9e8500a2014-09-17 12:05:32 -070044 if !forPeers.IsValid() {
Asim Shankar48bf0e62014-10-03 16:27:05 -070045 return nil, fmt.Errorf("%q is an invalid BlessingPattern", forPeers)
Ankur100eb272014-09-15 16:48:12 -070046 }
Asim Shankar48bf0e62014-10-03 16:27:05 -070047 if blessings != nil && !reflect.DeepEqual(blessings.PublicKey(), s.publicKey) {
48 return nil, errStoreAddMismatch
49 }
Ankur100eb272014-09-15 16:48:12 -070050 s.mu.Lock()
51 defer s.mu.Unlock()
Asim Shankar48bf0e62014-10-03 16:27:05 -070052 old, hadold := s.state.Store[forPeers]
53 if blessings != nil {
54 s.state.Store[forPeers] = blessings
Ankur100eb272014-09-15 16:48:12 -070055 } else {
Asim Shankar48bf0e62014-10-03 16:27:05 -070056 delete(s.state.Store, forPeers)
Ankur100eb272014-09-15 16:48:12 -070057 }
Ankur100eb272014-09-15 16:48:12 -070058 if err := s.save(); err != nil {
Asim Shankar48bf0e62014-10-03 16:27:05 -070059 if hadold {
60 s.state.Store[forPeers] = old
Ankur100eb272014-09-15 16:48:12 -070061 } else {
Asim Shankar48bf0e62014-10-03 16:27:05 -070062 delete(s.state.Store, forPeers)
Ankur100eb272014-09-15 16:48:12 -070063 }
Asim Shankar48bf0e62014-10-03 16:27:05 -070064 return nil, err
Ankur100eb272014-09-15 16:48:12 -070065 }
Asim Shankar48bf0e62014-10-03 16:27:05 -070066 return old, nil
Ankur100eb272014-09-15 16:48:12 -070067}
68
Ankurb4f03a12014-09-24 10:19:03 -070069func (s *blessingStore) ForPeer(peerBlessings ...string) security.Blessings {
Ankur100eb272014-09-15 16:48:12 -070070 s.mu.RLock()
71 defer s.mu.RUnlock()
72
Asim Shankar48bf0e62014-10-03 16:27:05 -070073 var ret security.Blessings
74 for pattern, blessings := range s.state.Store {
75 if pattern.MatchedBy(peerBlessings...) {
76 if union, err := security.UnionOfBlessings(ret, blessings); err != nil {
77 vlog.Errorf("UnionOfBlessings(%v, %v) failed: %v, dropping the latter from BlessingStore.ForPeers(%v)", ret, blessings, err, peerBlessings)
78 } else {
79 ret = union
Ankur100eb272014-09-15 16:48:12 -070080 }
81 }
82 }
Asim Shankar48bf0e62014-10-03 16:27:05 -070083 return ret
Ankur100eb272014-09-15 16:48:12 -070084}
85
Ankurb4f03a12014-09-24 10:19:03 -070086func (s *blessingStore) Default() security.Blessings {
Ankur100eb272014-09-15 16:48:12 -070087 s.mu.RLock()
88 defer s.mu.RUnlock()
89 if s.state.Default != nil {
90 return s.state.Default
91 }
92 return s.ForPeer()
93}
94
Ankurb4f03a12014-09-24 10:19:03 -070095func (s *blessingStore) SetDefault(blessings security.Blessings) error {
Ankur100eb272014-09-15 16:48:12 -070096 s.mu.Lock()
97 defer s.mu.Unlock()
98 if !reflect.DeepEqual(blessings.PublicKey(), s.publicKey) {
99 return errStoreAddMismatch
100 }
101 oldDefault := s.state.Default
102 s.state.Default = blessings
103 if err := s.save(); err != nil {
104 s.state.Default = oldDefault
105 }
106 return nil
107}
108
109func (s *blessingStore) PublicKey() security.PublicKey {
110 return s.publicKey
111}
112
113func (s *blessingStore) String() string {
114 return fmt.Sprintf("{state: %v, publicKey: %v, dir: %v}", s.state, s.publicKey, s.dir)
115}
116
Ankur1615a7d2014-10-09 11:58:02 -0700117// DebugString return a human-readable string encoding of the store
118// in the following format
119// Default blessing : <Default blessing of the store>
120//
121// Peer pattern : Blessings
122// <pattern> : <blessings>
123// ...
124// <pattern> : <blessings>
125func (br *blessingStore) DebugString() string {
126 const format = "%-30s : %s\n"
127 b := bytes.NewBufferString(fmt.Sprintf("Default blessings: %v\n", br.state.Default))
128
129 b.WriteString(fmt.Sprintf(format, "Peer pattern", "Blessings"))
130 for pattern, blessings := range br.state.Store {
131 b.WriteString(fmt.Sprintf(format, pattern, blessings))
132 }
133 return b.String()
134}
135
Ankur100eb272014-09-15 16:48:12 -0700136func (s *blessingStore) save() error {
137 if (s.signer == nil) && (s.dir == "") {
138 return nil
139 }
140 return encodeAndStore(s.state, s.dir, blessingStoreDataFile, blessingStoreSigFile, s.signer)
141}
142
Ankur7c890592014-10-02 11:36:28 -0700143// newInMemoryBlessingStore returns an in-memory security.BlessingStore for a
Ankur100eb272014-09-15 16:48:12 -0700144// principal with the provided PublicKey.
145//
146// The returned BlessingStore is initialized with an empty set of blessings.
Ankur7c890592014-10-02 11:36:28 -0700147func newInMemoryBlessingStore(publicKey security.PublicKey) security.BlessingStore {
Ankur100eb272014-09-15 16:48:12 -0700148 return &blessingStore{
149 publicKey: publicKey,
Asim Shankar48bf0e62014-10-03 16:27:05 -0700150 state: persistentState{Store: make(map[security.BlessingPattern]security.Blessings)},
Ankur100eb272014-09-15 16:48:12 -0700151 }
152}
153
Ankur7c890592014-10-02 11:36:28 -0700154// newPersistingBlessingStore returns a security.BlessingStore for a principal
Asim Shankarae8d4c52014-10-08 13:03:31 -0700155// that persists all updates to the specified directory and uses the provided
156// signer to ensure integrity of data read from the filesystem.
Ankur100eb272014-09-15 16:48:12 -0700157//
158// The returned BlessingStore is initialized from the existing data present in
159// the directory. The data is verified to have been written by a persisting
Asim Shankarae8d4c52014-10-08 13:03:31 -0700160// BlessingStore object constructed from the same signer.
Ankur100eb272014-09-15 16:48:12 -0700161//
162// Any errors obtained in reading or verifying the data are returned.
Asim Shankarae8d4c52014-10-08 13:03:31 -0700163func newPersistingBlessingStore(directory string, signer serialization.Signer) (security.BlessingStore, error) {
Ankur100eb272014-09-15 16:48:12 -0700164 if directory == "" || signer == nil {
165 return nil, errors.New("directory or signer is not specified")
166 }
167 s := &blessingStore{
Asim Shankarae8d4c52014-10-08 13:03:31 -0700168 publicKey: signer.PublicKey(),
Asim Shankar48bf0e62014-10-03 16:27:05 -0700169 state: persistentState{Store: make(map[security.BlessingPattern]security.Blessings)},
Ankur100eb272014-09-15 16:48:12 -0700170 dir: directory,
171 signer: signer,
172 }
173
174 if err := decodeFromStorage(&s.state, s.dir, blessingStoreDataFile, blessingStoreSigFile, s.signer.PublicKey()); err != nil {
175 return nil, err
176 }
177
Asim Shankar48bf0e62014-10-03 16:27:05 -0700178 for _, b := range s.state.Store {
Asim Shankarae8d4c52014-10-08 13:03:31 -0700179 if !reflect.DeepEqual(b.PublicKey(), s.publicKey) {
180 return nil, fmt.Errorf("directory contains Blessings: %v that are not for the provided PublicKey: %v", b, s.publicKey)
Ankur100eb272014-09-15 16:48:12 -0700181 }
182 }
183 return s, nil
184}