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