1// Copyright 2016 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package etcdserver
16
17import (
18	"sync"
19
20	"github.com/coreos/etcd/auth"
21	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
22	"github.com/coreos/etcd/lease"
23	"github.com/coreos/etcd/mvcc"
24)
25
26type authApplierV3 struct {
27	applierV3
28	as     auth.AuthStore
29	lessor lease.Lessor
30
31	// mu serializes Apply so that user isn't corrupted and so that
32	// serialized requests don't leak data from TOCTOU errors
33	mu sync.Mutex
34
35	authInfo auth.AuthInfo
36}
37
38func newAuthApplierV3(as auth.AuthStore, base applierV3, lessor lease.Lessor) *authApplierV3 {
39	return &authApplierV3{applierV3: base, as: as, lessor: lessor}
40}
41
42func (aa *authApplierV3) Apply(r *pb.InternalRaftRequest) *applyResult {
43	aa.mu.Lock()
44	defer aa.mu.Unlock()
45	if r.Header != nil {
46		// backward-compatible with pre-3.0 releases when internalRaftRequest
47		// does not have header field
48		aa.authInfo.Username = r.Header.Username
49		aa.authInfo.Revision = r.Header.AuthRevision
50	}
51	if needAdminPermission(r) {
52		if err := aa.as.IsAdminPermitted(&aa.authInfo); err != nil {
53			aa.authInfo.Username = ""
54			aa.authInfo.Revision = 0
55			return &applyResult{err: err}
56		}
57	}
58	ret := aa.applierV3.Apply(r)
59	aa.authInfo.Username = ""
60	aa.authInfo.Revision = 0
61	return ret
62}
63
64func (aa *authApplierV3) Put(txn mvcc.TxnWrite, r *pb.PutRequest) (*pb.PutResponse, error) {
65	if err := aa.as.IsPutPermitted(&aa.authInfo, r.Key); err != nil {
66		return nil, err
67	}
68
69	if err := aa.checkLeasePuts(lease.LeaseID(r.Lease)); err != nil {
70		// The specified lease is already attached with a key that cannot
71		// be written by this user. It means the user cannot revoke the
72		// lease so attaching the lease to the newly written key should
73		// be forbidden.
74		return nil, err
75	}
76
77	if r.PrevKv {
78		err := aa.as.IsRangePermitted(&aa.authInfo, r.Key, nil)
79		if err != nil {
80			return nil, err
81		}
82	}
83	return aa.applierV3.Put(txn, r)
84}
85
86func (aa *authApplierV3) Range(txn mvcc.TxnRead, r *pb.RangeRequest) (*pb.RangeResponse, error) {
87	if err := aa.as.IsRangePermitted(&aa.authInfo, r.Key, r.RangeEnd); err != nil {
88		return nil, err
89	}
90	return aa.applierV3.Range(txn, r)
91}
92
93func (aa *authApplierV3) DeleteRange(txn mvcc.TxnWrite, r *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) {
94	if err := aa.as.IsDeleteRangePermitted(&aa.authInfo, r.Key, r.RangeEnd); err != nil {
95		return nil, err
96	}
97	if r.PrevKv {
98		err := aa.as.IsRangePermitted(&aa.authInfo, r.Key, r.RangeEnd)
99		if err != nil {
100			return nil, err
101		}
102	}
103
104	return aa.applierV3.DeleteRange(txn, r)
105}
106
107func checkTxnReqsPermission(as auth.AuthStore, ai *auth.AuthInfo, reqs []*pb.RequestOp) error {
108	for _, requ := range reqs {
109		switch tv := requ.Request.(type) {
110		case *pb.RequestOp_RequestRange:
111			if tv.RequestRange == nil {
112				continue
113			}
114
115			if err := as.IsRangePermitted(ai, tv.RequestRange.Key, tv.RequestRange.RangeEnd); err != nil {
116				return err
117			}
118
119		case *pb.RequestOp_RequestPut:
120			if tv.RequestPut == nil {
121				continue
122			}
123
124			if err := as.IsPutPermitted(ai, tv.RequestPut.Key); err != nil {
125				return err
126			}
127
128		case *pb.RequestOp_RequestDeleteRange:
129			if tv.RequestDeleteRange == nil {
130				continue
131			}
132
133			if tv.RequestDeleteRange.PrevKv {
134				err := as.IsRangePermitted(ai, tv.RequestDeleteRange.Key, tv.RequestDeleteRange.RangeEnd)
135				if err != nil {
136					return err
137				}
138			}
139
140			err := as.IsDeleteRangePermitted(ai, tv.RequestDeleteRange.Key, tv.RequestDeleteRange.RangeEnd)
141			if err != nil {
142				return err
143			}
144		}
145	}
146
147	return nil
148}
149
150func checkTxnAuth(as auth.AuthStore, ai *auth.AuthInfo, rt *pb.TxnRequest) error {
151	for _, c := range rt.Compare {
152		if err := as.IsRangePermitted(ai, c.Key, c.RangeEnd); err != nil {
153			return err
154		}
155	}
156	if err := checkTxnReqsPermission(as, ai, rt.Success); err != nil {
157		return err
158	}
159	if err := checkTxnReqsPermission(as, ai, rt.Failure); err != nil {
160		return err
161	}
162	return nil
163}
164
165func (aa *authApplierV3) Txn(rt *pb.TxnRequest) (*pb.TxnResponse, error) {
166	if err := checkTxnAuth(aa.as, &aa.authInfo, rt); err != nil {
167		return nil, err
168	}
169	return aa.applierV3.Txn(rt)
170}
171
172func (aa *authApplierV3) LeaseRevoke(lc *pb.LeaseRevokeRequest) (*pb.LeaseRevokeResponse, error) {
173	if err := aa.checkLeasePuts(lease.LeaseID(lc.ID)); err != nil {
174		return nil, err
175	}
176	return aa.applierV3.LeaseRevoke(lc)
177}
178
179func (aa *authApplierV3) checkLeasePuts(leaseID lease.LeaseID) error {
180	lease := aa.lessor.Lookup(leaseID)
181	if lease != nil {
182		for _, key := range lease.Keys() {
183			if err := aa.as.IsPutPermitted(&aa.authInfo, []byte(key)); err != nil {
184				return err
185			}
186		}
187	}
188
189	return nil
190}
191
192func (aa *authApplierV3) UserGet(r *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error) {
193	err := aa.as.IsAdminPermitted(&aa.authInfo)
194	if err != nil && r.Name != aa.authInfo.Username {
195		aa.authInfo.Username = ""
196		aa.authInfo.Revision = 0
197		return &pb.AuthUserGetResponse{}, err
198	}
199
200	return aa.applierV3.UserGet(r)
201}
202
203func (aa *authApplierV3) RoleGet(r *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse, error) {
204	err := aa.as.IsAdminPermitted(&aa.authInfo)
205	if err != nil && !aa.as.HasRole(aa.authInfo.Username, r.Role) {
206		aa.authInfo.Username = ""
207		aa.authInfo.Revision = 0
208		return &pb.AuthRoleGetResponse{}, err
209	}
210
211	return aa.applierV3.RoleGet(r)
212}
213
214func needAdminPermission(r *pb.InternalRaftRequest) bool {
215	switch {
216	case r.AuthEnable != nil:
217		return true
218	case r.AuthDisable != nil:
219		return true
220	case r.AuthUserAdd != nil:
221		return true
222	case r.AuthUserDelete != nil:
223		return true
224	case r.AuthUserChangePassword != nil:
225		return true
226	case r.AuthUserGrantRole != nil:
227		return true
228	case r.AuthUserRevokeRole != nil:
229		return true
230	case r.AuthRoleAdd != nil:
231		return true
232	case r.AuthRoleGrantPermission != nil:
233		return true
234	case r.AuthRoleRevokePermission != nil:
235		return true
236	case r.AuthRoleDelete != nil:
237		return true
238	case r.AuthUserList != nil:
239		return true
240	case r.AuthRoleList != nil:
241		return true
242	default:
243		return false
244	}
245}
246