Skip to content

Commit

Permalink
approle: Fix role name case sensitivity issue
Browse files Browse the repository at this point in the history
  • Loading branch information
vishalnayak authored and jefferai committed Jun 5, 2018
1 parent cc003bb commit 8182186
Show file tree
Hide file tree
Showing 8 changed files with 670 additions and 257 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

SECURITY:

* The Vault team identified a race condition that could occur if a token's
* Tokens: A race condition was identified that could occur if a token's
lease expired while Vault was not running. In this case, when Vault came
back online, sometimes it would properly revoke the lease but other times it
would not, leading to a Vault token that no longer had an expiration and had
Expand All @@ -12,6 +12,11 @@ SECURITY:
future issues. In addition, the logic we have put in place ensures that such
lease-less tokens can no longer be used (unless they are root tokens that
never had an expiration to begin with).
* AppRole case-sensitive role name secret-id leaking: When using a mixed-case
role name via AppRole, deleting a secret-id via accessor or other operations
could end up leaving the secret-id behind and valid but without an accessor.
This has now been fixed, and we have put checks in place to prevent these
secret-ids from being used.

DEPRECATIONS/CHANGES:

Expand Down
332 changes: 332 additions & 0 deletions builtin/credential/approle/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package approle

import (
"context"
"strings"
"testing"

"github.com/hashicorp/vault/logical"
Expand All @@ -24,3 +25,334 @@ func createBackendWithStorage(t *testing.T) (*backend, logical.Storage) {
}
return b, config.StorageView
}

func TestAppRole_RoleNameCaseSensitivity(t *testing.T) {
testFunc := func(t *testing.T, roleName string) {
var resp *logical.Response
var err error
b, s := createBackendWithStorage(t)

// Create the role
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "role/" + roleName,
Operation: logical.CreateOperation,
Storage: s,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr:%v", resp, err)
}

// Get the role-id
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "role/" + roleName + "/role-id",
Operation: logical.ReadOperation,
Storage: s,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
}
roleID := resp.Data["role_id"]

// Create a secret-id
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "role/" + roleName + "/secret-id",
Operation: logical.UpdateOperation,
Storage: s,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
}
secretID := resp.Data["secret_id"]
secretIDAccessor := resp.Data["secret_id_accessor"]

// Ensure login works
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "login",
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"role_id": roleID,
"secret_id": secretID,
},
Storage: s,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
}
if resp.Auth == nil {
t.Fatalf("failed to perform login")
}

// Destroy secret ID accessor
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "role/" + roleName + "/secret-id-accessor/destroy",
Operation: logical.UpdateOperation,
Storage: s,
Data: map[string]interface{}{
"secret_id_accessor": secretIDAccessor,
},
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
}

// Login again using the accessor's corresponding secret ID should fail
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "login",
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"role_id": roleID,
"secret_id": secretID,
},
Storage: s,
})
if err != nil {
t.Fatal(err)
}
if resp == nil || !resp.IsError() {
t.Fatalf("expected error due to invalid secret ID")
}

// Generate another secret ID
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "role/" + roleName + "/secret-id",
Operation: logical.UpdateOperation,
Storage: s,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
}
secretID = resp.Data["secret_id"]
secretIDAccessor = resp.Data["secret_id_accessor"]

// Ensure login works
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "login",
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"role_id": roleID,
"secret_id": secretID,
},
Storage: s,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
}
if resp.Auth == nil {
t.Fatalf("failed to perform login")
}

// Destroy the secret ID
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "role/" + roleName + "/secret-id/destroy",
Operation: logical.UpdateOperation,
Storage: s,
Data: map[string]interface{}{
"secret_id": secretID,
},
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
}

// Login again using the same secret ID should fail
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "login",
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"role_id": roleID,
"secret_id": secretID,
},
Storage: s,
})
if err != nil {
t.Fatal(err)
}
if resp == nil || !resp.IsError() {
t.Fatalf("expected error due to invalid secret ID")
}

// Generate another secret ID
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "role/" + roleName + "/secret-id",
Operation: logical.UpdateOperation,
Storage: s,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
}
secretID = resp.Data["secret_id"]
secretIDAccessor = resp.Data["secret_id_accessor"]

// Ensure login works
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "login",
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"role_id": roleID,
"secret_id": secretID,
},
Storage: s,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
}
if resp.Auth == nil {
t.Fatalf("failed to perform login")
}

// Destroy the secret ID using lower cased role name
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "role/" + strings.ToLower(roleName) + "/secret-id/destroy",
Operation: logical.UpdateOperation,
Storage: s,
Data: map[string]interface{}{
"secret_id": secretID,
},
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
}

// Login again using the same secret ID should fail
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "login",
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"role_id": roleID,
"secret_id": secretID,
},
Storage: s,
})
if err != nil {
t.Fatal(err)
}
if resp == nil || !resp.IsError() {
t.Fatalf("expected error due to invalid secret ID")
}

// Generate another secret ID
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "role/" + roleName + "/secret-id",
Operation: logical.UpdateOperation,
Storage: s,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
}
secretID = resp.Data["secret_id"]
secretIDAccessor = resp.Data["secret_id_accessor"]

// Ensure login works
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "login",
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"role_id": roleID,
"secret_id": secretID,
},
Storage: s,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
}
if resp.Auth == nil {
t.Fatalf("failed to perform login")
}

// Destroy the secret ID using upper cased role name
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "role/" + strings.ToUpper(roleName) + "/secret-id/destroy",
Operation: logical.UpdateOperation,
Storage: s,
Data: map[string]interface{}{
"secret_id": secretID,
},
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
}

// Login again using the same secret ID should fail
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "login",
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"role_id": roleID,
"secret_id": secretID,
},
Storage: s,
})
if err != nil {
t.Fatal(err)
}
if resp == nil || !resp.IsError() {
t.Fatalf("expected error due to invalid secret ID")
}

// Generate another secret ID
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "role/" + roleName + "/secret-id",
Operation: logical.UpdateOperation,
Storage: s,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
}
secretID = resp.Data["secret_id"]
secretIDAccessor = resp.Data["secret_id_accessor"]

// Ensure login works
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "login",
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"role_id": roleID,
"secret_id": secretID,
},
Storage: s,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
}
if resp.Auth == nil {
t.Fatalf("failed to perform login")
}

// Destroy the secret ID using mixed case name
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "role/saMpleRolEnaMe/secret-id/destroy",
Operation: logical.UpdateOperation,
Storage: s,
Data: map[string]interface{}{
"secret_id": secretID,
},
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
}

// Login again using the same secret ID should fail
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "login",
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"role_id": roleID,
"secret_id": secretID,
},
Storage: s,
})
if err != nil {
t.Fatal(err)
}
if resp == nil || !resp.IsError() {
t.Fatalf("expected error due to invalid secret ID")
}
}

// Lower case role name
testFunc(t, "samplerolename")
// Upper case role name
testFunc(t, "SAMPLEROLENAME")
// Mixed case role name
testFunc(t, "SampleRoleName")
}
Loading

0 comments on commit 8182186

Please sign in to comment.