]> git.ipfire.org Git - thirdparty/shadow.git/commitdiff
lib/, src/: Fix utmp update: use initial PID
authorEvgeny Grin (Karlson2k) <k2k@drgrin.dev>
Mon, 14 Jul 2025 18:57:03 +0000 (20:57 +0200)
committerAlejandro Colomar <foss+github@alejandro-colomar.es>
Fri, 18 Jul 2025 19:55:10 +0000 (21:55 +0200)
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) <k2k@drgrin.dev>
lib/logind.c
lib/prototypes.h
lib/utmp.c
src/login.c

index 46ce321968f878fe215dcd1b746e234bbe6a2e25..f4b187e5bf2b622c5e33656dcff49c50ed3336c4 100644 (file)
 
 #include <systemd/sd-login.h>
 
-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;
     }
index b904121c803851a7635e64d606b55c1d63e061b8..4063ae164429162b809cda769dcfb2cf8cb26e86 100644 (file)
@@ -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 */
 
 /**
index 7c44aea462ac736c3e2982825edc339cef5472f1..e7bd0557859afb0c8c33ddb49219279751600562 100644 (file)
@@ -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);
index 88e8cc01a9c111ad1622ee350fcdccb8711e04e7..3238c46f3296b5471757f5dbac1a599acc97c0fd 100644 (file)
@@ -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));
        }