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