Skip to content

Commit

Permalink
identity/oidc: adds tests for validation of loopback IP redirect URIs (
Browse files Browse the repository at this point in the history
…#13939)

* identity/oidc: adds tests for validation of loopback IP redirect URIs

* Update vault/identity_store_oidc_provider_test.go

Co-authored-by: John-Michael Faircloth <[email protected]>

Co-authored-by: John-Michael Faircloth <[email protected]>
  • Loading branch information
austingebauer and fairclothjm committed Feb 12, 2022
1 parent 405cdb3 commit d95e197
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 36 deletions.
36 changes: 0 additions & 36 deletions vault/identity_store_oidc_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@ package vault

import (
"context"
"crypto/sha256"
"crypto/sha512"
"crypto/subtle"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"hash"
"net/http"
"net/url"
"sort"
Expand Down Expand Up @@ -2159,39 +2156,6 @@ func (i *IdentityStore) populateScopeTemplates(ctx context.Context, s logical.St
return populatedTemplates, false, nil
}

// computeHashClaim computes the hash value to be used for the at_hash
// and c_hash claims. For details on how this value is computed and the
// class of attacks it's used to prevent, see the spec at
// - https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken
// - https://openid.net/specs/openid-connect-core-1_0.html#HybridIDToken
// - https://openid.net/specs/openid-connect-core-1_0.html#TokenSubstitution
func computeHashClaim(alg string, input string) (string, error) {
signatureAlgToHash := map[jose.SignatureAlgorithm]func() hash.Hash{
jose.RS256: sha256.New,
jose.RS384: sha512.New384,
jose.RS512: sha512.New,
jose.ES256: sha256.New,
jose.ES384: sha512.New384,
jose.ES512: sha512.New,

// We use the Ed25519 curve key for EdDSA, which uses
// SHA-512 for its digest algorithm. See details at
// https://bitbucket.org/openid/connect/issues/1125.
jose.EdDSA: sha512.New,
}

newHash, ok := signatureAlgToHash[jose.SignatureAlgorithm(alg)]
if !ok {
return "", fmt.Errorf("unsupported signature algorithm: %q", alg)
}
h := newHash()

// Writing to the hash will never return an error
_, _ = h.Write([]byte(input))
sum := h.Sum(nil)
return base64.RawURLEncoding.EncodeToString(sum[:len(sum)/2]), nil
}

// entityHasAssignment returns true if the entity is enabled and a member of any
// of the assignments' groups or entities. Otherwise, returns false or an error.
func (i *IdentityStore) entityHasAssignment(ctx context.Context, s logical.Storage, entity *identity.Entity, assignments []string) (bool, error) {
Expand Down
72 changes: 72 additions & 0 deletions vault/identity_store_oidc_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,78 @@ func TestOIDC_Path_OIDC_Authorize(t *testing.T) {
authorizeReq: testAuthorizeReq(s, clientID),
},
},
{
name: "valid authorize request with port-agnostic loopback redirect_uri 127.0.0.1",
args: args{
entityID: entityID,
clientReq: func() *logical.Request {
req := testClientReq(s)
req.Data["redirect_uris"] = []string{"http://127.0.0.1/callback"}
return req
}(),
providerReq: testProviderReq(s, clientID),
assignmentReq: testAssignmentReq(s, entityID, groupID),
authorizeReq: func() *logical.Request {
req := testAuthorizeReq(s, clientID)
req.Data["redirect_uri"] = "http://127.0.0.1:51004/callback"
return req
}(),
},
},
{
name: "valid authorize request with port-agnostic loopback redirect_uri 127.0.0.1 with port",
args: args{
entityID: entityID,
clientReq: func() *logical.Request {
req := testClientReq(s)
req.Data["redirect_uris"] = []string{"http://127.0.0.1:8251/callback"}
return req
}(),
providerReq: testProviderReq(s, clientID),
assignmentReq: testAssignmentReq(s, entityID, groupID),
authorizeReq: func() *logical.Request {
req := testAuthorizeReq(s, clientID)
req.Data["redirect_uri"] = "http://127.0.0.1:51005/callback"
return req
}(),
},
},
{
name: "valid authorize request with port-agnostic loopback redirect_uri localhost",
args: args{
entityID: entityID,
clientReq: func() *logical.Request {
req := testClientReq(s)
req.Data["redirect_uris"] = []string{"http://localhost:8251/callback"}
return req
}(),
providerReq: testProviderReq(s, clientID),
assignmentReq: testAssignmentReq(s, entityID, groupID),
authorizeReq: func() *logical.Request {
req := testAuthorizeReq(s, clientID)
req.Data["redirect_uri"] = "http://localhost:51006/callback"
return req
}(),
},
},
{
name: "valid authorize request with port-agnostic loopback redirect_uri [::1]",
args: args{
entityID: entityID,
clientReq: func() *logical.Request {
req := testClientReq(s)
req.Data["redirect_uris"] = []string{"http://[::1]:8251/callback"}
return req
}(),
providerReq: testProviderReq(s, clientID),
assignmentReq: testAssignmentReq(s, entityID, groupID),
authorizeReq: func() *logical.Request {
req := testAuthorizeReq(s, clientID)
req.Data["redirect_uri"] = "http://[::1]:51007/callback"
return req
}(),
},
},
}

for _, tt := range tests {
Expand Down
39 changes: 39 additions & 0 deletions vault/identity_store_oidc_provider_util.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package vault

import (
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"fmt"
"hash"
"net/url"

"github.com/hashicorp/go-secure-stdlib/strutil"
"gopkg.in/square/go-jose.v2"
)

// validRedirect checks whether uri is in allowed using special handling for loopback uris.
Expand Down Expand Up @@ -36,3 +42,36 @@ func validRedirect(uri string, allowed []string) bool {

return false
}

// computeHashClaim computes the hash value to be used for the at_hash
// and c_hash claims. For details on how this value is computed and the
// class of attacks it's used to prevent, see the spec at
// - https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken
// - https://openid.net/specs/openid-connect-core-1_0.html#HybridIDToken
// - https://openid.net/specs/openid-connect-core-1_0.html#TokenSubstitution
func computeHashClaim(alg string, input string) (string, error) {
signatureAlgToHash := map[jose.SignatureAlgorithm]func() hash.Hash{
jose.RS256: sha256.New,
jose.RS384: sha512.New384,
jose.RS512: sha512.New,
jose.ES256: sha256.New,
jose.ES384: sha512.New384,
jose.ES512: sha512.New,

// We use the Ed25519 curve key for EdDSA, which uses
// SHA-512 for its digest algorithm. See details at
// https://bitbucket.org/openid/connect/issues/1125.
jose.EdDSA: sha512.New,
}

newHash, ok := signatureAlgToHash[jose.SignatureAlgorithm(alg)]
if !ok {
return "", fmt.Errorf("unsupported signature algorithm: %q", alg)
}
h := newHash()

// Writing to the hash will never return an error
_, _ = h.Write([]byte(input))
sum := h.Sum(nil)
return base64.RawURLEncoding.EncodeToString(sum[:len(sum)/2]), nil
}

0 comments on commit d95e197

Please sign in to comment.