From 06cdfd27a47748b5d74b9e5d475e1640019ede3b Mon Sep 17 00:00:00 2001 From: "Evgeny Grin (Karlson2k)" Date: Mon, 14 Jul 2025 20:57:03 +0200 Subject: [PATCH] lib/, src/: Fix utmp update: use initial PID The correct utmp update functionality was broken mainly by commit 91fc51387ca5341e1a1f778a967886c5fe589cb8, which moved update of utmp after forking (when PAM is used). It was also misinterpretation of GNU/Linux utmp specifications, where is specified that ut_pid must be the PID of the **login** process (not a PID of any forked process). Wrong ut_pid also prevents utmp cleanup, which performed by init process and should clean entry with the same ut_pid as started login process. GNU/Linux description of utmp updates can be found at the next url: https://man7.org/linux/man-pages/man5/utmp.5.html Signed-off-by: Evgeny Grin (Karlson2k) --- lib/logind.c | 4 ++-- lib/prototypes.h | 14 +++++++++++--- lib/utmp.c | 28 +++++++++++++++------------- src/login.c | 8 +++++--- 4 files changed, 33 insertions(+), 21 deletions(-) diff --git a/lib/logind.c b/lib/logind.c index 46ce32196..f4b187e5b 100644 --- a/lib/logind.c +++ b/lib/logind.c @@ -14,13 +14,13 @@ #include -int get_session_host (char **out) +int get_session_host (char **out, pid_t main_pid) { char *host = NULL; char *session = NULL; int ret; - ret = sd_pid_get_session (getpid(), &session); + ret = sd_pid_get_session(main_pid, &session); if (ret < 0) { return ret; } diff --git a/lib/prototypes.h b/lib/prototypes.h index b904121c8..4063ae164 100644 --- a/lib/prototypes.h +++ b/lib/prototypes.h @@ -468,11 +468,13 @@ extern int user_busy (const char *name, uid_t uid); * @brief Get host for the current session * * @param[out] out Host name + * @param[in] main_pid the PID of the main process (the parent PID if + * the process forked itself) * * @return 0 or a positive integer if the host was obtained properly, * another value on error. */ -extern int get_session_host (char **out); +extern int get_session_host (char **out, pid_t main_pid); #ifndef ENABLE_LOGIND /** * @brief Update or create an utmp entry in utmp, wtmp, utmpw, or wtmpx @@ -480,24 +482,30 @@ extern int get_session_host (char **out); * @param[in] user username * @param[in] tty tty * @param[in] host hostname + * @param[in] main_pid the PID of the main process (the parent PID if + * the process forked itself) * * @return 0 if utmp was updated properly, * 1 on error. */ extern int update_utmp (const char *user, const char *tty, - const char *host); + const char *host, + pid_t main_pid); /** * @brief Update the cumulative failure log * * @param[in] failent_user username * @param[in] tty tty * @param[in] host hostname + * @param[in] main_pid the PID of the main process (the parent PID if + * the process forked itself) * */ extern void record_failure(const char *failent_user, const char *tty, - const char *hostname); + const char *hostname, + pid_t main_pid); #endif /* ENABLE_LOGIND */ /** diff --git a/lib/utmp.c b/lib/utmp.c index 7c44aea46..e7bd05578 100644 --- a/lib/utmp.c +++ b/lib/utmp.c @@ -149,7 +149,7 @@ err_close: * Return NULL if no entries exist in utmp for the current process. */ static /*@null@*/ /*@only@*/struct utmpx * -get_current_utmp(void) +get_current_utmp(pid_t main_pid) { struct utmpx *ut; @@ -161,8 +161,7 @@ get_current_utmp(void) && (USER_PROCESS != ut->ut_type)) continue; - if ( ( (ut->ut_pid == getpid ()) - || (ut->ut_pid == getppid ())) + if ( (main_pid == ut->ut_pid) && ('\0' != ut->ut_id[0]) /* A process may have failed to close an entry * Check if this entry refers to the current tty */ @@ -187,12 +186,12 @@ get_current_utmp(void) int -get_session_host(char **out) +get_session_host(char **out, pid_t main_pid) { int ret = 0; struct utmpx *ut; - ut = get_current_utmp(); + ut = get_current_utmp(main_pid); #if defined(HAVE_STRUCT_UTMPX_UT_HOST) if ((ut != NULL) && (ut->ut_host[0] != '\0')) { @@ -250,7 +249,7 @@ updwtmpx(const char *filename, const struct utmpx *ut) */ static /*@only@*/struct utmpx * prepare_utmp(const char *name, const char *line, const char *host, - /*@null@*/const struct utmpx *ut) + /*@null@*/const struct utmpx *ut, pid_t main_pid) { char *hostname = NULL; struct utmpx *utent; @@ -274,7 +273,7 @@ prepare_utmp(const char *name, const char *line, const char *host, utent->ut_type = USER_PROCESS; - utent->ut_pid = getpid (); + utent->ut_pid = main_pid; STRNCPY(utent->ut_line, line); if (NULL != ut) { STRNCPY(utent->ut_id, ut->ut_id); @@ -372,12 +371,13 @@ setutmp(struct utmpx *ut) int -update_utmp(const char *user, const char *tty, const char *host) +update_utmp(const char *user, const char *tty, const char *host, + pid_t main_pid) { struct utmpx *utent, *ut; - utent = get_current_utmp (); - ut = prepare_utmp (user, tty, host, utent); + utent = get_current_utmp(main_pid); + ut = prepare_utmp(user, tty, host, utent, main_pid); (void) setutmp (ut); /* make entry in the utmp & wtmp files */ @@ -389,13 +389,15 @@ update_utmp(const char *user, const char *tty, const char *host) void -record_failure(const char *failent_user, const char *tty, const char *hostname) +record_failure(const char *failent_user, const char *tty, const char *hostname, + pid_t main_pid) { struct utmpx *utent, *failent; if (getdef_str ("FTMP_FILE") != NULL) { - utent = get_current_utmp (); - failent = prepare_utmp (failent_user, tty, hostname, utent); + utent = get_current_utmp(main_pid); + failent = prepare_utmp(failent_user, tty, hostname, utent, + main_pid); failtmp (failent_user, failent); free (utent); free (failent); diff --git a/src/login.c b/src/login.c index 88e8cc01a..3238c46f3 100644 --- a/src/login.c +++ b/src/login.c @@ -456,6 +456,7 @@ int main (int argc, char **argv) char *host = NULL; char tty[BUFSIZ]; char fromhost[512]; + pid_t initial_pid; /* the "session leader" PID */ const char *failent_user; const char *tmptty; const char *cp; @@ -504,7 +505,8 @@ int main (int argc, char **argv) exit (1); /* must be a terminal */ } - err = get_session_host(&host); + initial_pid = getpid(); + err = get_session_host(&host, initial_pid); /* * Be picky if run by normal users (possible if installed setuid * root), but not if run by root. @@ -944,7 +946,7 @@ int main (int argc, char **argv) failure (pwd->pw_uid, tty, &faillog); } #ifndef ENABLE_LOGIND - record_failure(failent_user, tty, hostname); + record_failure(failent_user, tty, hostname, initial_pid); #endif /* ENABLE_LOGIND */ retries--; @@ -1121,7 +1123,7 @@ int main (int argc, char **argv) * The utmp entry needs to be updated to indicate the new status * of the session, the new PID and SID. */ - err = update_utmp (username, tty, hostname); + err = update_utmp(username, tty, hostname, initial_pid); if (err != 0) { SYSLOG ((LOG_WARN, "Unable to update utmp entry for %s", username)); } -- 2.47.2