disallow token use if entity is invalid (#4791)

This commit is contained in:
Vishal Nayak 2018-06-19 12:57:19 -04:00 committed by GitHub
parent 961d24d89a
commit 0d8f424ab4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 144 additions and 16 deletions

View File

@ -44,6 +44,11 @@ func (c *Core) Capabilities(ctx context.Context, token, path string) ([]string,
}
if entity != nil && entity.Disabled {
c.logger.Warn("permission denied as the entity on the token is disabled")
return nil, logical.ErrPermissionDenied
}
if te != nil && te.EntityID != "" && entity == nil {
c.logger.Warn("permission denied as the entity on the token is invalid")
return nil, logical.ErrPermissionDenied
}

View File

@ -1003,7 +1003,6 @@ func (c *Core) sealInitCommon(ctx context.Context, req *logical.Request) (retErr
return retErr
}
// Validate the token is a root token
acl, te, entity, identityPolicies, err := c.fetchACLTokenEntryAndEntity(req)
if err != nil {
retErr = multierror.Append(retErr, err)
@ -1037,6 +1036,13 @@ func (c *Core) sealInitCommon(ctx context.Context, req *logical.Request) (retErr
}
if entity != nil && entity.Disabled {
c.logger.Warn("permission denied as the entity on the token is disabled")
retErr = multierror.Append(retErr, logical.ErrPermissionDenied)
c.stateLock.RUnlock()
return retErr
}
if te != nil && te.EntityID != "" && entity == nil {
c.logger.Warn("permission denied as the entity on the token is invalid")
retErr = multierror.Append(retErr, logical.ErrPermissionDenied)
c.stateLock.RUnlock()
return retErr

View File

@ -192,6 +192,14 @@ func (c *Core) StepDown(req *logical.Request) (retErr error) {
}
if entity != nil && entity.Disabled {
c.logger.Warn("permission denied as the entity on the token is disabled")
retErr = multierror.Append(retErr, logical.ErrPermissionDenied)
c.stateLock.RUnlock()
return retErr
}
if te != nil && te.EntityID != "" && entity == nil {
c.logger.Warn("permission denied as the entity on the token is invalid")
retErr = multierror.Append(retErr, logical.ErrPermissionDenied)
c.stateLock.RUnlock()
return retErr

View File

@ -311,10 +311,10 @@ func testIdentityStoreWithGithubAuth(t *testing.T) (*IdentityStore, string, *Cor
return is, ghA, c
}
// testIdentityStoreWithGithubAuth returns an instance of identity store which
// is mounted by default. This function also enables the github auth backend to
// assist with testing aliases and entities that require an valid mount
// accessor of an auth backend.
// testIdentityStoreWithGithubAuthRoot returns an instance of identity store
// which is mounted by default. This function also enables the github auth
// backend to assist with testing aliases and entities that require an valid
// mount accessor of an auth backend.
func testIdentityStoreWithGithubAuthRoot(t *testing.T) (*IdentityStore, string, *Core, string) {
// Add github credential factory to core config
err := AddTestCredentialBackend("github", credGithub.Factory)
@ -336,13 +336,7 @@ func testIdentityStoreWithGithubAuthRoot(t *testing.T) (*IdentityStore, string,
t.Fatal(err)
}
// Identity store will be mounted by now, just fetch it from router
identitystore := c.router.MatchingBackend("identity/")
if identitystore == nil {
t.Fatalf("failed to fetch identity store from router")
}
return identitystore.(*IdentityStore), meGH.Accessor, c, root
return c.identityStore, meGH.Accessor, c, root
}
func TestIdentityStore_MetadataKeyRegex(t *testing.T) {

View File

@ -1456,6 +1456,9 @@ func (b *SystemBackend) handleCapabilities(ctx context.Context, req *logical.Req
for _, path := range paths {
pathCap, err := b.Core.Capabilities(ctx, token, path)
if err != nil {
if !strings.HasSuffix(req.Path, "capabilities-self") && errwrap.Contains(err, logical.ErrPermissionDenied.Error()) {
return nil, &logical.StatusBadRequest{Err: "invalid token"}
}
return nil, err
}
ret.Data[path] = pathCap
@ -3476,15 +3479,20 @@ func (b *SystemBackend) pathInternalUIMountsRead(ctx context.Context, req *logic
isAuthed = true
var entity *identity.Entity
var te *logical.TokenEntry
// Load the ACL policies so we can walk the prefix for this mount
acl, _, entity, _, err = b.Core.fetchACLTokenEntryAndEntity(req)
acl, te, entity, _, err = b.Core.fetchACLTokenEntryAndEntity(req)
if err != nil {
return nil, err
}
if entity != nil && entity.Disabled {
b.logger.Warn("permission denied as the entity on the token is disabled")
return nil, logical.ErrPermissionDenied
}
if te != nil && te.EntityID != "" && entity == nil {
b.logger.Warn("permission denied as the entity on the token is invalid")
return nil, logical.ErrPermissionDenied
}
}
hasAccess := func(me *MountEntry) bool {
@ -3558,13 +3566,18 @@ func (b *SystemBackend) pathInternalUIMountRead(ctx context.Context, req *logica
resp.Data["path"] = me.Path
// Load the ACL policies so we can walk the prefix for this mount
acl, _, entity, _, err := b.Core.fetchACLTokenEntryAndEntity(req)
acl, te, entity, _, err := b.Core.fetchACLTokenEntryAndEntity(req)
if err != nil {
return nil, err
}
if entity != nil && entity.Disabled {
b.logger.Warn("permission denied as the entity on the token is disabled")
return errResp, logical.ErrPermissionDenied
}
if te != nil && te.EntityID != "" && entity == nil {
b.logger.Warn("permission denied as the entity on the token is invalid")
return nil, logical.ErrPermissionDenied
}
if !hasMountAccess(acl, me.Path) {
return errResp, logical.ErrPermissionDenied
@ -3579,12 +3592,17 @@ func (b *SystemBackend) pathInternalUIResultantACL(ctx context.Context, req *log
return nil, nil
}
acl, _, entity, _, err := b.Core.fetchACLTokenEntryAndEntity(req)
acl, te, entity, _, err := b.Core.fetchACLTokenEntryAndEntity(req)
if err != nil {
return nil, err
}
if entity != nil && entity.Disabled {
b.logger.Warn("permission denied as the entity on the token is disabled")
return logical.ErrorResponse(logical.ErrPermissionDenied.Error()), nil
}
if te != nil && te.EntityID != "" && entity == nil {
b.logger.Warn("permission denied as the entity on the token is invalid")
return logical.ErrorResponse(logical.ErrPermissionDenied.Error()), nil
}

View File

@ -170,6 +170,11 @@ func (c *Core) checkToken(ctx context.Context, req *logical.Request, unauth bool
}
if entity != nil && entity.Disabled {
c.logger.Warn("permission denied as the entity on the token is disabled")
return nil, te, logical.ErrPermissionDenied
}
if te != nil && te.EntityID != "" && entity == nil {
c.logger.Warn("permission denied as the entity on the token is invalid")
return nil, te, logical.ErrPermissionDenied
}

View File

@ -43,6 +43,7 @@ import (
"github.com/hashicorp/vault/physical"
"github.com/mitchellh/go-testing-interface"
credUserpass "github.com/hashicorp/vault/builtin/credential/userpass"
physInmem "github.com/hashicorp/vault/physical/inmem"
)
@ -84,6 +85,30 @@ oOyBJU/HMVvBfv4g+OVFLVgSwwm6owwsouZ0+D/LasbuHqYyqYqdyPJQYzWA2Y+F
`
)
func testCoreWithUserpassAuthRoot(t testing.T) (*Core, string, string) {
// Add userpass credential factory to core config
err := AddTestCredentialBackend("userpass", credUserpass.Factory)
if err != nil {
t.Fatal(err)
}
c, _, root := TestCoreUnsealed(t)
meGH := &MountEntry{
Table: credentialTableType,
Path: "userpass/",
Type: "userpass",
Description: "userpass auth",
}
err = c.enableCredential(context.Background(), meGH)
if err != nil {
t.Fatal(err)
}
return c, meGH.Accessor, root
}
// TestCore returns a pure in-memory, uninitialized core for testing.
func TestCore(t testing.T) *Core {
return TestCoreWithSeal(t, nil, false)

View File

@ -35,6 +35,73 @@ type TokenEntryOld struct {
Period time.Duration
}
func TestTokenStore_TokenInvalidEntityID(t *testing.T) {
c, _, root := testCoreWithUserpassAuthRoot(t)
// Add a user to the userpass backend
resp, err := c.HandleRequest(&logical.Request{
Path: "auth/userpass/users/testuser",
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"password": "testpassword",
},
ClientToken: root,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err: %v\nresp: %#v", err, resp)
}
// Perform a userpass login. This will also create an entity for the logged
// in user.
resp, err = c.HandleRequest(&logical.Request{
Path: "auth/userpass/login/testuser",
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"password": "testpassword",
},
Connection: &logical.Connection{
RemoteAddr: "127.0.0.1",
},
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err: %v\nresp: %#v", err, resp)
}
token := resp.Auth.ClientToken
// Make sure that the token is usable by invoking lookup-self
resp, err = c.HandleRequest(&logical.Request{
Path: "auth/token/lookup-self",
Operation: logical.ReadOperation,
ClientToken: token,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err: %v\nresp: %#v", err, resp)
}
// Delete the entity to which the token belongs to
entityID := resp.Data["entity_id"].(string)
resp, err = c.HandleRequest(&logical.Request{
Path: "identity/entity/id/" + entityID,
Operation: logical.DeleteOperation,
ClientToken: root,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err: %v\nresp: %#v", err, resp)
}
// Make sure that the token is usable
resp, err = c.HandleRequest(&logical.Request{
Path: "auth/token/lookup-self",
Operation: logical.ReadOperation,
ClientToken: token,
})
if err == nil {
t.Fatalf("expected error due to token being invalid when its entity is invalid")
}
}
func TestTokenStore_TokenEntryUpgrade(t *testing.T) {
var err error
c, _, _ := TestCoreUnsealed(t)