identity/oidc: adds tests for validation of loopback IP redirect URIs (#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 <fairclothjm@users.noreply.github.com>

Co-authored-by: John-Michael Faircloth <fairclothjm@users.noreply.github.com>
This commit is contained in:
Austin Gebauer 2022-02-07 12:50:36 -08:00 committed by GitHub
parent e811821ac7
commit 4c12c2bb42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 111 additions and 36 deletions

View File

@ -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"
@ -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) {

View File

@ -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 {

View File

@ -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.
@ -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
}