blob: 0b09ee82cb671625a1a05c6d787b375b285f1512 [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Asim Shankarae8d4c52014-10-08 13:03:31 -07005package security
Ankur100eb272014-09-15 16:48:12 -07006
Asim Shankarb07ec692015-02-27 23:40:44 -08007// TODO(ashankar,ataly): This file is a bit of a mess!! Define a serialization
8// format for the blessing store and rewrite this file before release!
9
Ankur100eb272014-09-15 16:48:12 -070010import (
Ankur1615a7d2014-10-09 11:58:02 -070011 "bytes"
Suharsh Sivakumard7d4e222015-06-22 11:10:44 -070012 "crypto/sha256"
Ankur100eb272014-09-15 16:48:12 -070013 "fmt"
14 "reflect"
Cosmos Nicolaou3bdf6372014-12-10 09:53:52 -080015 "sort"
Suharsh Sivakumard7d4e222015-06-22 11:10:44 -070016 "strings"
Ankur100eb272014-09-15 16:48:12 -070017 "sync"
Suharsh Sivakumard7d4e222015-06-22 11:10:44 -070018 "time"
Ankur100eb272014-09-15 16:48:12 -070019
Jiri Simsa6ac95222015-02-23 16:11:49 -080020 "v.io/v23/security"
Mike Burrows7f7088d2015-03-25 13:05:00 -070021 "v.io/v23/verror"
Jiri Simsa337af232015-02-27 14:36:46 -080022 "v.io/x/lib/vlog"
Todd Wangb3511492015-04-07 23:32:34 -070023 "v.io/x/ref/lib/security/serialization"
Ankur100eb272014-09-15 16:48:12 -070024)
25
Mike Burrows7f7088d2015-03-25 13:05:00 -070026var (
27 errStoreAddMismatch = verror.Register(pkgPath+".errStoreAddMismatch", verror.NoRetry, "{1:}{2:} blessing's public key does not match store's public key{:_}")
28 errBadBlessingPattern = verror.Register(pkgPath+".errBadBlessingPattern", verror.NoRetry, "{1:}{2:} {3} is an invalid BlessingPattern{:_}")
29 errBlessingsNotForKey = verror.Register(pkgPath+".errBlessingsNotForKey", verror.NoRetry, "{1:}{2:} read Blessings: {3} that are not for provided PublicKey{:_}")
30 errDataOrSignerUnspecified = verror.Register(pkgPath+".errDataOrSignerUnspecified", verror.NoRetry, "{1:}{2:} persisted data or signer is not specified{:_}")
31)
Ankur100eb272014-09-15 16:48:12 -070032
Suharsh Sivakumard7d4e222015-06-22 11:10:44 -070033const cacheKeyFormat = uint32(1)
34
Ankur100eb272014-09-15 16:48:12 -070035// blessingStore implements security.BlessingStore.
36type blessingStore struct {
Ankuree0aa812014-11-14 10:56:52 -080037 publicKey security.PublicKey
38 serializer SerializerReaderWriter
39 signer serialization.Signer
40 mu sync.RWMutex
Ankurcc043852015-04-14 13:10:28 -070041 state blessingStoreState // GUARDED_BY(mu)
Ankur100eb272014-09-15 16:48:12 -070042}
43
gauthamt1e313bc2014-11-10 15:45:56 -080044func (bs *blessingStore) Set(blessings security.Blessings, forPeers security.BlessingPattern) (security.Blessings, error) {
Ankur9e8500a2014-09-17 12:05:32 -070045 if !forPeers.IsValid() {
Mike Burrows7f7088d2015-03-25 13:05:00 -070046 return security.Blessings{}, verror.New(errBadBlessingPattern, nil, forPeers)
Ankur100eb272014-09-15 16:48:12 -070047 }
Asim Shankar2bf7b1e2015-02-27 00:45:12 -080048 if !blessings.IsZero() && !reflect.DeepEqual(blessings.PublicKey(), bs.publicKey) {
Mike Burrows7f7088d2015-03-25 13:05:00 -070049 return security.Blessings{}, verror.New(errStoreAddMismatch, nil)
Asim Shankar48bf0e62014-10-03 16:27:05 -070050 }
gauthamt1e313bc2014-11-10 15:45:56 -080051 bs.mu.Lock()
52 defer bs.mu.Unlock()
Ankurcc043852015-04-14 13:10:28 -070053 old, hadold := bs.state.PeerBlessings[forPeers]
Asim Shankar2bf7b1e2015-02-27 00:45:12 -080054 if !blessings.IsZero() {
Ankurcc043852015-04-14 13:10:28 -070055 bs.state.PeerBlessings[forPeers] = blessings
Ankur100eb272014-09-15 16:48:12 -070056 } else {
Ankurcc043852015-04-14 13:10:28 -070057 delete(bs.state.PeerBlessings, forPeers)
Ankur100eb272014-09-15 16:48:12 -070058 }
gauthamt1e313bc2014-11-10 15:45:56 -080059 if err := bs.save(); err != nil {
Asim Shankar48bf0e62014-10-03 16:27:05 -070060 if hadold {
Ankurcc043852015-04-14 13:10:28 -070061 bs.state.PeerBlessings[forPeers] = old
Ankur100eb272014-09-15 16:48:12 -070062 } else {
Ankurcc043852015-04-14 13:10:28 -070063 delete(bs.state.PeerBlessings, forPeers)
Ankur100eb272014-09-15 16:48:12 -070064 }
Asim Shankar2bf7b1e2015-02-27 00:45:12 -080065 return security.Blessings{}, err
Ankur100eb272014-09-15 16:48:12 -070066 }
Ankurcc043852015-04-14 13:10:28 -070067 return old, nil
Ankur100eb272014-09-15 16:48:12 -070068}
69
gauthamt1e313bc2014-11-10 15:45:56 -080070func (bs *blessingStore) ForPeer(peerBlessings ...string) security.Blessings {
71 bs.mu.RLock()
72 defer bs.mu.RUnlock()
Ankur100eb272014-09-15 16:48:12 -070073
Asim Shankar48bf0e62014-10-03 16:27:05 -070074 var ret security.Blessings
Ankurcc043852015-04-14 13:10:28 -070075 for pattern, b := range bs.state.PeerBlessings {
Asim Shankar48bf0e62014-10-03 16:27:05 -070076 if pattern.MatchedBy(peerBlessings...) {
Ankuree0aa812014-11-14 10:56:52 -080077 if union, err := security.UnionOfBlessings(ret, b); err != nil {
78 vlog.Errorf("UnionOfBlessings(%v, %v) failed: %v, dropping the latter from BlessingStore.ForPeers(%v)", ret, b, err, peerBlessings)
Asim Shankar48bf0e62014-10-03 16:27:05 -070079 } else {
80 ret = union
Ankur100eb272014-09-15 16:48:12 -070081 }
82 }
83 }
Asim Shankar48bf0e62014-10-03 16:27:05 -070084 return ret
Ankur100eb272014-09-15 16:48:12 -070085}
86
gauthamt1e313bc2014-11-10 15:45:56 -080087func (bs *blessingStore) Default() security.Blessings {
88 bs.mu.RLock()
89 defer bs.mu.RUnlock()
Ankurcc043852015-04-14 13:10:28 -070090 return bs.state.DefaultBlessings
Ankur100eb272014-09-15 16:48:12 -070091}
92
gauthamt1e313bc2014-11-10 15:45:56 -080093func (bs *blessingStore) SetDefault(blessings security.Blessings) error {
94 bs.mu.Lock()
95 defer bs.mu.Unlock()
Asim Shankar2bf7b1e2015-02-27 00:45:12 -080096 if !blessings.IsZero() && !reflect.DeepEqual(blessings.PublicKey(), bs.publicKey) {
Mike Burrows7f7088d2015-03-25 13:05:00 -070097 return verror.New(errStoreAddMismatch, nil)
Ankur100eb272014-09-15 16:48:12 -070098 }
Ankurcc043852015-04-14 13:10:28 -070099 oldDefault := bs.state.DefaultBlessings
100 bs.state.DefaultBlessings = blessings
gauthamt1e313bc2014-11-10 15:45:56 -0800101 if err := bs.save(); err != nil {
Ankurcc043852015-04-14 13:10:28 -0700102 bs.state.DefaultBlessings = oldDefault
103 return err
Ankur100eb272014-09-15 16:48:12 -0700104 }
105 return nil
106}
107
gauthamt1e313bc2014-11-10 15:45:56 -0800108func (bs *blessingStore) PublicKey() security.PublicKey {
109 return bs.publicKey
Ankur100eb272014-09-15 16:48:12 -0700110}
111
gauthamt1e313bc2014-11-10 15:45:56 -0800112func (bs *blessingStore) String() string {
113 return fmt.Sprintf("{state: %v, publicKey: %v}", bs.state, bs.publicKey)
Ankur100eb272014-09-15 16:48:12 -0700114}
115
gauthamtf8263932014-12-16 10:59:09 -0800116func (bs *blessingStore) PeerBlessings() map[security.BlessingPattern]security.Blessings {
117 m := make(map[security.BlessingPattern]security.Blessings)
Ankurcc043852015-04-14 13:10:28 -0700118 for pattern, b := range bs.state.PeerBlessings {
119 m[pattern] = b
gauthamtf8263932014-12-16 10:59:09 -0800120 }
121 return m
122}
123
Suharsh Sivakumard7d4e222015-06-22 11:10:44 -0700124func (bs *blessingStore) CacheDischarge(discharge security.Discharge, caveat security.Caveat, impetus security.DischargeImpetus) {
125 id := discharge.ID()
126 tp := caveat.ThirdPartyDetails()
127 // Only add to the cache if the caveat did not require arguments.
128 if id == "" || tp == nil || tp.Requirements().ReportArguments {
129 return
130 }
131 key := dcacheKey(tp, impetus)
132 bs.mu.Lock()
133 defer bs.mu.Unlock()
134 old, hadold := bs.state.DischargeCache[key]
135 bs.state.DischargeCache[key] = discharge
136 if err := bs.save(); err != nil {
137 if hadold {
138 bs.state.DischargeCache[key] = old
139 } else {
140 delete(bs.state.DischargeCache, key)
141 }
142 }
143 return
144}
145
146func (bs *blessingStore) ClearDischarges(discharges ...security.Discharge) {
147 bs.mu.Lock()
148 bs.clearDischargesLocked(discharges...)
149 bs.mu.Unlock()
150 return
151}
152
153func (bs *blessingStore) clearDischargesLocked(discharges ...security.Discharge) {
154 for _, d := range discharges {
155 for k, cached := range bs.state.DischargeCache {
156 if cached.Equivalent(d) {
157 delete(bs.state.DischargeCache, k)
158 }
159 }
160 }
161}
162
163func (bs *blessingStore) Discharge(caveat security.Caveat, impetus security.DischargeImpetus) (out security.Discharge) {
164 defer bs.mu.Unlock()
165 bs.mu.Lock()
166 tp := caveat.ThirdPartyDetails()
167 if tp == nil || tp.Requirements().ReportArguments {
168 return
169 }
170 key := dcacheKey(tp, impetus)
171 if cached, exists := bs.state.DischargeCache[key]; exists {
172 out = cached
173 // If the discharge has expired, purge it from the cache.
174 if hasDischargeExpired(out) {
175 out = security.Discharge{}
176 bs.clearDischargesLocked(cached)
177 }
178 }
179 return
180}
181
182func hasDischargeExpired(dis security.Discharge) bool {
183 expiry := dis.Expiry()
184 if expiry.IsZero() {
185 return false
186 }
187 return expiry.Before(time.Now())
188}
189
190func dcacheKey(tp security.ThirdPartyCaveat, impetus security.DischargeImpetus) dischargeCacheKey {
191 // If the algorithm for computing dcacheKey changes, cacheKeyFormat must be changed as well.
192 id := tp.ID()
193 r := tp.Requirements()
194 var method, servers string
195 // We currently do not cache on impetus.Arguments because there it seems there is no
196 // general way to generate a key from them.
197 if r.ReportMethod {
198 method = impetus.Method
199 }
200 if r.ReportServer && len(impetus.Server) > 0 {
201 // Sort the server blessing patterns to increase cache usage.
202 var bps []string
203 for _, bp := range impetus.Server {
204 bps = append(bps, string(bp))
205 }
206 sort.Strings(bps)
207 servers = strings.Join(bps, ",")
208 }
209 h := sha256.New()
210 h.Write(hashString(id))
211 h.Write(hashString(method))
212 h.Write(hashString(servers))
213 var key [sha256.Size]byte
214 copy(key[:], h.Sum(nil))
215 return key
216}
217
218func hashString(d string) []byte {
219 h := sha256.Sum256([]byte(d))
220 return h[:]
221}
222
Ankur1615a7d2014-10-09 11:58:02 -0700223// DebugString return a human-readable string encoding of the store
224// in the following format
Suharsh Sivakumar4bbe8ed2015-04-09 14:21:44 -0700225// Default Blessings <blessings>
226// Peer pattern Blessings
227// <pattern> <blessings>
Ankur1615a7d2014-10-09 11:58:02 -0700228// ...
Suharsh Sivakumar4bbe8ed2015-04-09 14:21:44 -0700229// <pattern> <blessings>
gauthamt1e313bc2014-11-10 15:45:56 -0800230func (bs *blessingStore) DebugString() string {
Suharsh Sivakumar4bbe8ed2015-04-09 14:21:44 -0700231 const format = "%-30s %s\n"
Ankurcc043852015-04-14 13:10:28 -0700232 buff := bytes.NewBufferString(fmt.Sprintf(format, "Default Blessings", bs.state.DefaultBlessings))
Ankur1615a7d2014-10-09 11:58:02 -0700233
Ankurcc043852015-04-14 13:10:28 -0700234 buff.WriteString(fmt.Sprintf(format, "Peer pattern", "Blessings"))
Cosmos Nicolaou3bdf6372014-12-10 09:53:52 -0800235
Ankurcc043852015-04-14 13:10:28 -0700236 sorted := make([]string, 0, len(bs.state.PeerBlessings))
237 for k, _ := range bs.state.PeerBlessings {
Cosmos Nicolaou3bdf6372014-12-10 09:53:52 -0800238 sorted = append(sorted, string(k))
239 }
240 sort.Strings(sorted)
241 for _, pattern := range sorted {
Ankurcc043852015-04-14 13:10:28 -0700242 buff.WriteString(fmt.Sprintf(format, pattern, bs.state.PeerBlessings[security.BlessingPattern(pattern)]))
Ankur1615a7d2014-10-09 11:58:02 -0700243 }
Ankurcc043852015-04-14 13:10:28 -0700244 return buff.String()
Ankur1615a7d2014-10-09 11:58:02 -0700245}
246
gauthamt1e313bc2014-11-10 15:45:56 -0800247func (bs *blessingStore) save() error {
Ankuree0aa812014-11-14 10:56:52 -0800248 if (bs.signer == nil) && (bs.serializer == nil) {
Ankur100eb272014-09-15 16:48:12 -0700249 return nil
250 }
Ankuree0aa812014-11-14 10:56:52 -0800251 data, signature, err := bs.serializer.Writers()
gauthamt1e313bc2014-11-10 15:45:56 -0800252 if err != nil {
253 return err
254 }
255 return encodeAndStore(bs.state, data, signature, bs.signer)
Ankur100eb272014-09-15 16:48:12 -0700256}
257
Ankur7c890592014-10-02 11:36:28 -0700258// newInMemoryBlessingStore returns an in-memory security.BlessingStore for a
Ankur100eb272014-09-15 16:48:12 -0700259// principal with the provided PublicKey.
260//
261// The returned BlessingStore is initialized with an empty set of blessings.
Ankur7c890592014-10-02 11:36:28 -0700262func newInMemoryBlessingStore(publicKey security.PublicKey) security.BlessingStore {
Ankur100eb272014-09-15 16:48:12 -0700263 return &blessingStore{
264 publicKey: publicKey,
Suharsh Sivakumard7d4e222015-06-22 11:10:44 -0700265 state: blessingStoreState{
266 PeerBlessings: make(map[security.BlessingPattern]security.Blessings),
267 DischargeCache: make(map[dischargeCacheKey]security.Discharge),
268 },
Ankur100eb272014-09-15 16:48:12 -0700269 }
270}
271
Ankur57483012014-11-19 13:17:37 -0800272func (bs *blessingStore) verifyState() error {
Ankurcc043852015-04-14 13:10:28 -0700273 for _, b := range bs.state.PeerBlessings {
274 if !reflect.DeepEqual(b.PublicKey(), bs.publicKey) {
275 return verror.New(errBlessingsNotForKey, nil, b, bs.publicKey)
Ankur57483012014-11-19 13:17:37 -0800276 }
277 }
Ankurcc043852015-04-14 13:10:28 -0700278 if !bs.state.DefaultBlessings.IsZero() && !reflect.DeepEqual(bs.state.DefaultBlessings.PublicKey(), bs.publicKey) {
279 return verror.New(errBlessingsNotForKey, nil, bs.state.DefaultBlessings, bs.publicKey)
Ankur57483012014-11-19 13:17:37 -0800280 }
281 return nil
282}
283
Ankur27c56fd2014-11-17 19:30:34 -0800284func (bs *blessingStore) deserialize() error {
285 data, signature, err := bs.serializer.Readers()
286 if err != nil {
287 return err
288 }
289 if data == nil && signature == nil {
290 return nil
291 }
Ankurcc043852015-04-14 13:10:28 -0700292 if err := decodeFromStorage(&bs.state, data, signature, bs.signer.PublicKey()); err != nil {
Suharsh Sivakumar911c81a2015-05-18 16:58:28 -0700293 return err
Ankur27c56fd2014-11-17 19:30:34 -0800294 }
Suharsh Sivakumard7d4e222015-06-22 11:10:44 -0700295 if bs.state.CacheKeyFormat != cacheKeyFormat {
296 bs.state.CacheKeyFormat = cacheKeyFormat
297 bs.state.DischargeCache = make(map[dischargeCacheKey]security.Discharge)
298 }
Ankurcc043852015-04-14 13:10:28 -0700299 return bs.verifyState()
Ankur27c56fd2014-11-17 19:30:34 -0800300}
301
Ankur7c890592014-10-02 11:36:28 -0700302// newPersistingBlessingStore returns a security.BlessingStore for a principal
gauthamt1e313bc2014-11-10 15:45:56 -0800303// that is initialized with the persisted data. The returned security.BlessingStore
304// also persists any updates to its state.
Ankuree0aa812014-11-14 10:56:52 -0800305func newPersistingBlessingStore(serializer SerializerReaderWriter, signer serialization.Signer) (security.BlessingStore, error) {
Ankuree0aa812014-11-14 10:56:52 -0800306 if serializer == nil || signer == nil {
Mike Burrows7f7088d2015-03-25 13:05:00 -0700307 return nil, verror.New(errDataOrSignerUnspecified, nil)
Ankur100eb272014-09-15 16:48:12 -0700308 }
gauthamt1e313bc2014-11-10 15:45:56 -0800309 bs := &blessingStore{
Ankuree0aa812014-11-14 10:56:52 -0800310 publicKey: signer.PublicKey(),
Ankuree0aa812014-11-14 10:56:52 -0800311 serializer: serializer,
312 signer: signer,
Ankur100eb272014-09-15 16:48:12 -0700313 }
Ankur27c56fd2014-11-17 19:30:34 -0800314 if err := bs.deserialize(); err != nil {
Ankur100eb272014-09-15 16:48:12 -0700315 return nil, err
316 }
Suharsh Sivakumard7d4e222015-06-22 11:10:44 -0700317 if bs.state.PeerBlessings == nil {
318 bs.state.PeerBlessings = make(map[security.BlessingPattern]security.Blessings)
319 }
320 if bs.state.DischargeCache == nil {
321 bs.state.DischargeCache = make(map[dischargeCacheKey]security.Discharge)
322 }
gauthamt1e313bc2014-11-10 15:45:56 -0800323 return bs, nil
Ankur100eb272014-09-15 16:48:12 -0700324}