Skip to content

Commit

Permalink
feat(pam/gdmmodel): Add support for password quality check (#423)
Browse files Browse the repository at this point in the history
Support checking for password quality when logging-in with GDM, due to
the nature of GDM model, we can only provide the final check after the
user as typed the password twice, but that's still better than not
having the check at all.

To keep the GDM model agnostic to the change, handle this only in the
`authentication` backend, so that in the gdm case it's also doing an
extra step to check the challenge before passing the data to the broker.

Add tests to handle the newpassword layout in gdm, since we had not them
yet.

UDENG-3374
  • Loading branch information
3v1n0 authored Jul 10, 2024
2 parents 66d74ce + 03304bd commit a5269fe
Show file tree
Hide file tree
Showing 11 changed files with 1,609 additions and 60 deletions.
17 changes: 15 additions & 2 deletions examplebroker/broker.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,12 @@ func (b *Broker) NewSession(ctx context.Context, username, lang, mode string) (s
info.neededAuthSteps = 3
}

if _, ok := exampleUsers[username]; !ok && strings.HasPrefix(username, "user-needs-reset-integration") {
exampleUsers[username] = userInfoBroker{Password: "goodpass"}
info.neededAuthSteps = 2
info.pwdChange = mustReset
}

pubASN1, err := x509.MarshalPKIXPublicKey(&b.privateKey.PublicKey)
if err != nil {
return "", "", err
Expand Down Expand Up @@ -623,8 +629,15 @@ func (b *Broker) handleIsAuthenticated(ctx context.Context, sessionInfo sessionI
exampleUsersMu.Lock()
defer exampleUsersMu.Unlock()

if challenge != "authd2404" {
return AuthRetry, `{"message": "new password does not match criteria: must be authd2404"}`, nil
expectedChallenge := "authd2404"
// Reset the password to default if it had already been changed.
// As at PAM level we'd refuse a previous password to be re-used.
if exampleUsers[sessionInfo.username].Password == expectedChallenge {
expectedChallenge = "goodpass"
}

if challenge != expectedChallenge {
return AuthRetry, fmt.Sprintf(`{"message": "new password does not match criteria: must be '%s'"}`, expectedChallenge), nil
}
exampleUsers[sessionInfo.username] = userInfoBroker{Password: challenge}
}
Expand Down
76 changes: 72 additions & 4 deletions pam/integration-tests/gdm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ const (
exampleBrokerName = "ExampleBroker"
ignoredBrokerName = "<ignored-broker>"

passwordAuthID = "password"
fido1AuthID = "fidodevice1"
phoneAck1ID = "phoneack1"
qrcodeID = "qrcodewithtypo"
passwordAuthID = "password"
newPasswordAuthID = "mandatoryreset"
fido1AuthID = "fidodevice1"
phoneAck1ID = "phoneack1"
qrcodeID = "qrcodewithtypo"
)

var testPasswordUILayout = authd.UILayout{
Expand All @@ -50,6 +51,16 @@ var testPasswordUILayout = authd.UILayout{
Wait: ptrValue(""),
}

var testNewPasswordUILayout = authd.UILayout{
Type: "newpassword",
Label: ptrValue("Enter your new password"),
Entry: ptrValue("chars_password"),
Button: ptrValue(""),
Code: ptrValue(""),
Content: ptrValue(""),
Wait: ptrValue(""),
}

var testQrcodeUILayout = authd.UILayout{
Type: "qrcode",
Label: ptrValue("Enter the following code after flashing the address: 1337"),
Expand Down Expand Up @@ -166,6 +177,63 @@ func TestGdmModule(t *testing.T) {
},
},
},
"Authenticates after password change": {
pamUser: ptrValue("user-needs-reset-integration-gdm-pass"),
wantAuthModeIDs: []string{passwordAuthID, newPasswordAuthID},
supportedLayouts: []*authd.UILayout{
pam_test.FormUILayout(),
pam_test.NewPasswordUILayout(),
},
eventPollResponses: map[gdm.EventType][]*gdm.EventData{
gdm.EventType_startAuthentication: {
gdm_test.IsAuthenticatedEvent(&authd.IARequest_AuthenticationData_Challenge{
Challenge: "goodpass",
}),
gdm_test.IsAuthenticatedEvent(&authd.IARequest_AuthenticationData_Challenge{
Challenge: "authd2404",
}),
},
},
wantUILayouts: []*authd.UILayout{&testPasswordUILayout, &testNewPasswordUILayout},
},
"Authenticates after various invalid password changes": {
pamUser: ptrValue("user-needs-reset-integration-gdm-retries"),
wantAuthModeIDs: []string{
passwordAuthID,
newPasswordAuthID,
newPasswordAuthID,
newPasswordAuthID,
newPasswordAuthID,
newPasswordAuthID,
},
supportedLayouts: []*authd.UILayout{
pam_test.FormUILayout(),
pam_test.NewPasswordUILayout(),
},
eventPollResponses: map[gdm.EventType][]*gdm.EventData{
gdm.EventType_startAuthentication: {
gdm_test.IsAuthenticatedEvent(&authd.IARequest_AuthenticationData_Challenge{
Challenge: "goodpass",
}),
gdm_test.IsAuthenticatedEvent(&authd.IARequest_AuthenticationData_Challenge{
Challenge: "authd",
}),
gdm_test.IsAuthenticatedEvent(&authd.IARequest_AuthenticationData_Challenge{
Challenge: "goodpass",
}),
gdm_test.IsAuthenticatedEvent(&authd.IARequest_AuthenticationData_Challenge{
Challenge: "foolinux",
}),
gdm_test.IsAuthenticatedEvent(&authd.IARequest_AuthenticationData_Challenge{
Challenge: "newpass",
}),
gdm_test.IsAuthenticatedEvent(&authd.IARequest_AuthenticationData_Challenge{
Challenge: "authd2404",
}),
},
},
wantUILayouts: []*authd.UILayout{&testPasswordUILayout, &testNewPasswordUILayout},
},
"Authenticates user with qrcode": {
wantAuthModeIDs: []string{qrcodeID},
supportedLayouts: []*authd.UILayout{pam_test.QrCodeUILayout()},
Expand Down
Loading

0 comments on commit a5269fe

Please sign in to comment.