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 auth
16
17import (
18	"bytes"
19	"context"
20	"encoding/binary"
21	"errors"
22	"sort"
23	"strings"
24	"sync"
25	"sync/atomic"
26	"time"
27
28	"github.com/coreos/etcd/auth/authpb"
29	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
30	"github.com/coreos/etcd/mvcc/backend"
31
32	"github.com/coreos/pkg/capnslog"
33	"golang.org/x/crypto/bcrypt"
34	"google.golang.org/grpc/credentials"
35	"google.golang.org/grpc/metadata"
36	"google.golang.org/grpc/peer"
37)
38
39var (
40	enableFlagKey = []byte("authEnabled")
41	authEnabled   = []byte{1}
42	authDisabled  = []byte{0}
43
44	revisionKey = []byte("authRevision")
45
46	authBucketName      = []byte("auth")
47	authUsersBucketName = []byte("authUsers")
48	authRolesBucketName = []byte("authRoles")
49
50	plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "auth")
51
52	ErrRootUserNotExist     = errors.New("auth: root user does not exist")
53	ErrRootRoleNotExist     = errors.New("auth: root user does not have root role")
54	ErrUserAlreadyExist     = errors.New("auth: user already exists")
55	ErrUserEmpty            = errors.New("auth: user name is empty")
56	ErrUserNotFound         = errors.New("auth: user not found")
57	ErrRoleAlreadyExist     = errors.New("auth: role already exists")
58	ErrRoleNotFound         = errors.New("auth: role not found")
59	ErrAuthFailed           = errors.New("auth: authentication failed, invalid user ID or password")
60	ErrPermissionDenied     = errors.New("auth: permission denied")
61	ErrRoleNotGranted       = errors.New("auth: role is not granted to the user")
62	ErrPermissionNotGranted = errors.New("auth: permission is not granted to the role")
63	ErrAuthNotEnabled       = errors.New("auth: authentication is not enabled")
64	ErrAuthOldRevision      = errors.New("auth: revision in header is old")
65	ErrInvalidAuthToken     = errors.New("auth: invalid auth token")
66	ErrInvalidAuthOpts      = errors.New("auth: invalid auth options")
67	ErrInvalidAuthMgmt      = errors.New("auth: invalid auth management")
68
69	// BcryptCost is the algorithm cost / strength for hashing auth passwords
70	BcryptCost = bcrypt.DefaultCost
71)
72
73const (
74	rootUser = "root"
75	rootRole = "root"
76
77	tokenTypeSimple = "simple"
78	tokenTypeJWT    = "jwt"
79
80	revBytesLen = 8
81)
82
83type AuthInfo struct {
84	Username string
85	Revision uint64
86}
87
88// AuthenticateParamIndex is used for a key of context in the parameters of Authenticate()
89type AuthenticateParamIndex struct{}
90
91// AuthenticateParamSimpleTokenPrefix is used for a key of context in the parameters of Authenticate()
92type AuthenticateParamSimpleTokenPrefix struct{}
93
94// saveConsistentIndexFunc is used to sync consistentIndex to backend, now reusing store.saveIndex
95type saveConsistentIndexFunc func(tx backend.BatchTx)
96
97// AuthStore defines auth storage interface.
98type AuthStore interface {
99	// AuthEnable turns on the authentication feature
100	AuthEnable() error
101
102	// AuthDisable turns off the authentication feature
103	AuthDisable()
104
105	// Authenticate does authentication based on given user name and password
106	Authenticate(ctx context.Context, username, password string) (*pb.AuthenticateResponse, error)
107
108	// Recover recovers the state of auth store from the given backend
109	Recover(b backend.Backend)
110
111	// UserAdd adds a new user
112	UserAdd(r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error)
113
114	// UserDelete deletes a user
115	UserDelete(r *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error)
116
117	// UserChangePassword changes a password of a user
118	UserChangePassword(r *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error)
119
120	// UserGrantRole grants a role to the user
121	UserGrantRole(r *pb.AuthUserGrantRoleRequest) (*pb.AuthUserGrantRoleResponse, error)
122
123	// UserGet gets the detailed information of a users
124	UserGet(r *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error)
125
126	// UserRevokeRole revokes a role of a user
127	UserRevokeRole(r *pb.AuthUserRevokeRoleRequest) (*pb.AuthUserRevokeRoleResponse, error)
128
129	// RoleAdd adds a new role
130	RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error)
131
132	// RoleGrantPermission grants a permission to a role
133	RoleGrantPermission(r *pb.AuthRoleGrantPermissionRequest) (*pb.AuthRoleGrantPermissionResponse, error)
134
135	// RoleGet gets the detailed information of a role
136	RoleGet(r *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse, error)
137
138	// RoleRevokePermission gets the detailed information of a role
139	RoleRevokePermission(r *pb.AuthRoleRevokePermissionRequest) (*pb.AuthRoleRevokePermissionResponse, error)
140
141	// RoleDelete gets the detailed information of a role
142	RoleDelete(r *pb.AuthRoleDeleteRequest) (*pb.AuthRoleDeleteResponse, error)
143
144	// UserList gets a list of all users
145	UserList(r *pb.AuthUserListRequest) (*pb.AuthUserListResponse, error)
146
147	// RoleList gets a list of all roles
148	RoleList(r *pb.AuthRoleListRequest) (*pb.AuthRoleListResponse, error)
149
150	// IsPutPermitted checks put permission of the user
151	IsPutPermitted(authInfo *AuthInfo, key []byte) error
152
153	// IsRangePermitted checks range permission of the user
154	IsRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error
155
156	// IsDeleteRangePermitted checks delete-range permission of the user
157	IsDeleteRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error
158
159	// IsAdminPermitted checks admin permission of the user
160	IsAdminPermitted(authInfo *AuthInfo) error
161
162	// GenTokenPrefix produces a random string in a case of simple token
163	// in a case of JWT, it produces an empty string
164	GenTokenPrefix() (string, error)
165
166	// Revision gets current revision of authStore
167	Revision() uint64
168
169	// CheckPassword checks a given pair of username and password is correct
170	CheckPassword(username, password string) (uint64, error)
171
172	// Close does cleanup of AuthStore
173	Close() error
174
175	// AuthInfoFromCtx gets AuthInfo from gRPC's context
176	AuthInfoFromCtx(ctx context.Context) (*AuthInfo, error)
177
178	// AuthInfoFromTLS gets AuthInfo from TLS info of gRPC's context
179	AuthInfoFromTLS(ctx context.Context) *AuthInfo
180
181	// WithRoot generates and installs a token that can be used as a root credential
182	WithRoot(ctx context.Context) context.Context
183
184	// HasRole checks that user has role
185	HasRole(user, role string) bool
186
187	// SetConsistentIndexSyncer sets consistentIndex syncer
188	SetConsistentIndexSyncer(syncer saveConsistentIndexFunc)
189}
190
191type TokenProvider interface {
192	info(ctx context.Context, token string, revision uint64) (*AuthInfo, bool)
193	assign(ctx context.Context, username string, revision uint64) (string, error)
194	enable()
195	disable()
196
197	invalidateUser(string)
198	genTokenPrefix() (string, error)
199}
200
201type authStore struct {
202	// atomic operations; need 64-bit align, or 32-bit tests will crash
203	revision uint64
204
205	be        backend.Backend
206	enabled   bool
207	enabledMu sync.RWMutex
208
209	rangePermCache map[string]*unifiedRangePermissions // username -> unifiedRangePermissions
210
211	tokenProvider       TokenProvider
212	syncConsistentIndex saveConsistentIndexFunc
213}
214
215func (as *authStore) SetConsistentIndexSyncer(syncer saveConsistentIndexFunc) {
216	as.syncConsistentIndex = syncer
217}
218func (as *authStore) AuthEnable() error {
219	as.enabledMu.Lock()
220	defer as.enabledMu.Unlock()
221	if as.enabled {
222		plog.Noticef("Authentication already enabled")
223		return nil
224	}
225	b := as.be
226	tx := b.BatchTx()
227	tx.Lock()
228	defer func() {
229		tx.Unlock()
230		b.ForceCommit()
231	}()
232
233	u := getUser(tx, rootUser)
234	if u == nil {
235		return ErrRootUserNotExist
236	}
237
238	if !hasRootRole(u) {
239		return ErrRootRoleNotExist
240	}
241
242	tx.UnsafePut(authBucketName, enableFlagKey, authEnabled)
243
244	as.enabled = true
245	as.tokenProvider.enable()
246
247	as.rangePermCache = make(map[string]*unifiedRangePermissions)
248
249	as.setRevision(getRevision(tx))
250
251	plog.Noticef("Authentication enabled")
252
253	return nil
254}
255
256func (as *authStore) AuthDisable() {
257	as.enabledMu.Lock()
258	defer as.enabledMu.Unlock()
259	if !as.enabled {
260		return
261	}
262	b := as.be
263	tx := b.BatchTx()
264	tx.Lock()
265	tx.UnsafePut(authBucketName, enableFlagKey, authDisabled)
266	as.commitRevision(tx)
267	as.saveConsistentIndex(tx)
268	tx.Unlock()
269	b.ForceCommit()
270
271	as.enabled = false
272	as.tokenProvider.disable()
273
274	plog.Noticef("Authentication disabled")
275}
276
277func (as *authStore) Close() error {
278	as.enabledMu.Lock()
279	defer as.enabledMu.Unlock()
280	if !as.enabled {
281		return nil
282	}
283	as.tokenProvider.disable()
284	return nil
285}
286
287func (as *authStore) Authenticate(ctx context.Context, username, password string) (*pb.AuthenticateResponse, error) {
288	if !as.isAuthEnabled() {
289		return nil, ErrAuthNotEnabled
290	}
291
292	tx := as.be.BatchTx()
293	tx.Lock()
294	defer tx.Unlock()
295
296	user := getUser(tx, username)
297	if user == nil {
298		return nil, ErrAuthFailed
299	}
300
301	// Password checking is already performed in the API layer, so we don't need to check for now.
302	// Staleness of password can be detected with OCC in the API layer, too.
303
304	token, err := as.tokenProvider.assign(ctx, username, as.Revision())
305	if err != nil {
306		return nil, err
307	}
308
309	plog.Debugf("authorized %s, token is %s", username, token)
310	return &pb.AuthenticateResponse{Token: token}, nil
311}
312
313func (as *authStore) CheckPassword(username, password string) (uint64, error) {
314	if !as.isAuthEnabled() {
315		return 0, ErrAuthNotEnabled
316	}
317
318	tx := as.be.BatchTx()
319	tx.Lock()
320	defer tx.Unlock()
321
322	user := getUser(tx, username)
323	if user == nil {
324		return 0, ErrAuthFailed
325	}
326
327	if bcrypt.CompareHashAndPassword(user.Password, []byte(password)) != nil {
328		plog.Noticef("authentication failed, invalid password for user %s", username)
329		return 0, ErrAuthFailed
330	}
331
332	return getRevision(tx), nil
333}
334
335func (as *authStore) Recover(be backend.Backend) {
336	enabled := false
337	as.be = be
338	tx := be.BatchTx()
339	tx.Lock()
340	_, vs := tx.UnsafeRange(authBucketName, enableFlagKey, nil, 0)
341	if len(vs) == 1 {
342		if bytes.Equal(vs[0], authEnabled) {
343			enabled = true
344		}
345	}
346
347	as.setRevision(getRevision(tx))
348
349	tx.Unlock()
350
351	as.enabledMu.Lock()
352	as.enabled = enabled
353	as.enabledMu.Unlock()
354}
355
356func (as *authStore) UserAdd(r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error) {
357	if len(r.Name) == 0 {
358		return nil, ErrUserEmpty
359	}
360
361	hashed, err := bcrypt.GenerateFromPassword([]byte(r.Password), BcryptCost)
362	if err != nil {
363		plog.Errorf("failed to hash password: %s", err)
364		return nil, err
365	}
366
367	tx := as.be.BatchTx()
368	tx.Lock()
369	defer tx.Unlock()
370
371	user := getUser(tx, r.Name)
372	if user != nil {
373		return nil, ErrUserAlreadyExist
374	}
375
376	newUser := &authpb.User{
377		Name:     []byte(r.Name),
378		Password: hashed,
379	}
380
381	putUser(tx, newUser)
382
383	as.commitRevision(tx)
384	as.saveConsistentIndex(tx)
385
386	plog.Noticef("added a new user: %s", r.Name)
387
388	return &pb.AuthUserAddResponse{}, nil
389}
390
391func (as *authStore) UserDelete(r *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error) {
392	if as.enabled && strings.Compare(r.Name, rootUser) == 0 {
393		plog.Errorf("the user root must not be deleted")
394		return nil, ErrInvalidAuthMgmt
395	}
396
397	tx := as.be.BatchTx()
398	tx.Lock()
399	defer tx.Unlock()
400
401	user := getUser(tx, r.Name)
402	if user == nil {
403		return nil, ErrUserNotFound
404	}
405
406	delUser(tx, r.Name)
407
408	as.commitRevision(tx)
409	as.saveConsistentIndex(tx)
410
411	as.invalidateCachedPerm(r.Name)
412	as.tokenProvider.invalidateUser(r.Name)
413
414	plog.Noticef("deleted a user: %s", r.Name)
415
416	return &pb.AuthUserDeleteResponse{}, nil
417}
418
419func (as *authStore) UserChangePassword(r *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error) {
420	// TODO(mitake): measure the cost of bcrypt.GenerateFromPassword()
421	// If the cost is too high, we should move the encryption to outside of the raft
422	hashed, err := bcrypt.GenerateFromPassword([]byte(r.Password), BcryptCost)
423	if err != nil {
424		plog.Errorf("failed to hash password: %s", err)
425		return nil, err
426	}
427
428	tx := as.be.BatchTx()
429	tx.Lock()
430	defer tx.Unlock()
431
432	user := getUser(tx, r.Name)
433	if user == nil {
434		return nil, ErrUserNotFound
435	}
436
437	updatedUser := &authpb.User{
438		Name:     []byte(r.Name),
439		Roles:    user.Roles,
440		Password: hashed,
441	}
442
443	putUser(tx, updatedUser)
444
445	as.commitRevision(tx)
446	as.saveConsistentIndex(tx)
447
448	as.invalidateCachedPerm(r.Name)
449	as.tokenProvider.invalidateUser(r.Name)
450
451	plog.Noticef("changed a password of a user: %s", r.Name)
452
453	return &pb.AuthUserChangePasswordResponse{}, nil
454}
455
456func (as *authStore) UserGrantRole(r *pb.AuthUserGrantRoleRequest) (*pb.AuthUserGrantRoleResponse, error) {
457	tx := as.be.BatchTx()
458	tx.Lock()
459	defer tx.Unlock()
460
461	user := getUser(tx, r.User)
462	if user == nil {
463		return nil, ErrUserNotFound
464	}
465
466	if r.Role != rootRole {
467		role := getRole(tx, r.Role)
468		if role == nil {
469			return nil, ErrRoleNotFound
470		}
471	}
472
473	idx := sort.SearchStrings(user.Roles, r.Role)
474	if idx < len(user.Roles) && strings.Compare(user.Roles[idx], r.Role) == 0 {
475		plog.Warningf("user %s is already granted role %s", r.User, r.Role)
476		return &pb.AuthUserGrantRoleResponse{}, nil
477	}
478
479	user.Roles = append(user.Roles, r.Role)
480	sort.Strings(user.Roles)
481
482	putUser(tx, user)
483
484	as.invalidateCachedPerm(r.User)
485
486	as.commitRevision(tx)
487	as.saveConsistentIndex(tx)
488
489	plog.Noticef("granted role %s to user %s", r.Role, r.User)
490	return &pb.AuthUserGrantRoleResponse{}, nil
491}
492
493func (as *authStore) UserGet(r *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error) {
494	tx := as.be.BatchTx()
495	tx.Lock()
496	user := getUser(tx, r.Name)
497	tx.Unlock()
498
499	if user == nil {
500		return nil, ErrUserNotFound
501	}
502
503	var resp pb.AuthUserGetResponse
504	resp.Roles = append(resp.Roles, user.Roles...)
505	return &resp, nil
506}
507
508func (as *authStore) UserList(r *pb.AuthUserListRequest) (*pb.AuthUserListResponse, error) {
509	tx := as.be.BatchTx()
510	tx.Lock()
511	users := getAllUsers(tx)
512	tx.Unlock()
513
514	resp := &pb.AuthUserListResponse{Users: make([]string, len(users))}
515	for i := range users {
516		resp.Users[i] = string(users[i].Name)
517	}
518	return resp, nil
519}
520
521func (as *authStore) UserRevokeRole(r *pb.AuthUserRevokeRoleRequest) (*pb.AuthUserRevokeRoleResponse, error) {
522	if as.enabled && strings.Compare(r.Name, rootUser) == 0 && strings.Compare(r.Role, rootRole) == 0 {
523		plog.Errorf("the role root must not be revoked from the user root")
524		return nil, ErrInvalidAuthMgmt
525	}
526
527	tx := as.be.BatchTx()
528	tx.Lock()
529	defer tx.Unlock()
530
531	user := getUser(tx, r.Name)
532	if user == nil {
533		return nil, ErrUserNotFound
534	}
535
536	updatedUser := &authpb.User{
537		Name:     user.Name,
538		Password: user.Password,
539	}
540
541	for _, role := range user.Roles {
542		if strings.Compare(role, r.Role) != 0 {
543			updatedUser.Roles = append(updatedUser.Roles, role)
544		}
545	}
546
547	if len(updatedUser.Roles) == len(user.Roles) {
548		return nil, ErrRoleNotGranted
549	}
550
551	putUser(tx, updatedUser)
552
553	as.invalidateCachedPerm(r.Name)
554
555	as.commitRevision(tx)
556	as.saveConsistentIndex(tx)
557
558	plog.Noticef("revoked role %s from user %s", r.Role, r.Name)
559	return &pb.AuthUserRevokeRoleResponse{}, nil
560}
561
562func (as *authStore) RoleGet(r *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse, error) {
563	tx := as.be.BatchTx()
564	tx.Lock()
565	defer tx.Unlock()
566
567	var resp pb.AuthRoleGetResponse
568
569	role := getRole(tx, r.Role)
570	if role == nil {
571		return nil, ErrRoleNotFound
572	}
573	resp.Perm = append(resp.Perm, role.KeyPermission...)
574	return &resp, nil
575}
576
577func (as *authStore) RoleList(r *pb.AuthRoleListRequest) (*pb.AuthRoleListResponse, error) {
578	tx := as.be.BatchTx()
579	tx.Lock()
580	roles := getAllRoles(tx)
581	tx.Unlock()
582
583	resp := &pb.AuthRoleListResponse{Roles: make([]string, len(roles))}
584	for i := range roles {
585		resp.Roles[i] = string(roles[i].Name)
586	}
587	return resp, nil
588}
589
590func (as *authStore) RoleRevokePermission(r *pb.AuthRoleRevokePermissionRequest) (*pb.AuthRoleRevokePermissionResponse, error) {
591	tx := as.be.BatchTx()
592	tx.Lock()
593	defer tx.Unlock()
594
595	role := getRole(tx, r.Role)
596	if role == nil {
597		return nil, ErrRoleNotFound
598	}
599
600	updatedRole := &authpb.Role{
601		Name: role.Name,
602	}
603
604	for _, perm := range role.KeyPermission {
605		if !bytes.Equal(perm.Key, []byte(r.Key)) || !bytes.Equal(perm.RangeEnd, []byte(r.RangeEnd)) {
606			updatedRole.KeyPermission = append(updatedRole.KeyPermission, perm)
607		}
608	}
609
610	if len(role.KeyPermission) == len(updatedRole.KeyPermission) {
611		return nil, ErrPermissionNotGranted
612	}
613
614	putRole(tx, updatedRole)
615
616	// TODO(mitake): currently single role update invalidates every cache
617	// It should be optimized.
618	as.clearCachedPerm()
619
620	as.commitRevision(tx)
621	as.saveConsistentIndex(tx)
622
623	plog.Noticef("revoked key %s from role %s", r.Key, r.Role)
624	return &pb.AuthRoleRevokePermissionResponse{}, nil
625}
626
627func (as *authStore) RoleDelete(r *pb.AuthRoleDeleteRequest) (*pb.AuthRoleDeleteResponse, error) {
628	if as.enabled && strings.Compare(r.Role, rootRole) == 0 {
629		plog.Errorf("the role root must not be deleted")
630		return nil, ErrInvalidAuthMgmt
631	}
632
633	tx := as.be.BatchTx()
634	tx.Lock()
635	defer tx.Unlock()
636
637	role := getRole(tx, r.Role)
638	if role == nil {
639		return nil, ErrRoleNotFound
640	}
641
642	delRole(tx, r.Role)
643
644	users := getAllUsers(tx)
645	for _, user := range users {
646		updatedUser := &authpb.User{
647			Name:     user.Name,
648			Password: user.Password,
649		}
650
651		for _, role := range user.Roles {
652			if strings.Compare(role, r.Role) != 0 {
653				updatedUser.Roles = append(updatedUser.Roles, role)
654			}
655		}
656
657		if len(updatedUser.Roles) == len(user.Roles) {
658			continue
659		}
660
661		putUser(tx, updatedUser)
662
663		as.invalidateCachedPerm(string(user.Name))
664	}
665
666	as.commitRevision(tx)
667	as.saveConsistentIndex(tx)
668
669	plog.Noticef("deleted role %s", r.Role)
670	return &pb.AuthRoleDeleteResponse{}, nil
671}
672
673func (as *authStore) RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error) {
674	tx := as.be.BatchTx()
675	tx.Lock()
676	defer tx.Unlock()
677
678	role := getRole(tx, r.Name)
679	if role != nil {
680		return nil, ErrRoleAlreadyExist
681	}
682
683	newRole := &authpb.Role{
684		Name: []byte(r.Name),
685	}
686
687	putRole(tx, newRole)
688
689	as.commitRevision(tx)
690	as.saveConsistentIndex(tx)
691
692	plog.Noticef("Role %s is created", r.Name)
693
694	return &pb.AuthRoleAddResponse{}, nil
695}
696
697func (as *authStore) authInfoFromToken(ctx context.Context, token string) (*AuthInfo, bool) {
698	return as.tokenProvider.info(ctx, token, as.Revision())
699}
700
701type permSlice []*authpb.Permission
702
703func (perms permSlice) Len() int {
704	return len(perms)
705}
706
707func (perms permSlice) Less(i, j int) bool {
708	return bytes.Compare(perms[i].Key, perms[j].Key) < 0
709}
710
711func (perms permSlice) Swap(i, j int) {
712	perms[i], perms[j] = perms[j], perms[i]
713}
714
715func (as *authStore) RoleGrantPermission(r *pb.AuthRoleGrantPermissionRequest) (*pb.AuthRoleGrantPermissionResponse, error) {
716	tx := as.be.BatchTx()
717	tx.Lock()
718	defer tx.Unlock()
719
720	role := getRole(tx, r.Name)
721	if role == nil {
722		return nil, ErrRoleNotFound
723	}
724
725	idx := sort.Search(len(role.KeyPermission), func(i int) bool {
726		return bytes.Compare(role.KeyPermission[i].Key, []byte(r.Perm.Key)) >= 0
727	})
728
729	if idx < len(role.KeyPermission) && bytes.Equal(role.KeyPermission[idx].Key, r.Perm.Key) && bytes.Equal(role.KeyPermission[idx].RangeEnd, r.Perm.RangeEnd) {
730		// update existing permission
731		role.KeyPermission[idx].PermType = r.Perm.PermType
732	} else {
733		// append new permission to the role
734		newPerm := &authpb.Permission{
735			Key:      []byte(r.Perm.Key),
736			RangeEnd: []byte(r.Perm.RangeEnd),
737			PermType: r.Perm.PermType,
738		}
739
740		role.KeyPermission = append(role.KeyPermission, newPerm)
741		sort.Sort(permSlice(role.KeyPermission))
742	}
743
744	putRole(tx, role)
745
746	// TODO(mitake): currently single role update invalidates every cache
747	// It should be optimized.
748	as.clearCachedPerm()
749
750	as.commitRevision(tx)
751	as.saveConsistentIndex(tx)
752
753	plog.Noticef("role %s's permission of key %s is updated as %s", r.Name, r.Perm.Key, authpb.Permission_Type_name[int32(r.Perm.PermType)])
754
755	return &pb.AuthRoleGrantPermissionResponse{}, nil
756}
757
758func (as *authStore) isOpPermitted(userName string, revision uint64, key, rangeEnd []byte, permTyp authpb.Permission_Type) error {
759	// TODO(mitake): this function would be costly so we need a caching mechanism
760	if !as.isAuthEnabled() {
761		return nil
762	}
763
764	// only gets rev == 0 when passed AuthInfo{}; no user given
765	if revision == 0 {
766		return ErrUserEmpty
767	}
768	rev := as.Revision()
769	if revision < rev {
770		plog.Warningf("request auth revision is less than current node auth revision,"+
771			"current node auth revision is %d,"+
772			"request auth revision is %d,"+
773			"request key is %s, "+
774			"err is %v", rev, revision, key, ErrAuthOldRevision)
775		return ErrAuthOldRevision
776	}
777
778	tx := as.be.BatchTx()
779	tx.Lock()
780	defer tx.Unlock()
781
782	user := getUser(tx, userName)
783	if user == nil {
784		plog.Errorf("invalid user name %s for permission checking", userName)
785		return ErrPermissionDenied
786	}
787
788	// root role should have permission on all ranges
789	if hasRootRole(user) {
790		return nil
791	}
792
793	if as.isRangeOpPermitted(tx, userName, key, rangeEnd, permTyp) {
794		return nil
795	}
796
797	return ErrPermissionDenied
798}
799
800func (as *authStore) IsPutPermitted(authInfo *AuthInfo, key []byte) error {
801	return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, nil, authpb.WRITE)
802}
803
804func (as *authStore) IsRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error {
805	return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, rangeEnd, authpb.READ)
806}
807
808func (as *authStore) IsDeleteRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error {
809	return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, rangeEnd, authpb.WRITE)
810}
811
812func (as *authStore) IsAdminPermitted(authInfo *AuthInfo) error {
813	if !as.isAuthEnabled() {
814		return nil
815	}
816	if authInfo == nil || authInfo.Username == "" {
817		return ErrUserEmpty
818	}
819
820	tx := as.be.BatchTx()
821	tx.Lock()
822	u := getUser(tx, authInfo.Username)
823	tx.Unlock()
824
825	if u == nil {
826		return ErrUserNotFound
827	}
828
829	if !hasRootRole(u) {
830		return ErrPermissionDenied
831	}
832
833	return nil
834}
835
836func getUser(tx backend.BatchTx, username string) *authpb.User {
837	_, vs := tx.UnsafeRange(authUsersBucketName, []byte(username), nil, 0)
838	if len(vs) == 0 {
839		return nil
840	}
841
842	user := &authpb.User{}
843	err := user.Unmarshal(vs[0])
844	if err != nil {
845		plog.Panicf("failed to unmarshal user struct (name: %s): %s", username, err)
846	}
847	return user
848}
849
850func getAllUsers(tx backend.BatchTx) []*authpb.User {
851	_, vs := tx.UnsafeRange(authUsersBucketName, []byte{0}, []byte{0xff}, -1)
852	if len(vs) == 0 {
853		return nil
854	}
855
856	users := make([]*authpb.User, len(vs))
857	for i := range vs {
858		user := &authpb.User{}
859		err := user.Unmarshal(vs[i])
860		if err != nil {
861			plog.Panicf("failed to unmarshal user struct: %s", err)
862		}
863		users[i] = user
864	}
865	return users
866}
867
868func putUser(tx backend.BatchTx, user *authpb.User) {
869	b, err := user.Marshal()
870	if err != nil {
871		plog.Panicf("failed to marshal user struct (name: %s): %s", user.Name, err)
872	}
873	tx.UnsafePut(authUsersBucketName, user.Name, b)
874}
875
876func delUser(tx backend.BatchTx, username string) {
877	tx.UnsafeDelete(authUsersBucketName, []byte(username))
878}
879
880func getRole(tx backend.BatchTx, rolename string) *authpb.Role {
881	_, vs := tx.UnsafeRange(authRolesBucketName, []byte(rolename), nil, 0)
882	if len(vs) == 0 {
883		return nil
884	}
885
886	role := &authpb.Role{}
887	err := role.Unmarshal(vs[0])
888	if err != nil {
889		plog.Panicf("failed to unmarshal role struct (name: %s): %s", rolename, err)
890	}
891	return role
892}
893
894func getAllRoles(tx backend.BatchTx) []*authpb.Role {
895	_, vs := tx.UnsafeRange(authRolesBucketName, []byte{0}, []byte{0xff}, -1)
896	if len(vs) == 0 {
897		return nil
898	}
899
900	roles := make([]*authpb.Role, len(vs))
901	for i := range vs {
902		role := &authpb.Role{}
903		err := role.Unmarshal(vs[i])
904		if err != nil {
905			plog.Panicf("failed to unmarshal role struct: %s", err)
906		}
907		roles[i] = role
908	}
909	return roles
910}
911
912func putRole(tx backend.BatchTx, role *authpb.Role) {
913	b, err := role.Marshal()
914	if err != nil {
915		plog.Panicf("failed to marshal role struct (name: %s): %s", role.Name, err)
916	}
917
918	tx.UnsafePut(authRolesBucketName, []byte(role.Name), b)
919}
920
921func delRole(tx backend.BatchTx, rolename string) {
922	tx.UnsafeDelete(authRolesBucketName, []byte(rolename))
923}
924
925func (as *authStore) isAuthEnabled() bool {
926	as.enabledMu.RLock()
927	defer as.enabledMu.RUnlock()
928	return as.enabled
929}
930
931func NewAuthStore(be backend.Backend, tp TokenProvider) *authStore {
932	tx := be.BatchTx()
933	tx.Lock()
934
935	tx.UnsafeCreateBucket(authBucketName)
936	tx.UnsafeCreateBucket(authUsersBucketName)
937	tx.UnsafeCreateBucket(authRolesBucketName)
938
939	enabled := false
940	_, vs := tx.UnsafeRange(authBucketName, enableFlagKey, nil, 0)
941	if len(vs) == 1 {
942		if bytes.Equal(vs[0], authEnabled) {
943			enabled = true
944		}
945	}
946
947	as := &authStore{
948		be:             be,
949		revision:       getRevision(tx),
950		enabled:        enabled,
951		rangePermCache: make(map[string]*unifiedRangePermissions),
952		tokenProvider:  tp,
953	}
954
955	if enabled {
956		as.tokenProvider.enable()
957	}
958
959	if as.Revision() == 0 {
960		as.commitRevision(tx)
961	}
962
963	as.setupMetricsReporter()
964
965	tx.Unlock()
966	be.ForceCommit()
967
968	return as
969}
970
971func hasRootRole(u *authpb.User) bool {
972	// u.Roles is sorted in UserGrantRole(), so we can use binary search.
973	idx := sort.SearchStrings(u.Roles, rootRole)
974	return idx != len(u.Roles) && u.Roles[idx] == rootRole
975}
976
977func (as *authStore) commitRevision(tx backend.BatchTx) {
978	atomic.AddUint64(&as.revision, 1)
979	revBytes := make([]byte, revBytesLen)
980	binary.BigEndian.PutUint64(revBytes, as.Revision())
981	tx.UnsafePut(authBucketName, revisionKey, revBytes)
982}
983
984func getRevision(tx backend.BatchTx) uint64 {
985	_, vs := tx.UnsafeRange(authBucketName, []byte(revisionKey), nil, 0)
986	if len(vs) != 1 {
987		// this can happen in the initialization phase
988		return 0
989	}
990
991	return binary.BigEndian.Uint64(vs[0])
992}
993
994func (as *authStore) setRevision(rev uint64) {
995	atomic.StoreUint64(&as.revision, rev)
996}
997
998func (as *authStore) Revision() uint64 {
999	return atomic.LoadUint64(&as.revision)
1000}
1001
1002func (as *authStore) AuthInfoFromTLS(ctx context.Context) *AuthInfo {
1003	peer, ok := peer.FromContext(ctx)
1004	if !ok || peer == nil || peer.AuthInfo == nil {
1005		return nil
1006	}
1007
1008	tlsInfo := peer.AuthInfo.(credentials.TLSInfo)
1009	for _, chains := range tlsInfo.State.VerifiedChains {
1010		for _, chain := range chains {
1011			cn := chain.Subject.CommonName
1012			plog.Debugf("found common name %s", cn)
1013
1014			ai := &AuthInfo{
1015				Username: cn,
1016				Revision: as.Revision(),
1017			}
1018			md, ok := metadata.FromIncomingContext(ctx)
1019			if !ok {
1020				return nil
1021			}
1022
1023			// gRPC-gateway proxy request to etcd server includes Grpcgateway-Accept
1024			// header. The proxy uses etcd client server certificate. If the certificate
1025			// has a CommonName we should never use this for authentication.
1026			if gw := md["grpcgateway-accept"]; len(gw) > 0 {
1027				plog.Warningf("ignoring common name in gRPC-gateway proxy request %s", ai.Username)
1028				return nil
1029			}
1030			return ai
1031		}
1032	}
1033
1034	return nil
1035}
1036
1037func (as *authStore) AuthInfoFromCtx(ctx context.Context) (*AuthInfo, error) {
1038	md, ok := metadata.FromIncomingContext(ctx)
1039	if !ok {
1040		return nil, nil
1041	}
1042
1043	//TODO(mitake|hexfusion) review unifying key names
1044	ts, ok := md["token"]
1045	if !ok {
1046		ts, ok = md["authorization"]
1047	}
1048	if !ok {
1049		return nil, nil
1050	}
1051
1052	token := ts[0]
1053	authInfo, uok := as.authInfoFromToken(ctx, token)
1054	if !uok {
1055		plog.Warningf("invalid auth token: %s", token)
1056		return nil, ErrInvalidAuthToken
1057	}
1058
1059	return authInfo, nil
1060}
1061
1062func (as *authStore) GenTokenPrefix() (string, error) {
1063	return as.tokenProvider.genTokenPrefix()
1064}
1065
1066func decomposeOpts(optstr string) (string, map[string]string, error) {
1067	opts := strings.Split(optstr, ",")
1068	tokenType := opts[0]
1069
1070	typeSpecificOpts := make(map[string]string)
1071	for i := 1; i < len(opts); i++ {
1072		pair := strings.Split(opts[i], "=")
1073
1074		if len(pair) != 2 {
1075			plog.Errorf("invalid token specific option: %s", optstr)
1076			return "", nil, ErrInvalidAuthOpts
1077		}
1078
1079		if _, ok := typeSpecificOpts[pair[0]]; ok {
1080			plog.Errorf("invalid token specific option, duplicated parameters (%s): %s", pair[0], optstr)
1081			return "", nil, ErrInvalidAuthOpts
1082		}
1083
1084		typeSpecificOpts[pair[0]] = pair[1]
1085	}
1086
1087	return tokenType, typeSpecificOpts, nil
1088
1089}
1090
1091// NewTokenProvider creates a new token provider.
1092func NewTokenProvider(
1093	tokenOpts string,
1094	indexWaiter func(uint64) <-chan struct{},
1095	TokenTTL time.Duration) (TokenProvider, error) {
1096	tokenType, typeSpecificOpts, err := decomposeOpts(tokenOpts)
1097	if err != nil {
1098		return nil, ErrInvalidAuthOpts
1099	}
1100
1101	switch tokenType {
1102	case tokenTypeSimple:
1103		plog.Warningf("simple token is not cryptographically signed")
1104		return newTokenProviderSimple(indexWaiter, TokenTTL), nil
1105
1106	case tokenTypeJWT:
1107		return newTokenProviderJWT(typeSpecificOpts)
1108
1109	case "":
1110		return newTokenProviderNop()
1111	default:
1112		plog.Errorf("unknown token type: %s", tokenType)
1113		return nil, ErrInvalidAuthOpts
1114	}
1115}
1116
1117func (as *authStore) WithRoot(ctx context.Context) context.Context {
1118	if !as.isAuthEnabled() {
1119		return ctx
1120	}
1121
1122	var ctxForAssign context.Context
1123	if ts, ok := as.tokenProvider.(*tokenSimple); ok && ts != nil {
1124		ctx1 := context.WithValue(ctx, AuthenticateParamIndex{}, uint64(0))
1125		prefix, err := ts.genTokenPrefix()
1126		if err != nil {
1127			plog.Errorf("failed to generate prefix of internally used token")
1128			return ctx
1129		}
1130		ctxForAssign = context.WithValue(ctx1, AuthenticateParamSimpleTokenPrefix{}, prefix)
1131	} else {
1132		ctxForAssign = ctx
1133	}
1134
1135	token, err := as.tokenProvider.assign(ctxForAssign, "root", as.Revision())
1136	if err != nil {
1137		// this must not happen
1138		plog.Errorf("failed to assign token for lease revoking: %s", err)
1139		return ctx
1140	}
1141
1142	mdMap := map[string]string{
1143		"token": token,
1144	}
1145	tokenMD := metadata.New(mdMap)
1146
1147	// use "mdIncomingKey{}" since it's called from local etcdserver
1148	return metadata.NewIncomingContext(ctx, tokenMD)
1149}
1150
1151func (as *authStore) HasRole(user, role string) bool {
1152	tx := as.be.BatchTx()
1153	tx.Lock()
1154	u := getUser(tx, user)
1155	tx.Unlock()
1156
1157	if u == nil {
1158		plog.Warningf("tried to check user %s has role %s, but user %s doesn't exist", user, role, user)
1159		return false
1160	}
1161
1162	for _, r := range u.Roles {
1163		if role == r {
1164			return true
1165		}
1166	}
1167
1168	return false
1169}
1170
1171func (as *authStore) saveConsistentIndex(tx backend.BatchTx) {
1172	if as.syncConsistentIndex != nil {
1173		as.syncConsistentIndex(tx)
1174	} else {
1175		plog.Errorf("failed to save consistentIndex,syncConsistentIndex is nil")
1176	}
1177}
1178
1179func (as *authStore) setupMetricsReporter() {
1180	reportCurrentAuthRevMu.Lock()
1181	reportCurrentAuthRev = func() float64 {
1182		return float64(as.Revision())
1183	}
1184	reportCurrentAuthRevMu.Unlock()
1185}
1186