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 | 764efb7 | 2014-12-25 20:57:03 -0800 | [diff] [blame] | 11 | "v.io/core/veyron/security/serialization" |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 12 | |
Jiri Simsa | 764efb7 | 2014-12-25 20:57:03 -0800 | [diff] [blame] | 13 | "v.io/core/veyron2/security" |
| 14 | "v.io/core/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() |
Asim Shankar | b378e66 | 2015-01-16 10:50:48 -0800 | [diff] [blame] | 119 | if blessings != nil && !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 | |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 138 | func (bs *blessingStore) PeerBlessings() map[security.BlessingPattern]security.Blessings { |
| 139 | m := make(map[security.BlessingPattern]security.Blessings) |
| 140 | for pattern, wb := range bs.state.Store { |
| 141 | m[pattern] = wb.Blessings() |
| 142 | } |
| 143 | return m |
| 144 | } |
| 145 | |
Ankur | 1615a7d | 2014-10-09 11:58:02 -0700 | [diff] [blame] | 146 | // DebugString return a human-readable string encoding of the store |
| 147 | // in the following format |
| 148 | // Default blessing : <Default blessing of the store> |
| 149 | // |
| 150 | // Peer pattern : Blessings |
| 151 | // <pattern> : <blessings> |
| 152 | // ... |
| 153 | // <pattern> : <blessings> |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 154 | func (bs *blessingStore) DebugString() string { |
Ankur | 1615a7d | 2014-10-09 11:58:02 -0700 | [diff] [blame] | 155 | const format = "%-30s : %s\n" |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 156 | b := bytes.NewBufferString(fmt.Sprintf("Default blessings: %v\n", bs.state.Default.Blessings())) |
Ankur | 1615a7d | 2014-10-09 11:58:02 -0700 | [diff] [blame] | 157 | |
| 158 | b.WriteString(fmt.Sprintf(format, "Peer pattern", "Blessings")) |
Cosmos Nicolaou | 3bdf637 | 2014-12-10 09:53:52 -0800 | [diff] [blame] | 159 | |
| 160 | sorted := make([]string, 0, len(bs.state.Store)) |
| 161 | for k, _ := range bs.state.Store { |
| 162 | sorted = append(sorted, string(k)) |
| 163 | } |
| 164 | sort.Strings(sorted) |
| 165 | for _, pattern := range sorted { |
| 166 | wb := bs.state.Store[security.BlessingPattern(pattern)] |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 167 | b.WriteString(fmt.Sprintf(format, pattern, wb.Blessings())) |
Ankur | 1615a7d | 2014-10-09 11:58:02 -0700 | [diff] [blame] | 168 | } |
| 169 | return b.String() |
| 170 | } |
| 171 | |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 172 | func (bs *blessingStore) save() error { |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 173 | if (bs.signer == nil) && (bs.serializer == nil) { |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 174 | return nil |
| 175 | } |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 176 | data, signature, err := bs.serializer.Writers() |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 177 | if err != nil { |
| 178 | return err |
| 179 | } |
| 180 | return encodeAndStore(bs.state, data, signature, bs.signer) |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 181 | } |
| 182 | |
Ankur | 7c89059 | 2014-10-02 11:36:28 -0700 | [diff] [blame] | 183 | // newInMemoryBlessingStore returns an in-memory security.BlessingStore for a |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 184 | // principal with the provided PublicKey. |
| 185 | // |
| 186 | // The returned BlessingStore is initialized with an empty set of blessings. |
Ankur | 7c89059 | 2014-10-02 11:36:28 -0700 | [diff] [blame] | 187 | func newInMemoryBlessingStore(publicKey security.PublicKey) security.BlessingStore { |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 188 | return &blessingStore{ |
| 189 | publicKey: publicKey, |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 190 | state: state{Store: make(map[security.BlessingPattern]*blessings)}, |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 191 | } |
| 192 | } |
| 193 | |
Ankur | 27c56fd | 2014-11-17 19:30:34 -0800 | [diff] [blame] | 194 | // TODO(ataly, ashankar): Get rid of this struct once we have switched all credentials |
| 195 | // directories to the new serialization format. |
| 196 | type oldState struct { |
| 197 | Store map[security.BlessingPattern]security.WireBlessings |
| 198 | Default security.WireBlessings |
| 199 | } |
| 200 | |
| 201 | // TODO(ataly, ashankar): Get rid of this method once we have switched all |
| 202 | // credentials directories to the new serialization format. |
| 203 | func (bs *blessingStore) tryOldFormat() bool { |
| 204 | var empty security.WireBlessings |
| 205 | if len(bs.state.Store) == 0 { |
| 206 | return bs.state.Default == nil || reflect.DeepEqual(bs.state.Default.Value, empty) |
| 207 | } |
| 208 | for _, wb := range bs.state.Store { |
| 209 | if len(wb.Value.CertificateChains) == 0 { |
| 210 | return true |
| 211 | } |
| 212 | } |
| 213 | return false |
| 214 | } |
| 215 | |
Ankur | 5748301 | 2014-11-19 13:17:37 -0800 | [diff] [blame] | 216 | func (bs *blessingStore) verifyState() error { |
| 217 | verifyBlessings := func(wb *blessings, key security.PublicKey) error { |
| 218 | if err := wb.Verify(); err != nil { |
| 219 | return err |
| 220 | } |
| 221 | if b := wb.Blessings(); b != nil && !reflect.DeepEqual(b.PublicKey(), key) { |
| 222 | return fmt.Errorf("read Blessings: %v that are not for provided PublicKey: %v", b, key) |
| 223 | } |
| 224 | return nil |
| 225 | } |
| 226 | for _, wb := range bs.state.Store { |
| 227 | if err := verifyBlessings(wb, bs.publicKey); err != nil { |
| 228 | return err |
| 229 | } |
| 230 | } |
| 231 | if bs.state.Default != nil { |
| 232 | if err := verifyBlessings(bs.state.Default, bs.publicKey); err != nil { |
| 233 | return err |
| 234 | } |
| 235 | } |
| 236 | return nil |
| 237 | } |
| 238 | |
Ankur | 27c56fd | 2014-11-17 19:30:34 -0800 | [diff] [blame] | 239 | // TODO(ataly, ashankar): Get rid of this method once we have switched all |
| 240 | // credentials directories to the new serialization format. |
| 241 | func (bs *blessingStore) deserializeOld() error { |
| 242 | data, signature, err := bs.serializer.Readers() |
| 243 | if err != nil { |
| 244 | return err |
| 245 | } |
| 246 | if data == nil && signature == nil { |
| 247 | return nil |
| 248 | } |
| 249 | var old oldState |
| 250 | if err := decodeFromStorage(&old, data, signature, bs.signer.PublicKey()); err != nil { |
| 251 | return err |
| 252 | } |
| 253 | for p, wire := range old.Store { |
| 254 | bs.state.Store[p] = &blessings{Value: wire} |
| 255 | } |
| 256 | bs.state.Default = &blessings{Value: old.Default} |
Ankur | 5748301 | 2014-11-19 13:17:37 -0800 | [diff] [blame] | 257 | |
| 258 | if err := bs.verifyState(); err != nil { |
| 259 | return err |
| 260 | } |
| 261 | // Save the blessingstore in the new serialization format. This will ensure |
| 262 | // that all credentials directories in the old format will switch to the new |
| 263 | // format. |
| 264 | if err := bs.save(); err != nil { |
| 265 | return err |
| 266 | } |
Ankur | 27c56fd | 2014-11-17 19:30:34 -0800 | [diff] [blame] | 267 | return nil |
| 268 | } |
| 269 | |
| 270 | func (bs *blessingStore) deserialize() error { |
| 271 | data, signature, err := bs.serializer.Readers() |
| 272 | if err != nil { |
| 273 | return err |
| 274 | } |
| 275 | if data == nil && signature == nil { |
| 276 | return nil |
| 277 | } |
| 278 | 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] | 279 | return bs.verifyState() |
Ankur | 27c56fd | 2014-11-17 19:30:34 -0800 | [diff] [blame] | 280 | } |
| 281 | if err := bs.deserializeOld(); err != nil { |
| 282 | return err |
| 283 | } |
| 284 | return nil |
| 285 | } |
| 286 | |
Ankur | 7c89059 | 2014-10-02 11:36:28 -0700 | [diff] [blame] | 287 | // newPersistingBlessingStore returns a security.BlessingStore for a principal |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 288 | // that is initialized with the persisted data. The returned security.BlessingStore |
| 289 | // also persists any updates to its state. |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 290 | func newPersistingBlessingStore(serializer SerializerReaderWriter, signer serialization.Signer) (security.BlessingStore, error) { |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 291 | if serializer == nil || signer == nil { |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 292 | return nil, errors.New("persisted data or signer is not specified") |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 293 | } |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 294 | bs := &blessingStore{ |
Ankur | ee0aa81 | 2014-11-14 10:56:52 -0800 | [diff] [blame] | 295 | publicKey: signer.PublicKey(), |
| 296 | state: state{Store: make(map[security.BlessingPattern]*blessings)}, |
| 297 | serializer: serializer, |
| 298 | signer: signer, |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 299 | } |
Ankur | 27c56fd | 2014-11-17 19:30:34 -0800 | [diff] [blame] | 300 | if err := bs.deserialize(); err != nil { |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 301 | return nil, err |
| 302 | } |
gauthamt | 1e313bc | 2014-11-10 15:45:56 -0800 | [diff] [blame] | 303 | return bs, nil |
Ankur | 100eb27 | 2014-09-15 16:48:12 -0700 | [diff] [blame] | 304 | } |