diff --git a/pam/integration-tests/gdm_test.go b/pam/integration-tests/gdm_test.go index 92a21e7c7..263f50bc9 100644 --- a/pam/integration-tests/gdm_test.go +++ b/pam/integration-tests/gdm_test.go @@ -348,6 +348,12 @@ func testGdmModule(t *testing.T, libPath string, args []string) { require.ErrorIs(t, gh.tx.AcctMgmt(pamFlags), tc.wantAcctMgmtErr, "Account Management PAM Error messages do not match") + if tc.wantAcctMgmtErr == nil { + // Ensure that we ignore a second request to it. + require.ErrorIs(t, gh.tx.AcctMgmt(pamFlags), pam_test.ErrIgnore, + "Account Management PAM Error messages do not match") + } + if tc.wantError != nil { requirePreviousBrokerForUser(t, socketPath, "", tc.pamUser) return @@ -502,6 +508,10 @@ func testGdmModuleAcctMgmtWithoutGdmExtension(t *testing.T, libPath string, modu require.ErrorIs(t, gh.tx.AcctMgmt(pamFlags), pam_test.ErrIgnore, "Account Management PAM Error message do not match") requirePreviousBrokerForUser(t, socketPath, "", pamUser) + + require.ErrorIs(t, gh.tx.AcctMgmt(pamFlags), pam_test.ErrIgnore, + "Account Management PAM Error message do not match") + requirePreviousBrokerForUser(t, socketPath, "", pamUser) } func TestGdmModuleAcctMgmtWithoutGdmExtension(t *testing.T) { diff --git a/pam/pam.go b/pam/pam.go index 6284648a3..2d6f39842 100644 --- a/pam/pam.go +++ b/pam/pam.go @@ -43,6 +43,11 @@ const ( // do this again. alreadyAuthenticatedKey = "authd.already-authenticated-flag" + // alreadyAcctMgmt is the Key used to store in the library that + // we've already performed account management with this module and so that + // there's no need to do this again. + alreadyAcctMgmt = "authd.already-acct-mgmt-flag" + // gdmServiceName is the name of the service that is loaded by GDM. // Keep this in sync with the service file installed by the package. gdmServiceName = "gdm-authd" @@ -54,6 +59,7 @@ var supportedArgs = []string{ "disable_journal", // Disable logging on systemd journal (this is implicit when `logfile` is set). "socket", // The authd socket to connect to. "force_reauth", // Whether the authentication should be performed again even if it has been already completed. + "force_reaccount", // Whether the account management should be performed again even if it has been already completed. } // parseArgs parses the PAM arguments and returns a map of them and a function that logs the parsing issues. @@ -336,6 +342,14 @@ func (h *pamModule) AcctMgmt(mTx pam.ModuleTransaction, flags pam.Flags, args [] } logArgsIssues() + alreadyDone, err := mTx.GetData(alreadyAcctMgmt) + if alreadyDone != nil && err == nil && parsedArgs["force_reaccount"] != "true" { + return pam.ErrIgnore + } + if err != nil && !errors.Is(err, pam.ErrNoModuleData) { + return err + } + // We ignore AcctMgmt in case we're loading the module through the exec client serviceName, err := mTx.GetItem(pam.Service) if err != nil { @@ -405,6 +419,10 @@ func (h *pamModule) AcctMgmt(mTx pam.ModuleTransaction, flags pam.Flags, args [] return pam.ErrIgnore } + if err := mTx.SetData(alreadyAcctMgmt, true); err != nil { + return err + } + return nil }