Asim Shankar | ae8d4c5 | 2014-10-08 13:03:31 -0700 | [diff] [blame] | 1 | package security |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 2 | |
| 3 | import ( |
Ankur | 1615a7d | 2014-10-09 11:58:02 -0700 | [diff] [blame] | 4 | "bytes" |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 5 | "errors" |
| 6 | "fmt" |
| 7 | "reflect" |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 8 | "sync" |
| 9 | |
Jiri Simsa | 519c507 | 2014-09-17 21:37:57 -0700 | [diff] [blame] | 10 | "veyron.io/veyron/veyron/security/serialization" |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 11 | |
Jiri Simsa | 519c507 | 2014-09-17 21:37:57 -0700 | [diff] [blame] | 12 | "veyron.io/veyron/veyron2/security" |
| 13 | "veyron.io/veyron/veyron2/vlog" |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 14 | ) |
| 15 | |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 16 | var errStoreAddMismatch = errors.New("blessing's public key does not match store's public key") |
| 17 | |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 18 | type blessings struct { |
| 19 | Value security.WireBlessings |
| 20 | unmarshaled security.Blessings |
| 21 | } |
| 22 | |
| 23 | func (w *blessings) Blessings() security.Blessings { |
| 24 | if w == nil { |
| 25 | return nil |
| 26 | } |
| 27 | return w.unmarshaled |
| 28 | } |
| 29 | |
| 30 | func (w *blessings) Verify() error { |
| 31 | var err error |
| 32 | if w.unmarshaled == nil { |
| 33 | w.unmarshaled, err = security.NewBlessings(w.Value) |
| 34 | } |
| 35 | return err |
| 36 | } |
| 37 | |
| 38 | func newWireBlessings(b security.Blessings) *blessings { |
| 39 | return &blessings{Value: security.MarshalBlessings(b), unmarshaled: b} |
| 40 | } |
| 41 | |
| 42 | type state struct { |
Asim Shankar | 48bf0e6 | 2014-10-03 16:27:05 -0700 | [diff] [blame] | 43 | // Store maps BlessingPatterns to the Blessings object that is to be shared |
| 44 | // with peers which present blessings of their own that match the pattern. |
| 45 | // |
| 46 | // All blessings bind to the same public key. |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 47 | Store map[security.BlessingPattern]*blessings |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 48 | // Default is the default Blessings to be shared with peers for which |
| 49 | // no other information is available to select blessings. |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 50 | Default *blessings |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 51 | } |
| 52 | |
| 53 | // blessingStore implements security.BlessingStore. |
| 54 | type blessingStore struct { |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 55 | publicKey security.PublicKey |
| 56 | serializer SerializerReaderWriter |
| 57 | signer serialization.Signer |
| 58 | mu sync.RWMutex |
| 59 | state state // GUARDED_BY(mu) |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 60 | } |
| 61 | |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 62 | func (bs *blessingStore) Set(blessings security.Blessings, forPeers security.BlessingPattern) (security.Blessings, error) { |
Ankur | 9e8500a | 2014-09-17 12:05:32 -0700 | [diff] [blame] | 63 | if !forPeers.IsValid() { |
Asim Shankar | 48bf0e6 | 2014-10-03 16:27:05 -0700 | [diff] [blame] | 64 | return nil, fmt.Errorf("%q is an invalid BlessingPattern", forPeers) |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 65 | } |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 66 | if blessings != nil && !reflect.DeepEqual(blessings.PublicKey(), bs.publicKey) { |
Asim Shankar | 48bf0e6 | 2014-10-03 16:27:05 -0700 | [diff] [blame] | 67 | return nil, errStoreAddMismatch |
| 68 | } |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 69 | bs.mu.Lock() |
| 70 | defer bs.mu.Unlock() |
| 71 | old, hadold := bs.state.Store[forPeers] |
Asim Shankar | 48bf0e6 | 2014-10-03 16:27:05 -0700 | [diff] [blame] | 72 | if blessings != nil { |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 73 | bs.state.Store[forPeers] = newWireBlessings(blessings) |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 74 | } else { |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 75 | delete(bs.state.Store, forPeers) |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 76 | } |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 77 | if err := bs.save(); err != nil { |
Asim Shankar | 48bf0e6 | 2014-10-03 16:27:05 -0700 | [diff] [blame] | 78 | if hadold { |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 79 | bs.state.Store[forPeers] = old |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 80 | } else { |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 81 | delete(bs.state.Store, forPeers) |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 82 | } |
Asim Shankar | 48bf0e6 | 2014-10-03 16:27:05 -0700 | [diff] [blame] | 83 | return nil, err |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 84 | } |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 85 | return old.Blessings(), nil |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 86 | } |
| 87 | |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 88 | func (bs *blessingStore) ForPeer(peerBlessings ...string) security.Blessings { |
| 89 | bs.mu.RLock() |
| 90 | defer bs.mu.RUnlock() |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 91 | |
Asim Shankar | 48bf0e6 | 2014-10-03 16:27:05 -0700 | [diff] [blame] | 92 | var ret security.Blessings |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 93 | for pattern, wb := range bs.state.Store { |
Asim Shankar | 48bf0e6 | 2014-10-03 16:27:05 -0700 | [diff] [blame] | 94 | if pattern.MatchedBy(peerBlessings...) { |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 95 | b := wb.Blessings() |
| 96 | if union, err := security.UnionOfBlessings(ret, b); err != nil { |
| 97 | vlog.Errorf("UnionOfBlessings(%v, %v) failed: %v, dropping the latter from BlessingStore.ForPeers(%v)", ret, b, err, peerBlessings) |
Asim Shankar | 48bf0e6 | 2014-10-03 16:27:05 -0700 | [diff] [blame] | 98 | } else { |
| 99 | ret = union |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 100 | } |
| 101 | } |
| 102 | } |
Asim Shankar | 48bf0e6 | 2014-10-03 16:27:05 -0700 | [diff] [blame] | 103 | return ret |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 104 | } |
| 105 | |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 106 | func (bs *blessingStore) Default() security.Blessings { |
| 107 | bs.mu.RLock() |
| 108 | defer bs.mu.RUnlock() |
| 109 | if bs.state.Default != nil { |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 110 | return bs.state.Default.Blessings() |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 111 | } |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 112 | return bs.ForPeer() |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 113 | } |
| 114 | |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 115 | func (bs *blessingStore) SetDefault(blessings security.Blessings) error { |
| 116 | bs.mu.Lock() |
| 117 | defer bs.mu.Unlock() |
| 118 | if !reflect.DeepEqual(blessings.PublicKey(), bs.publicKey) { |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 119 | return errStoreAddMismatch |
| 120 | } |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 121 | oldDefault := bs.state.Default |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 122 | bs.state.Default = newWireBlessings(blessings) |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 123 | if err := bs.save(); err != nil { |
| 124 | bs.state.Default = oldDefault |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 125 | } |
| 126 | return nil |
| 127 | } |
| 128 | |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 129 | func (bs *blessingStore) PublicKey() security.PublicKey { |
| 130 | return bs.publicKey |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 131 | } |
| 132 | |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 133 | func (bs *blessingStore) String() string { |
| 134 | return fmt.Sprintf("{state: %v, publicKey: %v}", bs.state, bs.publicKey) |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 135 | } |
| 136 | |
Ankur | 1615a7d | 2014-10-09 11:58:02 -0700 | [diff] [blame] | 137 | // DebugString return a human-readable string encoding of the store |
| 138 | // in the following format |
| 139 | // Default blessing : <Default blessing of the store> |
| 140 | // |
| 141 | // Peer pattern : Blessings |
| 142 | // <pattern> : <blessings> |
| 143 | // ... |
| 144 | // <pattern> : <blessings> |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 145 | func (bs *blessingStore) DebugString() string { |
Ankur | 1615a7d | 2014-10-09 11:58:02 -0700 | [diff] [blame] | 146 | const format = "%-30s : %s\n" |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 147 | b := bytes.NewBufferString(fmt.Sprintf("Default blessings: %v\n", bs.state.Default.Blessings())) |
Ankur | 1615a7d | 2014-10-09 11:58:02 -0700 | [diff] [blame] | 148 | |
| 149 | b.WriteString(fmt.Sprintf(format, "Peer pattern", "Blessings")) |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 150 | for pattern, wb := range bs.state.Store { |
| 151 | b.WriteString(fmt.Sprintf(format, pattern, wb.Blessings())) |
Ankur | 1615a7d | 2014-10-09 11:58:02 -0700 | [diff] [blame] | 152 | } |
| 153 | return b.String() |
| 154 | } |
| 155 | |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 156 | func (bs *blessingStore) save() error { |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 157 | if (bs.signer == nil) && (bs.serializer == nil) { |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 158 | return nil |
| 159 | } |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 160 | data, signature, err := bs.serializer.Writers() |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 161 | if err != nil { |
| 162 | return err |
| 163 | } |
| 164 | return encodeAndStore(bs.state, data, signature, bs.signer) |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 165 | } |
| 166 | |
Ankur | 7c89059 | 2014-10-02 11:36:28 -0700 | [diff] [blame] | 167 | // newInMemoryBlessingStore returns an in-memory security.BlessingStore for a |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 168 | // principal with the provided PublicKey. |
| 169 | // |
| 170 | // The returned BlessingStore is initialized with an empty set of blessings. |
Ankur | 7c89059 | 2014-10-02 11:36:28 -0700 | [diff] [blame] | 171 | func newInMemoryBlessingStore(publicKey security.PublicKey) security.BlessingStore { |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 172 | return &blessingStore{ |
| 173 | publicKey: publicKey, |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 174 | state: state{Store: make(map[security.BlessingPattern]*blessings)}, |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 175 | } |
| 176 | } |
| 177 | |
Ankur | 27c56fd | 2014-11-17 19:30:34 -0800 | [diff] [blame] | 178 | // TODO(ataly, ashankar): Get rid of this struct once we have switched all credentials |
| 179 | // directories to the new serialization format. |
| 180 | type oldState struct { |
| 181 | Store map[security.BlessingPattern]security.WireBlessings |
| 182 | Default security.WireBlessings |
| 183 | } |
| 184 | |
| 185 | // TODO(ataly, ashankar): Get rid of this method once we have switched all |
| 186 | // credentials directories to the new serialization format. |
| 187 | func (bs *blessingStore) tryOldFormat() bool { |
| 188 | var empty security.WireBlessings |
| 189 | if len(bs.state.Store) == 0 { |
| 190 | return bs.state.Default == nil || reflect.DeepEqual(bs.state.Default.Value, empty) |
| 191 | } |
| 192 | for _, wb := range bs.state.Store { |
| 193 | if len(wb.Value.CertificateChains) == 0 { |
| 194 | return true |
| 195 | } |
| 196 | } |
| 197 | return false |
| 198 | } |
| 199 | |
Ankur | 5748301 | 2014-11-19 13:17:37 -0800 | [diff] [blame] | 200 | func (bs *blessingStore) verifyState() error { |
| 201 | verifyBlessings := func(wb *blessings, key security.PublicKey) error { |
| 202 | if err := wb.Verify(); err != nil { |
| 203 | return err |
| 204 | } |
| 205 | if b := wb.Blessings(); b != nil && !reflect.DeepEqual(b.PublicKey(), key) { |
| 206 | return fmt.Errorf("read Blessings: %v that are not for provided PublicKey: %v", b, key) |
| 207 | } |
| 208 | return nil |
| 209 | } |
| 210 | for _, wb := range bs.state.Store { |
| 211 | if err := verifyBlessings(wb, bs.publicKey); err != nil { |
| 212 | return err |
| 213 | } |
| 214 | } |
| 215 | if bs.state.Default != nil { |
| 216 | if err := verifyBlessings(bs.state.Default, bs.publicKey); err != nil { |
| 217 | return err |
| 218 | } |
| 219 | } |
| 220 | return nil |
| 221 | } |
| 222 | |
Ankur | 27c56fd | 2014-11-17 19:30:34 -0800 | [diff] [blame] | 223 | // TODO(ataly, ashankar): Get rid of this method once we have switched all |
| 224 | // credentials directories to the new serialization format. |
| 225 | func (bs *blessingStore) deserializeOld() error { |
| 226 | data, signature, err := bs.serializer.Readers() |
| 227 | if err != nil { |
| 228 | return err |
| 229 | } |
| 230 | if data == nil && signature == nil { |
| 231 | return nil |
| 232 | } |
| 233 | var old oldState |
| 234 | if err := decodeFromStorage(&old, data, signature, bs.signer.PublicKey()); err != nil { |
| 235 | return err |
| 236 | } |
| 237 | for p, wire := range old.Store { |
| 238 | bs.state.Store[p] = &blessings{Value: wire} |
| 239 | } |
| 240 | bs.state.Default = &blessings{Value: old.Default} |
Ankur | 5748301 | 2014-11-19 13:17:37 -0800 | [diff] [blame] | 241 | |
| 242 | if err := bs.verifyState(); err != nil { |
| 243 | return err |
| 244 | } |
| 245 | // Save the blessingstore in the new serialization format. This will ensure |
| 246 | // that all credentials directories in the old format will switch to the new |
| 247 | // format. |
| 248 | if err := bs.save(); err != nil { |
| 249 | return err |
| 250 | } |
Ankur | 27c56fd | 2014-11-17 19:30:34 -0800 | [diff] [blame] | 251 | return nil |
| 252 | } |
| 253 | |
| 254 | func (bs *blessingStore) deserialize() error { |
| 255 | data, signature, err := bs.serializer.Readers() |
| 256 | if err != nil { |
| 257 | return err |
| 258 | } |
| 259 | if data == nil && signature == nil { |
| 260 | return nil |
| 261 | } |
| 262 | if err := decodeFromStorage(&bs.state, data, signature, bs.signer.PublicKey()); err == nil && !bs.tryOldFormat() { |
Ankur | 5748301 | 2014-11-19 13:17:37 -0800 | [diff] [blame] | 263 | return bs.verifyState() |
Ankur | 27c56fd | 2014-11-17 19:30:34 -0800 | [diff] [blame] | 264 | } |
| 265 | if err := bs.deserializeOld(); err != nil { |
| 266 | return err |
| 267 | } |
| 268 | return nil |
| 269 | } |
| 270 | |
Ankur | 7c89059 | 2014-10-02 11:36:28 -0700 | [diff] [blame] | 271 | // newPersistingBlessingStore returns a security.BlessingStore for a principal |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 272 | // that is initialized with the persisted data. The returned security.BlessingStore |
| 273 | // also persists any updates to its state. |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 274 | func newPersistingBlessingStore(serializer SerializerReaderWriter, signer serialization.Signer) (security.BlessingStore, error) { |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 275 | if serializer == nil || signer == nil { |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 276 | return nil, errors.New("persisted data or signer is not specified") |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 277 | } |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 278 | bs := &blessingStore{ |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 279 | publicKey: signer.PublicKey(), |
| 280 | state: state{Store: make(map[security.BlessingPattern]*blessings)}, |
| 281 | serializer: serializer, |
| 282 | signer: signer, |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 283 | } |
Ankur | 27c56fd | 2014-11-17 19:30:34 -0800 | [diff] [blame] | 284 | if err := bs.deserialize(); err != nil { |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 285 | return nil, err |
| 286 | } |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 287 | return bs, nil |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 288 | } |