diff --git a/internal/testutils/args.go b/internal/testutils/args.go index 6ade22d0c2..baf20f1cc2 100644 --- a/internal/testutils/args.go +++ b/internal/testutils/args.go @@ -75,3 +75,9 @@ func SleepMultiplier() float64 { return sleepMultiplier } + +// IsCI returns whether the test is running in CI environment. +var IsCI = sync.OnceValue(func() bool { + _, ok := os.LookupEnv("GITHUB_ACTIONS") + return ok +}) diff --git a/internal/users/db/bbolt/testutils.go b/internal/users/db/bbolt/testutils.go index 5068c56d12..3ce2aacb11 100644 --- a/internal/users/db/bbolt/testutils.go +++ b/internal/users/db/bbolt/testutils.go @@ -4,6 +4,7 @@ package bbolt // They are not exported, and guarded by testing assertions. import ( + "encoding/json" "os" "path/filepath" "strings" @@ -14,14 +15,26 @@ import ( "gopkg.in/yaml.v3" ) -// We need to replace the current time by a deterministic time in the golden files to be able to compare them. -// We use the first second of the year 2020 as a recognizable value (which is not the zero value). -var redactedCurrentTime = "2020-01-01T00:00:00Z" +const ( + // We need to replace the current time by a deterministic time in the golden files to be able to compare them. + // We use the first second of the year 2020 as a recognizable value (which is not the zero value). + redactedCurrentTime = "2020-01-01T00:00:00Z" + + // User homes can have an invalid prefix that we can adjust during tests. + redactedUserHome = "@HOME_BASE@" +) // Z_ForTests_CreateDBFromYAML creates the database inside destDir and loads the src file content into it. // // nolint:revive,nolintlint // We want to use underscores in the function name here. func Z_ForTests_CreateDBFromYAML(src, destDir string) (err error) { + return Z_ForTests_CreateDBFromYAMLWithBaseHome(src, destDir, "") +} + +// Z_ForTests_CreateDBFromYAMLWithBaseHome creates the database inside destDir and loads the src file content into it. +// +// nolint:revive,nolintlint // We want to use underscores in the function name here. +func Z_ForTests_CreateDBFromYAMLWithBaseHome(src, destDir, baseHomeDir string) (err error) { testsdetection.MustBeTesting() src, err = filepath.Abs(src) @@ -64,7 +77,26 @@ func Z_ForTests_CreateDBFromYAML(src, destDir string) (err error) { if bucketName == userByIDBucketName || bucketName == userByNameBucketName { // Replace the redacted time in the json value by a valid time. val = strings.Replace(val, redactedCurrentTime, time.Now().Format(time.RFC3339), 1) + + if baseHomeDir != "" { + var u UserDB + if err := json.Unmarshal([]byte(val), &u); err != nil { + return err + } + + u.Dir = strings.ReplaceAll(u.Dir, redactedUserHome, baseHomeDir) + if err := os.MkdirAll(u.Dir, 0700); err != nil { + return err + } + + v, err := json.Marshal(u) + if err != nil { + return err + } + val = string(v) + } } + if err := bucket.Put([]byte(key), []byte(val)); err != nil { panic("programming error: put called in a RO transaction") } diff --git a/pam/integration-tests/helpers_test.go b/pam/integration-tests/helpers_test.go index 8db286e2da..9ab32d9235 100644 --- a/pam/integration-tests/helpers_test.go +++ b/pam/integration-tests/helpers_test.go @@ -564,7 +564,8 @@ func useOldDatabaseEnv(t *testing.T, oldDB string) []string { oldDBDir, err := os.MkdirTemp(tempDir, "old-db-path") require.NoError(t, err, "Cannot create db directory in %q", tempDir) - err = bbolt.Z_ForTests_CreateDBFromYAML(filepath.Join("testdata", "db", oldDB+".db.yaml"), oldDBDir) + err = bbolt.Z_ForTests_CreateDBFromYAMLWithBaseHome( + filepath.Join("testdata", "db", oldDB+".db.yaml"), oldDBDir, t.TempDir()) require.NoError(t, err, "Setup: creating old database") return []string{fmt.Sprintf("AUTHD_INTEGRATIONTESTS_OLD_DB_DIR=%s", oldDBDir)} diff --git a/pam/integration-tests/ssh_test.go b/pam/integration-tests/ssh_test.go index 480b361329..aa9c021066 100644 --- a/pam/integration-tests/ssh_test.go +++ b/pam/integration-tests/ssh_test.go @@ -74,6 +74,8 @@ func testSSHAuthenticate(t *testing.T, sharedSSHd bool) { execModule := buildExecModuleWithCFlags(t, []string{"-std=c11"}, true) execChild := buildPAMExecChild(t) + specialUserAcceptAll := "authd-test-user-sshd-accept-all" + mkHomeDirHelper, err := exec.LookPath("mkhomedir_helper") require.NoError(t, err, "Setup: mkhomedir_helper not found") pamMkHomeDirModule := buildCPAMModule(t, @@ -94,7 +96,8 @@ func testSSHAuthenticate(t *testing.T, sharedSSHd bool) { nssLibrary, nssEnv = testutils.BuildRustNSSLib(t, true) sshdPreloadLibraries = append(sshdPreloadLibraries, nssLibrary) sshdPreloaderCFlags = append(sshdPreloaderCFlags, - "-DAUTHD_TESTS_SSH_USE_AUTHD_NSS") + "-DAUTHD_TESTS_SSH_USE_AUTHD_NSS", + fmt.Sprintf("-DAUTHD_SPECIAL_USER_ACCEPT_ALL=%q", specialUserAcceptAll)) nssEnv = append(nssEnv, nssTestEnvBase(t, nssLibrary)...) } else if err != nil { t.Logf("Using the dummy library to implement NSS: %v", err) @@ -127,8 +130,9 @@ func testSSHAuthenticate(t *testing.T, sharedSSHd bool) { serviceFile := createSshdServiceFile(t, execModule, execChild, pamMkHomeDirModule, defaultSocketPath) sshdEnv = append(sshdEnv, nssEnv...) sshdEnv = append(sshdEnv, fmt.Sprintf("AUTHD_NSS_SOCKET=%s", defaultSocketPath)) - defaultSSHDPort, defaultUserHome = startSSHdForTest(t, serviceFile, sshdHostKey, - "authd-test-user-sshd-accept-all", sshdPreloadLibraries, sshdEnv, true, false) + defaultUserHome = expectedUserHome(t, specialUserAcceptAll) + defaultSSHDPort = startSSHdForTest(t, serviceFile, sshdHostKey, + defaultUserHome, specialUserAcceptAll, sshdPreloadLibraries, sshdEnv, true, false) } sshEnvVariablesRegex = regexp.MustCompile(`(?m) (PATH|HOME|PWD|SSH_[A-Z]+)=.*(\n*)($[^ ]{2}.*)?$`) @@ -146,6 +150,7 @@ func testSSHAuthenticate(t *testing.T, sharedSSHd bool) { socketPath string daemonizeSSHd bool interactiveShell bool + command []string oldDB string wantUserAlreadyExist bool @@ -163,6 +168,10 @@ func testSSHAuthenticate(t *testing.T, sharedSSHd bool) { tape: "simple_auth_with_shell", interactiveShell: true, }, + "Authenticate_user_successfully_launching_command": { + tape: "simple_auth", + command: []string{"true"}, + }, "Authenticate_user_successfully_with_upper_case": { tape: "simple_auth", user: strings.ToUpper(vhsTestUserNameFull(t, @@ -405,6 +414,9 @@ Wait@%dms`, sshDefaultFinalWaitTimeout), user = vhsTestUserNameFull(t, tc.userPrefix, "ssh") } + sshdPort := defaultSSHDPort + userHome := defaultUserHome + var userClient authd.UserServiceClient if tc.socketPath == "" { conn, err := grpc.NewClient("unix://"+socketPath, @@ -419,14 +431,17 @@ Wait@%dms`, sshDefaultFinalWaitTimeout), userClient = authd.NewUserServiceClient(conn) if tc.wantUserAlreadyExist { - requireAuthdUser(t, userClient, user) + authdUser := requireAuthdUser(t, userClient, user) + userHome = authdUser.Homedir } else { requireNoAuthdUser(t, userClient, user) } } - sshdPort := defaultSSHDPort - userHome := defaultUserHome + if userHome == "" { + userHome = expectedUserHome(t, user) + } + if !sharedSSHd || tc.wantLocalGroups || tc.oldDB != "" || tc.interactiveShell || tc.socketPath != "" { sshdEnv := sshdEnv @@ -441,13 +456,20 @@ Wait@%dms`, sshDefaultFinalWaitTimeout), } serviceFile := createSshdServiceFile(t, execModule, execChild, pamMkHomeDirModule, socketPath) - sshdPort, userHome = startSSHdForTest(t, serviceFile, sshdHostKey, user, - sshdPreloadLibraries, sshdEnv, tc.daemonizeSSHd, tc.interactiveShell) + sshdPort = startSSHdForTest(t, serviceFile, sshdHostKey, + userHome, user, sshdPreloadLibraries, sshdEnv, tc.daemonizeSSHd, + tc.interactiveShell) } if !sharedSSHd { _, err := os.Stat(userHome) - require.ErrorIs(t, err, os.ErrNotExist, "Unexpected error checking for %q", userHome) + if tc.wantUserAlreadyExist { + require.NoError(t, err, os.ErrNotExist, + "User home %q must already exist", userHome) + } else { + require.ErrorIs(t, err, os.ErrNotExist, + "Unexpected error checking for %q", userHome) + } } knownHost := filepath.Join(t.TempDir(), "known_hosts") @@ -456,13 +478,7 @@ Wait@%dms`, sshDefaultFinalWaitTimeout), ), 0600) require.NoError(t, err, "Setup: can't create known hosts file") - outDir := t.TempDir() - td := newTapeData(tc.tape, append(defaultTapeSettings, tc.tapeSettings...)...) - td.Command = tapeCommand - td.Env[pam_test.RunnerEnvSupportsConversation] = "1" - td.Env["HOME"] = t.TempDir() - td.Env[pamSSHUserEnv] = user - td.Env["AUTHD_PAM_SSH_ARGS"] = strings.Join([]string{ + sshArgs := []string{ "-p", sshdPort, "-F", os.DevNull, "-i", os.DevNull, @@ -470,7 +486,20 @@ Wait@%dms`, sshDefaultFinalWaitTimeout), "-o", "PasswordAuthentication=no", "-o", "PubkeyAuthentication=no", "-o", "UserKnownHostsFile=" + knownHost, - }, " ") + } + + if tc.interactiveShell { + require.Nil(t, tc.command, "Setup: Interactive shell and commands are incompatible") + } + sshArgs = append(sshArgs, tc.command...) + + outDir := t.TempDir() + td := newTapeData(tc.tape, append(defaultTapeSettings, tc.tapeSettings...)...) + td.Command = tapeCommand + td.Env[pam_test.RunnerEnvSupportsConversation] = "1" + td.Env["HOME"] = t.TempDir() + td.Env[pamSSHUserEnv] = user + td.Env["AUTHD_PAM_SSH_ARGS"] = strings.Join(sshArgs, " ") td.Variables = tc.tapeVariables td.RunVhs(t, vhsTestTypeSSH, outDir, nil) got := sanitizeGoldenFile(t, td, outDir) @@ -486,6 +515,9 @@ Wait@%dms`, sshDefaultFinalWaitTimeout), if nssLibrary != "" { requireGetEntExists(t, nssLibrary, socketPath, user, tc.isLocalUser) } + + _, err := os.Stat(userHome) + require.Error(t, err, "User %q home %q must not exist", user, userHome) } else { require.Contains(t, got, userEnv, "Logged in user does not matches") @@ -503,12 +535,10 @@ Wait@%dms`, sshDefaultFinalWaitTimeout), } } - if !tc.wantUserAlreadyExist { - // Check if user home has been created, but only if the user is a new one. - stat, err := os.Stat(userHome) - require.NoError(t, err, "Error checking for %q", userHome) - require.True(t, stat.IsDir(), "%q is not a directory", userHome) - } + // Check if user home has been created. + stat, err := os.Stat(userHome) + require.NoError(t, err, "Error checking for %q", userHome) + require.True(t, stat.IsDir(), "User %q home %q is not a directory", user, userHome) } if tc.wantLocalGroups || tc.oldDB != "" { @@ -590,12 +620,18 @@ func createSshdServiceFile(t *testing.T, module, execChild, mkHomeModule, socket return serviceFile } -func startSSHdForTest(t *testing.T, serviceFile, hostKey, user string, preloadLibraries []string, env []string, daemonize bool, interactiveShell bool) (string, string) { +func expectedUserHome(t *testing.T, userName string) string { + t.Helper() + + return filepath.Join(t.TempDir(), strings.ToLower(userName)) +} + +func startSSHdForTest(t *testing.T, serviceFile, hostKey, userHome, user string, preloadLibraries []string, env []string, daemonize bool, interactiveShell bool) string { t.Helper() sshdConnectCommand := fmt.Sprintf( - "/usr/bin/echo ' SSHD: Connected to ssh via authd module! [%s]'", - t.Name()) + "/usr/bin/sleep %.2f && /usr/bin/echo ' SSHD: Connected to ssh via authd module! [%s]'", + sleepDuration(1*time.Second).Seconds(), t.Name()) if daemonize { // When in daemon mode SSH doesn't show debug infos, so let's // handle this manually. @@ -605,17 +641,18 @@ func startSSHdForTest(t *testing.T, serviceFile, hostKey, user string, preloadLi sshdConnectCommand = "/bin/sh" } - homeBase := t.TempDir() - userHome := filepath.Join(homeBase, user) + require.NotEmpty(t, user, "Setup: User name is unset") + require.NotEmpty(t, userHome, "Setup: User HOME for %q is unset", user) + sshdPort := startSSHd(t, hostKey, sshdConnectCommand, append([]string{ - fmt.Sprintf("HOME=%s", homeBase), + fmt.Sprintf("HOME=%s", t.TempDir()), fmt.Sprintf("LD_PRELOAD=%s", strings.Join(preloadLibraries, ":")), fmt.Sprintf("AUTHD_TEST_SSH_USER=%s", user), fmt.Sprintf("AUTHD_TEST_SSH_HOME=%s", userHome), fmt.Sprintf("AUTHD_TEST_SSH_PAM_SERVICE=%s", serviceFile), }, env...), daemonize) - return sshdPort, userHome + return sshdPort } func sshdCommand(t *testing.T, port, hostKey, forcedCommand string, env []string, daemonize bool) (*exec.Cmd, string, string) { @@ -674,7 +711,9 @@ func startSSHd(t *testing.T, hostKey, forcedCommand string, env []string, daemon sshd, sshdPidFile, sshdLogFile := sshdCommand(t, sshdPort, hostKey, forcedCommand, env, daemonize) sshdStderr := bytes.Buffer{} sshd.Stderr = &sshdStderr - if testing.Verbose() { + + isCIVerbose := testutils.IsCI() && testing.Verbose() + if isCIVerbose { sshd.Stdout = os.Stdout sshd.Stderr = os.Stderr } @@ -685,9 +724,14 @@ func startSSHd(t *testing.T, hostKey, forcedCommand string, env []string, daemon sshdPid := sshd.Process.Pid t.Cleanup(func() { - if testing.Verbose() || !t.Failed() { + if isCIVerbose || !t.Failed() { return } + + if testing.Verbose() { + t.Logf("SSHd log:\n%s", sshdStderr.Bytes()) + } + sshdLog := filepath.Join(t.TempDir(), "sshd.log") require.NoError(t, os.WriteFile(sshdLog, sshdStderr.Bytes(), 0600), "TearDown: Saving sshd log") diff --git a/pam/integration-tests/sshd_preloader/sshd_preloader.c b/pam/integration-tests/sshd_preloader/sshd_preloader.c index 37dcb69e4b..966f93d57b 100644 --- a/pam/integration-tests/sshd_preloader/sshd_preloader.c +++ b/pam/integration-tests/sshd_preloader/sshd_preloader.c @@ -18,7 +18,6 @@ #define AUTHD_TEST_SHELL "/bin/sh" #define AUTHD_TEST_GECOS "" #define AUTHD_DEFAULT_SSH_PAM_SERVICE_NAME "sshd" -#define AUTHD_SPECIAL_USER_ACCEPT_ALL "authd-test-user-sshd-accept-all" #define SIZE_OF_ARRAY(a) (sizeof ((a)) / sizeof (*(a))) diff --git a/pam/integration-tests/testdata/db/authd_0.4.1_bbolt_with_mixed_case_users.db.yaml b/pam/integration-tests/testdata/db/authd_0.4.1_bbolt_with_mixed_case_users.db.yaml index 568fc1bedb..70c4168212 100644 --- a/pam/integration-tests/testdata/db/authd_0.4.1_bbolt_with_mixed_case_users.db.yaml +++ b/pam/integration-tests/testdata/db/authd_0.4.1_bbolt_with_mixed_case_users.db.yaml @@ -68,23 +68,23 @@ GroupToUsers: "1911746559": '{"GID":1911746559,"UIDs":[1911746559]}' "1047623009": '{"GID":1047623009,"UIDs":[1047623009]}' UserByID: - "1031497592": '{"Name":"user-integration-WITH-Mixed-CaSe","UID":1031497592,"GID":1031497592,"Gecos":"gecos for user-integration-WITH-Mixed-CaSe","Dir":"/home/user-integration-WITH-Mixed-CaSe","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:17:18.01191855+02:00"}' - "1047623009": '{"Name":"user-local-groups-Mixed-Case","UID":1047623009,"GID":1047623009,"Gecos":"gecos for user-local-groups-Mixed-Case","Dir":"/home/user-local-groups-Mixed-Case","Shell":"/usr/bin/bash","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-06-02T01:05:19.742290239+02:00"}' - "1448654198": '{"Name":"user-integration-cached","UID":1448654198,"GID":1448654198,"Gecos":"gecos for user-integration-cached","Dir":"/home/user-integration-cached","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:16:55.049670042+02:00"}' - "1668487632": '{"Name":"user-integration-pre-check-WITH-UPPER-CASE","UID":1668487632,"GID":1668487632,"Gecos":"gecos for user-integration-pre-check-WITH-UPPER-CASE","Dir":"/home/user-integration-pre-check-WITH-UPPER-CASE","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:26:39.600082108+02:00"}' - "1674497680": '{"Name":"user-local-groups-UPPER-CASE","UID":1674497680,"GID":1674497680,"Gecos":"gecos for user-local-groups-UPPER-CASE","Dir":"/home/user-local-groups-UPPER-CASE","Shell":"/usr/bin/bash","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-06-02T01:08:33.759143141+02:00"}' - "1690031958": '{"Name":"user-local-groups-lower-case","UID":1690031958,"GID":1690031958,"Gecos":"gecos for user-local-groups-lower-case","Dir":"/home/user-local-groups-lower-case","Shell":"/usr/bin/bash","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-06-02T01:08:07.712846646+02:00"}' - "1911746559": '{"Name":"user-integration-UPPER-CASE","UID":1911746559,"GID":1911746559,"Gecos":"gecos for user-integration-UPPER-CASE","Dir":"/home/user-integration-UPPER-CASE","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:20:04.201035581+02:00"}' - "1974679960": '{"Name":"user-pre-check","UID":1974679960,"GID":1974679960,"Gecos":"gecos for user-pre-check","Dir":"/home/user-pre-check","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:24:42.349318925+02:00"}' + "1031497592": '{"Name":"user-integration-WITH-Mixed-CaSe","UID":1031497592,"GID":1031497592,"Gecos":"gecos for user-integration-WITH-Mixed-CaSe","Dir":"@HOME_BASE@/user-integration-WITH-Mixed-CaSe","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:17:18.01191855+02:00"}' + "1047623009": '{"Name":"user-local-groups-Mixed-Case","UID":1047623009,"GID":1047623009,"Gecos":"gecos for user-local-groups-Mixed-Case","Dir":"@HOME_BASE@/user-local-groups-Mixed-Case","Shell":"/usr/bin/bash","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-06-02T01:05:19.742290239+02:00"}' + "1448654198": '{"Name":"user-integration-cached","UID":1448654198,"GID":1448654198,"Gecos":"gecos for user-integration-cached","Dir":"@HOME_BASE@/user-integration-cached","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:16:55.049670042+02:00"}' + "1668487632": '{"Name":"user-integration-pre-check-WITH-UPPER-CASE","UID":1668487632,"GID":1668487632,"Gecos":"gecos for user-integration-pre-check-WITH-UPPER-CASE","Dir":"@HOME_BASE@/user-integration-pre-check-WITH-UPPER-CASE","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:26:39.600082108+02:00"}' + "1674497680": '{"Name":"user-local-groups-UPPER-CASE","UID":1674497680,"GID":1674497680,"Gecos":"gecos for user-local-groups-UPPER-CASE","Dir":"@HOME_BASE@/user-local-groups-UPPER-CASE","Shell":"/usr/bin/bash","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-06-02T01:08:33.759143141+02:00"}' + "1690031958": '{"Name":"user-local-groups-lower-case","UID":1690031958,"GID":1690031958,"Gecos":"gecos for user-local-groups-lower-case","Dir":"@HOME_BASE@/user-local-groups-lower-case","Shell":"/usr/bin/bash","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-06-02T01:08:07.712846646+02:00"}' + "1911746559": '{"Name":"user-integration-UPPER-CASE","UID":1911746559,"GID":1911746559,"Gecos":"gecos for user-integration-UPPER-CASE","Dir":"@HOME_BASE@/user-integration-UPPER-CASE","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:20:04.201035581+02:00"}' + "1974679960": '{"Name":"user-pre-check","UID":1974679960,"GID":1974679960,"Gecos":"gecos for user-pre-check","Dir":"@HOME_BASE@/user-pre-check","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:24:42.349318925+02:00"}' UserByName: - user-integration-UPPER-CASE: '{"Name":"user-integration-UPPER-CASE","UID":1911746559,"GID":1911746559,"Gecos":"gecos for user-integration-UPPER-CASE","Dir":"/home/user-integration-UPPER-CASE","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:20:04.201035581+02:00"}' - user-integration-WITH-Mixed-CaSe: '{"Name":"user-integration-WITH-Mixed-CaSe","UID":1031497592,"GID":1031497592,"Gecos":"gecos for user-integration-WITH-Mixed-CaSe","Dir":"/home/user-integration-WITH-Mixed-CaSe","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:17:18.01191855+02:00"}' - user-integration-cached: '{"Name":"user-integration-cached","UID":1448654198,"GID":1448654198,"Gecos":"gecos for user-integration-cached","Dir":"/home/user-integration-cached","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:16:55.049670042+02:00"}' - user-integration-pre-check-WITH-UPPER-CASE: '{"Name":"user-integration-pre-check-WITH-UPPER-CASE","UID":1668487632,"GID":1668487632,"Gecos":"gecos for user-integration-pre-check-WITH-UPPER-CASE","Dir":"/home/user-integration-pre-check-WITH-UPPER-CASE","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:26:39.600082108+02:00"}' - user-local-groups-Mixed-Case: '{"Name":"user-local-groups-Mixed-Case","UID":1047623009,"GID":1047623009,"Gecos":"gecos for user-local-groups-Mixed-Case","Dir":"/home/user-local-groups-Mixed-Case","Shell":"/usr/bin/bash","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-06-02T01:05:19.742290239+02:00"}' - user-local-groups-UPPER-CASE: '{"Name":"user-local-groups-UPPER-CASE","UID":1674497680,"GID":1674497680,"Gecos":"gecos for user-local-groups-UPPER-CASE","Dir":"/home/user-local-groups-UPPER-CASE","Shell":"/usr/bin/bash","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-06-02T01:08:33.759143141+02:00"}' - user-local-groups-lower-case: '{"Name":"user-local-groups-lower-case","UID":1690031958,"GID":1690031958,"Gecos":"gecos for user-local-groups-lower-case","Dir":"/home/user-local-groups-lower-case","Shell":"/usr/bin/bash","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-06-02T01:08:07.712846646+02:00"}' - user-pre-check: '{"Name":"user-pre-check","UID":1974679960,"GID":1974679960,"Gecos":"gecos for user-pre-check","Dir":"/home/user-pre-check","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:24:42.349318925+02:00"}' + user-integration-UPPER-CASE: '{"Name":"user-integration-UPPER-CASE","UID":1911746559,"GID":1911746559,"Gecos":"gecos for user-integration-UPPER-CASE","Dir":"@HOME_BASE@/user-integration-UPPER-CASE","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:20:04.201035581+02:00"}' + user-integration-WITH-Mixed-CaSe: '{"Name":"user-integration-WITH-Mixed-CaSe","UID":1031497592,"GID":1031497592,"Gecos":"gecos for user-integration-WITH-Mixed-CaSe","Dir":"@HOME_BASE@/user-integration-WITH-Mixed-CaSe","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:17:18.01191855+02:00"}' + user-integration-cached: '{"Name":"user-integration-cached","UID":1448654198,"GID":1448654198,"Gecos":"gecos for user-integration-cached","Dir":"@HOME_BASE@/user-integration-cached","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:16:55.049670042+02:00"}' + user-integration-pre-check-WITH-UPPER-CASE: '{"Name":"user-integration-pre-check-WITH-UPPER-CASE","UID":1668487632,"GID":1668487632,"Gecos":"gecos for user-integration-pre-check-WITH-UPPER-CASE","Dir":"@HOME_BASE@/user-integration-pre-check-WITH-UPPER-CASE","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:26:39.600082108+02:00"}' + user-local-groups-Mixed-Case: '{"Name":"user-local-groups-Mixed-Case","UID":1047623009,"GID":1047623009,"Gecos":"gecos for user-local-groups-Mixed-Case","Dir":"@HOME_BASE@/user-local-groups-Mixed-Case","Shell":"/usr/bin/bash","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-06-02T01:05:19.742290239+02:00"}' + user-local-groups-UPPER-CASE: '{"Name":"user-local-groups-UPPER-CASE","UID":1674497680,"GID":1674497680,"Gecos":"gecos for user-local-groups-UPPER-CASE","Dir":"@HOME_BASE@/user-local-groups-UPPER-CASE","Shell":"/usr/bin/bash","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-06-02T01:08:33.759143141+02:00"}' + user-local-groups-lower-case: '{"Name":"user-local-groups-lower-case","UID":1690031958,"GID":1690031958,"Gecos":"gecos for user-local-groups-lower-case","Dir":"@HOME_BASE@/user-local-groups-lower-case","Shell":"/usr/bin/bash","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-06-02T01:08:07.712846646+02:00"}' + user-pre-check: '{"Name":"user-pre-check","UID":1974679960,"GID":1974679960,"Gecos":"gecos for user-pre-check","Dir":"@HOME_BASE@/user-pre-check","Shell":"/bin/sh","LastPwdChange":-1,"MaxPwdAge":-1,"PwdWarnPeriod":-1,"PwdInactivity":-1,"MinPwdAge":-1,"ExpirationDate":-1,"LastLogin":"2025-05-30T18:24:42.349318925+02:00"}' UserToBroker: "1031497592": '"2221040704"' "1047623009": '"2221040704"' diff --git a/pam/integration-tests/testdata/golden/TestSSHAuthenticate/Authenticate_user_successfully_launching_command b/pam/integration-tests/testdata/golden/TestSSHAuthenticate/Authenticate_user_successfully_launching_command new file mode 100644 index 0000000000..00c489b7a2 --- /dev/null +++ b/pam/integration-tests/testdata/golden/TestSSHAuthenticate/Authenticate_user_successfully_launching_command @@ -0,0 +1,49 @@ +> ssh ${AUTHD_PAM_SSH_USER}@localhost ${AUTHD_PAM_SSH_ARGS} +== Provider selection == + 1. local + 2. ExampleBroker +(user-integration-pre-check-ssh-authenticate-user-successfully-launching-command@localhost) Choose your provider: +> +──────────────────────────────────────────────────────────────────────────────── +> ssh ${AUTHD_PAM_SSH_USER}@localhost ${AUTHD_PAM_SSH_ARGS} +== Provider selection == + 1. local + 2. ExampleBroker +(user-integration-pre-check-ssh-authenticate-user-successfully-launching-command@localhost) Choose your provider: +> 2 +──────────────────────────────────────────────────────────────────────────────── +> ssh ${AUTHD_PAM_SSH_USER}@localhost ${AUTHD_PAM_SSH_ARGS} +== Provider selection == + 1. local + 2. ExampleBroker +(user-integration-pre-check-ssh-authenticate-user-successfully-launching-command@localhost) Choose your provider: +> 2 +== Password authentication == +Enter 'r' to cancel the request and go back to select the authentication method +(user-integration-pre-check-ssh-authenticate-user-successfully-launching-command@localhost) Gimme your password: +> +──────────────────────────────────────────────────────────────────────────────── +> ssh ${AUTHD_PAM_SSH_USER}@localhost ${AUTHD_PAM_SSH_ARGS} +== Provider selection == + 1. local + 2. ExampleBroker +(user-integration-pre-check-ssh-authenticate-user-successfully-launching-command@localhost) Choose your provider: +> 2 +== Password authentication == +Enter 'r' to cancel the request and go back to select the authentication method +(user-integration-pre-check-ssh-authenticate-user-successfully-launching-command@localhost) Gimme your password: +> +PAM Authenticate() finished for user 'user-integration-pre-check-ssh-authenticate-user-successfully-launching-command' +PAM AcctMgmt() finished for user 'user-integration-pre-check-ssh-authenticate-user-successfully-launching-command' +Environment: + USER=user-integration-pre-check-ssh-authenticate-user-successfully-launching-command + LOGNAME=user-integration-pre-check-ssh-authenticate-user-successfully-launching-command + HOME=${AUTHD_TEST_HOME} + PATH=${AUTHD_TEST_PATH} + SHELL=/bin/sh + SSH_CLIENT=${AUTHD_TEST_SSH_CLIENT} + SSH_CONNECTION=${AUTHD_TEST_SSH_CONNECTION} + SSH_ORIGINAL_COMMAND=true + SSHD: Connected to ssh via authd module! [TestSSHAuthenticate/Authenticate_user_successfully_launching_command] +> +──────────────────────────────────────────────────────────────────────────────── diff --git a/pam/integration-tests/testdata/golden/TestSSHAuthenticate/Authenticate_user_successfully_launching_command_on_shared_SSHd b/pam/integration-tests/testdata/golden/TestSSHAuthenticate/Authenticate_user_successfully_launching_command_on_shared_SSHd new file mode 100644 index 0000000000..8b4fbe3471 --- /dev/null +++ b/pam/integration-tests/testdata/golden/TestSSHAuthenticate/Authenticate_user_successfully_launching_command_on_shared_SSHd @@ -0,0 +1,49 @@ +> ssh ${AUTHD_PAM_SSH_USER}@localhost ${AUTHD_PAM_SSH_ARGS} +== Provider selection == + 1. local + 2. ExampleBroker +(user-integration-pre-check-ssh-authenticate-user-successfully-launching-command-on-shared-sshd@localhost) Choose your provider: +> +──────────────────────────────────────────────────────────────────────────────── +> ssh ${AUTHD_PAM_SSH_USER}@localhost ${AUTHD_PAM_SSH_ARGS} +== Provider selection == + 1. local + 2. ExampleBroker +(user-integration-pre-check-ssh-authenticate-user-successfully-launching-command-on-shared-sshd@localhost) Choose your provider: +> 2 +──────────────────────────────────────────────────────────────────────────────── +> ssh ${AUTHD_PAM_SSH_USER}@localhost ${AUTHD_PAM_SSH_ARGS} +== Provider selection == + 1. local + 2. ExampleBroker +(user-integration-pre-check-ssh-authenticate-user-successfully-launching-command-on-shared-sshd@localhost) Choose your provider: +> 2 +== Password authentication == +Enter 'r' to cancel the request and go back to select the authentication method +(user-integration-pre-check-ssh-authenticate-user-successfully-launching-command-on-shared-sshd@localhost) Gimme your password: +> +──────────────────────────────────────────────────────────────────────────────── +> ssh ${AUTHD_PAM_SSH_USER}@localhost ${AUTHD_PAM_SSH_ARGS} +== Provider selection == + 1. local + 2. ExampleBroker +(user-integration-pre-check-ssh-authenticate-user-successfully-launching-command-on-shared-sshd@localhost) Choose your provider: +> 2 +== Password authentication == +Enter 'r' to cancel the request and go back to select the authentication method +(user-integration-pre-check-ssh-authenticate-user-successfully-launching-command-on-shared-sshd@localhost) Gimme your password: +> +PAM Authenticate() finished for user 'user-integration-pre-check-ssh-authenticate-user-successfully-launching-command-on-shared-sshd' +PAM AcctMgmt() finished for user 'user-integration-pre-check-ssh-authenticate-user-successfully-launching-command-on-shared-sshd' + SSHD: Connected to ssh via authd module! [TestSSHAuthenticate] + HOME=${AUTHD_TEST_HOME} + LOGNAME=user-integration-pre-check-ssh-authenticate-user-successfully-launching-command-on-shared-sshd + PATH=${AUTHD_TEST_PATH} + PWD=${AUTHD_TEST_PWD} + SHELL=/bin/sh + SSH_CLIENT=${AUTHD_TEST_SSH_CLIENT} + SSH_CONNECTION=${AUTHD_TEST_SSH_CONNECTION} + SSH_ORIGINAL_COMMAND=true + USER=user-integration-pre-check-ssh-authenticate-user-successfully-launching-command-on-shared-sshd +> +──────────────────────────────────────────────────────────────────────────────── diff --git a/pam/internal/adapter/gdmmodel_test.go b/pam/internal/adapter/gdmmodel_test.go index ede22f98bd..ada230b00d 100644 --- a/pam/internal/adapter/gdmmodel_test.go +++ b/pam/internal/adapter/gdmmodel_test.go @@ -2551,6 +2551,9 @@ func TestGdmModel(t *testing.T) { if tc.timeout == 0 { tc.timeout = 10 * time.Second } + if testutils.IsCI() { + tc.timeout *= 3 + } waitChan := make(chan struct{}) go func() { wg.Wait()