1// Copyright 2016 Keybase Inc. All rights reserved.
2// Use of this source code is governed by a BSD
3// license that can be found in the LICENSE file.
4
5package libkbfs
6
7import (
8	"sync"
9
10	"github.com/keybase/client/go/kbfs/kbfscodec"
11	"github.com/keybase/client/go/kbfs/kbfscrypto"
12	"github.com/keybase/client/go/kbfs/kbfsmd"
13	"github.com/keybase/client/go/kbfs/tlf"
14	"github.com/keybase/client/go/protocol/keybase1"
15	"github.com/pkg/errors"
16	"golang.org/x/net/context"
17)
18
19// TODO: Have the functions below wrap their errors.
20
21// Helper to aid in enforcement that only specified public keys can
22// access TLF metadata. mergedMasterHead can be nil, in which case
23// true is returned.
24func isReader(ctx context.Context, teamMemChecker kbfsmd.TeamMembershipChecker,
25	currentUID keybase1.UID, mergedMasterHead kbfsmd.RootMetadata,
26	extra kbfsmd.ExtraMetadata) (bool, error) {
27	h, err := mergedMasterHead.MakeBareTlfHandle(extra)
28	if err != nil {
29		return false, err
30	}
31
32	if h.Type() == tlf.SingleTeam {
33		isReader, err := teamMemChecker.IsTeamReader(
34			ctx, h.Writers[0].AsTeamOrBust(), currentUID,
35			keybase1.OfflineAvailability_NONE)
36		if err != nil {
37			return false, kbfsmd.ServerError{Err: err}
38		}
39		return isReader, nil
40	}
41
42	return h.IsReader(currentUID.AsUserOrTeam()), nil
43}
44
45// Helper to aid in enforcement that only specified public keys can
46// access TLF metadata. mergedMasterHead can be nil, in which case
47// true is returned.
48func isWriterOrValidRekey(ctx context.Context,
49	teamMemChecker kbfsmd.TeamMembershipChecker, codec kbfscodec.Codec,
50	currentUID keybase1.UID, verifyingKey kbfscrypto.VerifyingKey,
51	mergedMasterHead, newMd kbfsmd.RootMetadata, prevExtra, extra kbfsmd.ExtraMetadata) (
52	bool, error) {
53	h, err := mergedMasterHead.MakeBareTlfHandle(prevExtra)
54	if err != nil {
55		return false, err
56	}
57
58	if h.Type() == tlf.SingleTeam {
59		isWriter, err := teamMemChecker.IsTeamWriter(
60			ctx, h.Writers[0].AsTeamOrBust(), currentUID, verifyingKey,
61			keybase1.OfflineAvailability_NONE)
62		if err != nil {
63			return false, kbfsmd.ServerError{Err: err}
64		}
65		// Team TLFs can't be rekeyed, so readers aren't ever valid.
66		return isWriter, nil
67	}
68
69	if h.IsWriter(currentUID.AsUserOrTeam()) {
70		return true, nil
71	}
72
73	if h.IsReader(currentUID.AsUserOrTeam()) {
74		// if this is a reader, are they acting within their
75		// restrictions?
76		return newMd.IsValidRekeyRequest(
77			codec, mergedMasterHead, currentUID, prevExtra, extra)
78	}
79
80	return false, nil
81}
82
83// mdServerLocalTruncateLockManager manages the truncate locks for a
84// set of TLFs. Note that it is not goroutine-safe.
85type mdServerLocalTruncateLockManager struct {
86	// TLF ID -> device crypt public key.
87	locksDb map[tlf.ID]kbfscrypto.CryptPublicKey
88}
89
90func newMDServerLocalTruncatedLockManager() mdServerLocalTruncateLockManager {
91	return mdServerLocalTruncateLockManager{
92		locksDb: make(map[tlf.ID]kbfscrypto.CryptPublicKey),
93	}
94}
95
96func (m mdServerLocalTruncateLockManager) truncateLock(
97	deviceKey kbfscrypto.CryptPublicKey, id tlf.ID) (bool, error) {
98	lockKey, ok := m.locksDb[id]
99	if !ok {
100		m.locksDb[id] = deviceKey
101		return true, nil
102	}
103
104	if lockKey == deviceKey {
105		// idempotent
106		return true, nil
107	}
108
109	// Locked by someone else.
110	return false, kbfsmd.ServerErrorLocked{}
111}
112
113func (m mdServerLocalTruncateLockManager) truncateUnlock(
114	deviceKey kbfscrypto.CryptPublicKey, id tlf.ID) (bool, error) {
115	lockKey, ok := m.locksDb[id]
116	if !ok {
117		// Already unlocked.
118		return true, nil
119	}
120
121	if lockKey == deviceKey {
122		delete(m.locksDb, id)
123		return true, nil
124	}
125
126	// Locked by someone else.
127	return false, kbfsmd.ServerErrorLocked{}
128}
129
130// mdServerLocalUpdateManager manages the observers for a set of TLFs
131// referenced by multiple mdServerLocal instances sharing the same
132// data. It is goroutine-safe.
133type mdServerLocalUpdateManager struct {
134	// Protects observers and sessionHeads.
135	lock         sync.Mutex
136	observers    map[tlf.ID]map[mdServerLocal]chan<- error
137	sessionHeads map[tlf.ID]mdServerLocal
138}
139
140func newMDServerLocalUpdateManager() *mdServerLocalUpdateManager {
141	return &mdServerLocalUpdateManager{
142		observers:    make(map[tlf.ID]map[mdServerLocal]chan<- error),
143		sessionHeads: make(map[tlf.ID]mdServerLocal),
144	}
145}
146
147func (m *mdServerLocalUpdateManager) setHead(id tlf.ID, server mdServerLocal) {
148	m.lock.Lock()
149	defer m.lock.Unlock()
150
151	m.sessionHeads[id] = server
152
153	// now fire all the observers that aren't from this session
154	for k, v := range m.observers[id] {
155		if k != server {
156			v <- nil
157			close(v)
158			delete(m.observers[id], k)
159		}
160	}
161	if len(m.observers[id]) == 0 {
162		delete(m.observers, id)
163	}
164}
165
166func (m *mdServerLocalUpdateManager) registerForUpdate(
167	id tlf.ID, currHead, currMergedHeadRev kbfsmd.Revision,
168	server mdServerLocal) <-chan error {
169	m.lock.Lock()
170	defer m.lock.Unlock()
171
172	c := make(chan error, 1)
173	if currMergedHeadRev > currHead && server != m.sessionHeads[id] {
174		c <- nil
175		close(c)
176		return c
177	}
178
179	if _, ok := m.observers[id]; !ok {
180		m.observers[id] = make(map[mdServerLocal]chan<- error)
181	}
182
183	// Otherwise, this is a legit observer.  This assumes that each
184	// client will be using a unique instance of MDServerLocal.
185	if _, ok := m.observers[id][server]; ok {
186		// If the local node registers something twice, it indicates a
187		// fatal bug.  Note that in the real MDServer implementation,
188		// we should allow this, in order to make the RPC properly
189		// idempotent.
190		panic(errors.Errorf("Attempted double-registration for MDServerLocal %v",
191			server))
192	}
193	m.observers[id][server] = c
194	return c
195}
196
197func (m *mdServerLocalUpdateManager) cancel(id tlf.ID, server mdServerLocal) {
198	m.lock.Lock()
199	defer m.lock.Unlock()
200
201	// Cancel the registration for this server only.
202	for k, v := range m.observers[id] {
203		if k == server {
204			v <- errors.New("Registration canceled")
205			close(v)
206			delete(m.observers[id], k)
207		}
208	}
209	if len(m.observers[id]) == 0 {
210		delete(m.observers, id)
211	}
212}
213
214type keyBundleGetter func(tlf.ID, kbfsmd.TLFWriterKeyBundleID, kbfsmd.TLFReaderKeyBundleID) (
215	*kbfsmd.TLFWriterKeyBundleV3, *kbfsmd.TLFReaderKeyBundleV3, error)
216
217func getExtraMetadata(kbg keyBundleGetter, brmd kbfsmd.RootMetadata) (kbfsmd.ExtraMetadata, error) {
218	tlfID := brmd.TlfID()
219	wkbID := brmd.GetTLFWriterKeyBundleID()
220	rkbID := brmd.GetTLFReaderKeyBundleID()
221	if (wkbID == kbfsmd.TLFWriterKeyBundleID{}) !=
222		(rkbID == kbfsmd.TLFReaderKeyBundleID{}) {
223		return nil, errors.Errorf(
224			"wkbID is empty (%t) != rkbID is empty (%t)",
225			wkbID == kbfsmd.TLFWriterKeyBundleID{},
226			rkbID == kbfsmd.TLFReaderKeyBundleID{})
227	}
228
229	if wkbID == (kbfsmd.TLFWriterKeyBundleID{}) {
230		return nil, nil
231	}
232
233	wkb, rkb, err := kbg(tlfID, wkbID, rkbID)
234	if err != nil {
235		return nil, err
236	}
237
238	return kbfsmd.NewExtraMetadataV3(*wkb, *rkb, false, false), nil
239}
240