blob: ac71eee772256160a528290ab998e2679f1d1d8d [file] [log] [blame]
Suharsh Sivakumar720b7042014-12-22 17:33:23 -08001package ipc
2
3import (
Jungho Ahn44d8daf2015-01-16 10:39:15 -08004 "crypto/sha256"
Suharsh Sivakumar720b7042014-12-22 17:33:23 -08005 "fmt"
6 "reflect"
7 "sync"
8
Suharsh Sivakumaraf862a52015-02-04 13:50:47 -08009 "v.io/core/veyron/runtimes/google/ipc/stream"
Jiri Simsa6ac95222015-02-23 16:11:49 -080010 "v.io/v23/ipc"
11 "v.io/v23/security"
Suharsh Sivakumar720b7042014-12-22 17:33:23 -080012)
13
14// clientEncodeBlessings gets or inserts the blessings into the cache.
15func clientEncodeBlessings(cache stream.VCDataCache, blessings security.Blessings) ipc.BlessingsRequest {
Jungho Ahn44d8daf2015-01-16 10:39:15 -080016 blessingsCacheAny := cache.GetOrInsert(clientBlessingsCacheKey{}, newClientBlessingsCache)
Suharsh Sivakumar720b7042014-12-22 17:33:23 -080017 blessingsCache := blessingsCacheAny.(*clientBlessingsCache)
18 return blessingsCache.getOrInsert(blessings)
19}
20
21// clientAckBlessings verifies that the server has updated its cache to include blessings.
22// This means that subsequent rpcs from the client with blessings can send only a cache key.
23func clientAckBlessings(cache stream.VCDataCache, blessings security.Blessings) {
Jungho Ahn44d8daf2015-01-16 10:39:15 -080024 blessingsCacheAny := cache.GetOrInsert(clientBlessingsCacheKey{}, newClientBlessingsCache)
Suharsh Sivakumar720b7042014-12-22 17:33:23 -080025 blessingsCache := blessingsCacheAny.(*clientBlessingsCache)
26 blessingsCache.acknowledge(blessings)
27}
28
29// serverDecodeBlessings insert the key and blessings into the cache or get the blessings if only
30// key is provided in req.
31func serverDecodeBlessings(cache stream.VCDataCache, req ipc.BlessingsRequest, stats *ipcStats) (security.Blessings, error) {
Jungho Ahn44d8daf2015-01-16 10:39:15 -080032 blessingsCacheAny := cache.GetOrInsert(serverBlessingsCacheKey{}, newServerBlessingsCache)
Suharsh Sivakumar720b7042014-12-22 17:33:23 -080033 blessingsCache := blessingsCacheAny.(*serverBlessingsCache)
34 return blessingsCache.getOrInsert(req, stats)
35}
36
37// IMPLEMENTATION DETAILS BELOW
38
Jungho Ahn44d8daf2015-01-16 10:39:15 -080039// clientBlessingsCache is a thread-safe map from blessings to cache id.
Suharsh Sivakumar720b7042014-12-22 17:33:23 -080040type clientBlessingsCache struct {
41 sync.RWMutex
Jungho Ahn44d8daf2015-01-16 10:39:15 -080042 m map[[sha256.Size]byte]clientCacheValue
43 curId uint64
Suharsh Sivakumar720b7042014-12-22 17:33:23 -080044}
45
46type clientCacheValue struct {
Jungho Ahn44d8daf2015-01-16 10:39:15 -080047 id uint64
48 // ack is set to true once the server has confirmed receipt of the cache id.
49 // Clients that insert into the cache when ack is false must send both the id
Suharsh Sivakumar720b7042014-12-22 17:33:23 -080050 // and the blessings.
51 ack bool
52}
53
Jungho Ahn44d8daf2015-01-16 10:39:15 -080054// clientBlessingsCacheKey is the key used to retrieve the clientBlessingsCache from the VCDataCache.
55type clientBlessingsCacheKey struct{}
Suharsh Sivakumar720b7042014-12-22 17:33:23 -080056
57func newClientBlessingsCache() interface{} {
Jungho Ahn44d8daf2015-01-16 10:39:15 -080058 return &clientBlessingsCache{m: make(map[[sha256.Size]byte]clientCacheValue)}
59}
60
61func getBlessingsHashKey(blessings security.Blessings) (key [sha256.Size]byte) {
62 h := sha256.New()
63 for _, chain := range security.MarshalBlessings(blessings).CertificateChains {
64 if len(chain) == 0 {
65 continue
66 }
67 cert := chain[len(chain)-1]
68 s := sha256.Sum256(cert.Signature.R)
69 h.Write(s[:])
70 s = sha256.Sum256(cert.Signature.S)
71 h.Write(s[:])
72 }
73 copy(key[:], h.Sum(nil))
74 return
Suharsh Sivakumar720b7042014-12-22 17:33:23 -080075}
76
77func (c *clientBlessingsCache) getOrInsert(blessings security.Blessings) ipc.BlessingsRequest {
Jungho Ahn44d8daf2015-01-16 10:39:15 -080078 key := getBlessingsHashKey(blessings)
Suharsh Sivakumar720b7042014-12-22 17:33:23 -080079 c.RLock()
Jungho Ahn44d8daf2015-01-16 10:39:15 -080080 val, exists := c.m[key]
Suharsh Sivakumar720b7042014-12-22 17:33:23 -080081 c.RUnlock()
82 if exists {
83 return c.makeBlessingsRequest(val, blessings)
84 }
Jungho Ahn44d8daf2015-01-16 10:39:15 -080085 // If the val doesn't exist we must create a new id, update the cache, and send the id and blessings.
Suharsh Sivakumar720b7042014-12-22 17:33:23 -080086 c.Lock()
Jungho Ahn44d8daf2015-01-16 10:39:15 -080087 // We must check that the val wasn't inserted in the time we changed locks.
88 val, exists = c.m[key]
89 if !exists {
90 val = clientCacheValue{id: c.nextIdLocked()}
91 c.m[key] = val
Suharsh Sivakumar720b7042014-12-22 17:33:23 -080092 }
Jungho Ahn44d8daf2015-01-16 10:39:15 -080093 c.Unlock()
94 return c.makeBlessingsRequest(val, blessings)
Suharsh Sivakumar720b7042014-12-22 17:33:23 -080095}
96
97func (c *clientBlessingsCache) acknowledge(blessings security.Blessings) {
Jungho Ahn44d8daf2015-01-16 10:39:15 -080098 key := getBlessingsHashKey(blessings)
Suharsh Sivakumar720b7042014-12-22 17:33:23 -080099 c.Lock()
Jungho Ahn44d8daf2015-01-16 10:39:15 -0800100 val := c.m[key]
Suharsh Sivakumar720b7042014-12-22 17:33:23 -0800101 val.ack = true
Jungho Ahn44d8daf2015-01-16 10:39:15 -0800102 c.m[key] = val
Suharsh Sivakumar720b7042014-12-22 17:33:23 -0800103 c.Unlock()
104}
105
106func (c *clientBlessingsCache) makeBlessingsRequest(val clientCacheValue, blessings security.Blessings) ipc.BlessingsRequest {
107 if val.ack {
108 // when the value is acknowledged, only send the key, since the server has confirmed that it knows the key.
Jungho Ahn44d8daf2015-01-16 10:39:15 -0800109 return ipc.BlessingsRequest{Key: val.id}
Suharsh Sivakumar720b7042014-12-22 17:33:23 -0800110 }
111 // otherwise we still need to send both key and blessings, but we must ensure that we send the same key.
112 wireBlessings := security.MarshalBlessings(blessings)
Jungho Ahn44d8daf2015-01-16 10:39:15 -0800113 return ipc.BlessingsRequest{val.id, &wireBlessings}
Suharsh Sivakumar720b7042014-12-22 17:33:23 -0800114}
115
Jungho Ahn44d8daf2015-01-16 10:39:15 -0800116// nextIdLocked creates a new id for inserting blessings. It must be called after acquiring a writer lock.
117func (c *clientBlessingsCache) nextIdLocked() uint64 {
118 c.curId++
119 return c.curId
Suharsh Sivakumar720b7042014-12-22 17:33:23 -0800120}
121
122// serverBlessingsCache is a thread-safe map from cache key to blessings.
123type serverBlessingsCache struct {
124 sync.RWMutex
125 m map[uint64]security.Blessings
126}
127
Jungho Ahn44d8daf2015-01-16 10:39:15 -0800128// serverBlessingsCacheKey is the key used to retrieve the serverBlessingsCache from the VCDataCache.
129type serverBlessingsCacheKey struct{}
Suharsh Sivakumar720b7042014-12-22 17:33:23 -0800130
131func newServerBlessingsCache() interface{} {
132 return &serverBlessingsCache{m: make(map[uint64]security.Blessings)}
133}
134
135func (c *serverBlessingsCache) getOrInsert(req ipc.BlessingsRequest, stats *ipcStats) (security.Blessings, error) {
136 // In the case that the key sent is 0, we are running in VCSecurityNone and should
137 // return nil for the client Blessings.
138 if req.Key == 0 {
139 return nil, nil
140 }
141 if req.Blessings == nil {
142 // Fastpath, lookup based on the key.
143 c.RLock()
144 cached, exists := c.m[req.Key]
145 c.RUnlock()
146 if !exists {
147 return nil, fmt.Errorf("ipc: key was not in the cache")
148 }
149 stats.recordBlessingCache(true)
150 return cached, nil
151 }
Suharsh Sivakumardefea2b2015-01-13 09:33:50 -0800152 // Always count the slow path as a cache miss since we do not get the benefit of sending only the cache key.
153 stats.recordBlessingCache(false)
Suharsh Sivakumar720b7042014-12-22 17:33:23 -0800154 // Slowpath, might need to update the cache, or check that the received blessings are
155 // the same as what's in the cache.
156 recv, err := security.NewBlessings(*req.Blessings)
157 if err != nil {
158 return nil, fmt.Errorf("ipc: create new client blessings failed: %v", err)
159 }
160 c.Lock()
161 defer c.Unlock()
162 if cached, exists := c.m[req.Key]; exists {
163 // TODO(suharshs): Replace this reflect.DeepEqual() with a less expensive check.
164 if !reflect.DeepEqual(cached, recv) {
165 return nil, fmt.Errorf("client sent invalid Blessings")
166 }
Suharsh Sivakumar720b7042014-12-22 17:33:23 -0800167 return cached, nil
168 }
169 c.m[req.Key] = recv
Suharsh Sivakumar720b7042014-12-22 17:33:23 -0800170 return recv, nil
171}