Skip to content

Commit

Permalink
Update verification logic; Add test.
Browse files Browse the repository at this point in the history
  • Loading branch information
Joerger committed Oct 21, 2024
1 parent 94668b4 commit ce9dea5
Show file tree
Hide file tree
Showing 3 changed files with 766 additions and 6 deletions.
4 changes: 4 additions & 0 deletions lib/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -6856,6 +6856,10 @@ func (a *Server) ValidateMFAAuthResponse(
user string,
requiredExtensions *mfav1.ChallengeExtensions,
) (*authz.MFAAuthData, error) {
if requiredExtensions == nil {
return nil, trace.BadParameter("required challenge extensions parameter required")
}

authData, validateErr := a.validateMFAAuthResponseInternal(ctx, resp, user, requiredExtensions)
// validateErr handled after audit.

Expand Down
19 changes: 13 additions & 6 deletions lib/auth/sso_mfa.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package auth

import (
"context"
"crypto/subtle"

"github.com/gravitational/trace"

Expand Down Expand Up @@ -74,32 +75,38 @@ func (a *Server) beginSSOMFAChallenge(ctx context.Context, user string, sso *typ
// for the user and session ID. It also checks the required extensions, and finishes by deleting
// the MFA session if reuse is not allowed.
func (a *Server) verifySSOMFASession(ctx context.Context, username, sessionID, token string, requiredExtensions *mfav1.ChallengeExtensions) (*authz.MFAAuthData, error) {
if requiredExtensions == nil {
return nil, trace.BadParameter("requested challenge extensions must be supplied.")
}

const notFoundErrMsg = "mfa sso session data not found"
mfaSess, err := a.GetSSOMFASessionData(ctx, sessionID)
if trace.IsNotFound(err) {
return nil, trace.AccessDenied("mfa sso session data not found")
return nil, trace.AccessDenied(notFoundErrMsg)
} else if err != nil {
return nil, trace.Wrap(err)
}

// Verify the user's name and sso device matches.
if mfaSess.Username != username {
return nil, trace.AccessDenied("mfa sso session data not found")
return nil, trace.AccessDenied(notFoundErrMsg)
}

// Check if the MFA session matches the user's SSO MFA settings.
devs, err := a.Services.GetMFADevices(ctx, username, false /* withSecrets */)
if err != nil {
return nil, trace.Wrap(err)
}

groupedDevs := groupByDeviceType(devs)
if groupedDevs.SSO == nil {
return nil, trace.BadParameter("invalid sso mfa session data; non-sso user")
return nil, trace.AccessDenied("invalid sso mfa session data; non-sso user")
} else if groupedDevs.SSO.GetSso().ConnectorId != mfaSess.ConnectorID || groupedDevs.SSO.GetSso().ConnectorType != mfaSess.ConnectorType {
return nil, trace.BadParameter("invalid sso mfa session data; mismatched sso auth connector")
return nil, trace.AccessDenied("invalid sso mfa session data; mismatched sso auth connector")
}

// Verify the token matches.
if mfaSess.Token != token {
if subtle.ConstantTimeCompare([]byte(mfaSess.Token), []byte(token)) == 0 {
return nil, trace.AccessDenied("invalid SSO MFA challenge response")
}

Expand All @@ -108,7 +115,7 @@ func (a *Server) verifySSOMFASession(ctx context.Context, username, sessionID, t
return nil, trace.AccessDenied("required scope %q is not satisfied by the given sso mfa session with scope %q", requiredExtensions.Scope, mfaSess.ChallengeExtensions.Scope)
}

// If this session is reusable, but this login forbids reusable sessions, return an error.
// If this session is reusable, but this context forbids reusable sessions, return an error.
if requiredExtensions.AllowReuse == mfav1.ChallengeAllowReuse_CHALLENGE_ALLOW_REUSE_NO && mfaSess.ChallengeExtensions.AllowReuse == mfav1.ChallengeAllowReuse_CHALLENGE_ALLOW_REUSE_YES {
return nil, trace.AccessDenied("the given sso mfa session allows reuse, but reuse is not permitted in this context")
}
Expand Down
Loading

0 comments on commit ce9dea5

Please sign in to comment.