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)
23
24type authApplierV3 struct {
25	applierV3
26	as auth.AuthStore
27
28	// mu serializes Apply so that user isn't corrupted and so that
29	// serialized requests don't leak data from TOCTOU errors
30	mu sync.Mutex
31
32	authInfo auth.AuthInfo
33}
34
35func newAuthApplierV3(as auth.AuthStore, base applierV3) *authApplierV3 {
36	return &authApplierV3{applierV3: base, as: as}
37}
38
39func (aa *authApplierV3) Apply(r *pb.InternalRaftRequest) *applyResult {
40	aa.mu.Lock()
41	defer aa.mu.Unlock()
42	if r.Header != nil {
43		// backward-compatible with pre-3.0 releases when internalRaftRequest
44		// does not have header field
45		aa.authInfo.Username = r.Header.Username
46		aa.authInfo.Revision = r.Header.AuthRevision
47	}
48	if needAdminPermission(r) {
49		if err := aa.as.IsAdminPermitted(&aa.authInfo); err != nil {
50			aa.authInfo.Username = ""
51			aa.authInfo.Revision = 0
52			return &applyResult{err: err}
53		}
54	}
55	ret := aa.applierV3.Apply(r)
56	aa.authInfo.Username = ""
57	aa.authInfo.Revision = 0
58	return ret
59}
60
61func (aa *authApplierV3) Put(txnID int64, r *pb.PutRequest) (*pb.PutResponse, error) {
62	if err := aa.as.IsPutPermitted(&aa.authInfo, r.Key); err != nil {
63		return nil, err
64	}
65	if r.PrevKv {
66		err := aa.as.IsRangePermitted(&aa.authInfo, r.Key, nil)
67		if err != nil {
68			return nil, err
69		}
70	}
71	return aa.applierV3.Put(txnID, r)
72}
73
74func (aa *authApplierV3) Range(txnID int64, r *pb.RangeRequest) (*pb.RangeResponse, error) {
75	if err := aa.as.IsRangePermitted(&aa.authInfo, r.Key, r.RangeEnd); err != nil {
76		return nil, err
77	}
78	return aa.applierV3.Range(txnID, r)
79}
80
81func (aa *authApplierV3) DeleteRange(txnID int64, r *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) {
82	if err := aa.as.IsDeleteRangePermitted(&aa.authInfo, r.Key, r.RangeEnd); err != nil {
83		return nil, err
84	}
85	if r.PrevKv {
86		err := aa.as.IsRangePermitted(&aa.authInfo, r.Key, r.RangeEnd)
87		if err != nil {
88			return nil, err
89		}
90	}
91
92	return aa.applierV3.DeleteRange(txnID, r)
93}
94
95func checkTxnReqsPermission(as auth.AuthStore, ai *auth.AuthInfo, reqs []*pb.RequestOp) error {
96	for _, requ := range reqs {
97		switch tv := requ.Request.(type) {
98		case *pb.RequestOp_RequestRange:
99			if tv.RequestRange == nil {
100				continue
101			}
102
103			if err := as.IsRangePermitted(ai, tv.RequestRange.Key, tv.RequestRange.RangeEnd); err != nil {
104				return err
105			}
106
107		case *pb.RequestOp_RequestPut:
108			if tv.RequestPut == nil {
109				continue
110			}
111
112			if err := as.IsPutPermitted(ai, tv.RequestPut.Key); err != nil {
113				return err
114			}
115
116		case *pb.RequestOp_RequestDeleteRange:
117			if tv.RequestDeleteRange == nil {
118				continue
119			}
120
121			if tv.RequestDeleteRange.PrevKv {
122				err := as.IsRangePermitted(ai, tv.RequestDeleteRange.Key, tv.RequestDeleteRange.RangeEnd)
123				if err != nil {
124					return err
125				}
126			}
127
128			err := as.IsDeleteRangePermitted(ai, tv.RequestDeleteRange.Key, tv.RequestDeleteRange.RangeEnd)
129			if err != nil {
130				return err
131			}
132		}
133	}
134
135	return nil
136}
137
138func checkTxnAuth(as auth.AuthStore, ai *auth.AuthInfo, rt *pb.TxnRequest) error {
139	for _, c := range rt.Compare {
140		if err := as.IsRangePermitted(ai, c.Key, nil); err != nil {
141			return err
142		}
143	}
144	if err := checkTxnReqsPermission(as, ai, rt.Success); err != nil {
145		return err
146	}
147	if err := checkTxnReqsPermission(as, ai, rt.Failure); err != nil {
148		return err
149	}
150	return nil
151}
152
153func (aa *authApplierV3) Txn(rt *pb.TxnRequest) (*pb.TxnResponse, error) {
154	if err := checkTxnAuth(aa.as, &aa.authInfo, rt); err != nil {
155		return nil, err
156	}
157	return aa.applierV3.Txn(rt)
158}
159
160func needAdminPermission(r *pb.InternalRaftRequest) bool {
161	switch {
162	case r.AuthEnable != nil:
163		return true
164	case r.AuthDisable != nil:
165		return true
166	case r.AuthUserAdd != nil:
167		return true
168	case r.AuthUserDelete != nil:
169		return true
170	case r.AuthUserChangePassword != nil:
171		return true
172	case r.AuthUserGrantRole != nil:
173		return true
174	case r.AuthUserGet != nil:
175		return true
176	case r.AuthUserRevokeRole != nil:
177		return true
178	case r.AuthRoleAdd != nil:
179		return true
180	case r.AuthRoleGrantPermission != nil:
181		return true
182	case r.AuthRoleGet != nil:
183		return true
184	case r.AuthRoleRevokePermission != nil:
185		return true
186	case r.AuthRoleDelete != nil:
187		return true
188	case r.AuthUserList != nil:
189		return true
190	case r.AuthRoleList != nil:
191		return true
192	default:
193		return false
194	}
195}
196