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