]> git.ipfire.org Git - thirdparty/shadow.git/commitdiff
lib/utmp.c: Fix umtp entry search master
authorEvgeny Grin (Karlson2k) <k2k@drgrin.dev>
Mon, 14 Jul 2025 19:37:08 +0000 (21:37 +0200)
committerIker Pedrosa <ikerpedrosam@gmail.com>
Sat, 9 Aug 2025 09:18:51 +0000 (11:18 +0200)
Updated utmp entry search algorithm to follow GNU/Linux description:
https://man7.org/linux/man-pages/man5/utmp.5.html#DESCRIPTION

An entry is found by looking for matching PID.  If several such entries
found (for example, due to cleanup failure of old entries) then first
entry with both matching PID and matching 'ut_line' (current terminal)
is used.  If not entry has matching 'ut_line' then first entry with
matching PID is used (if getty/init process does not set 'ut_line').

When no single entry is matched by PID, then but at least one entry is
matched current terminal the the first such entry is selected (if getty
does not set correct PID).

This commit uses non-portable Elvis operator is it is already used
everywhere in the code.

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
lib/utmp.c

index e7bd0557859afb0c8c33ddb49219279751600562..157fc5737eee3e41b0a165236cdc21e8015e8450 100644 (file)
@@ -3,6 +3,7 @@
  * SPDX-FileCopyrightText: 1996 - 1999, Marek Michałkiewicz
  * SPDX-FileCopyrightText: 2001 - 2005, Tomasz Kłoczko
  * SPDX-FileCopyrightText: 2008 - 2009, Nicolas François
+ * SPDX-FileCopyrightText: 2025, Evgeny Grin (Karlson2k)
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -138,20 +139,19 @@ err_close:
  * get_current_utmp - return the most probable utmp entry for the current
  *                    session
  *
- *     The utmp file is scanned for an entry with the same process ID.
- *     The line entered by the *getty / telnetd, etc. should also match
- *     the current terminal.
+ *     When an entry is returned by this function, and if the utmpx structure
+ *     has a ut_id field and this field is not empty, then this field
+ *     should be used to update the entry information.
  *
- *     When an entry is returned by get_current_utmp, and if the utmpx
- *     structure has a ut_id field, this field should be used to update
- *     the entry information.
- *
- *     Return NULL if no entries exist in utmp for the current process.
+ *     Return NULL if no entries exist in utmp for the current process or
+ *                 there is an error reading utmp.
  */
 static /*@null@*/ /*@only@*/struct utmpx *
 get_current_utmp(pid_t main_pid)
 {
        struct utmpx  *ut;
+       struct utmpx  *ut_by_pid  = NULL;
+       struct utmpx  *ut_by_line = NULL;
 
        setutxent();
 
@@ -161,16 +161,23 @@ get_current_utmp(pid_t main_pid)
                    && (USER_PROCESS  != ut->ut_type))
                        continue;
 
-               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 */
-                   && is_my_tty(ut->ut_line))
-               {
-                       break;
+               if (main_pid == ut->ut_pid) {
+                       if (is_my_tty(ut->ut_line))
+                               break; /* Perfect match, stop the search */
+
+                       if (NULL == ut_by_pid)
+                               ut_by_pid = ut;
+
+               } else if (   (NULL == ut_by_line)
+                          && (LOGIN_PROCESS == ut->ut_type) /* Be more picky when matching by 'ut_line' only */
+                          && (is_my_tty(ut->ut_line))) {
+                       ut_by_line = ut;
                }
        }
 
+       if (NULL == ut)
+               ut = ut_by_pid ?: ut_by_line;
+
        if (NULL != ut) {
                struct utmpx  *ut_copy;
 
@@ -236,7 +243,8 @@ updwtmpx(const char *filename, const struct utmpx *ut)
  *     It accepts an utmp entry in input (ut) to return an entry with
  *     the right ut_id. This is typically an entry returned by
  *     get_current_utmp
- *     If ut is NULL, ut_id will be forged based on the line argument.
+ *     If ut is NULL or ut->ut_id is empty, ut_id will be forged based on
+ *     the line argument.
  *
  *     The ut_host field of the input structure may also be kept, and is
  *     used to define the ut_addr/ut_addr_v6 fields. (if these fields
@@ -275,7 +283,8 @@ prepare_utmp(const char *name, const char *line, const char *host,
        utent->ut_type = USER_PROCESS;
        utent->ut_pid = main_pid;
        STRNCPY(utent->ut_line, line);
-       if (NULL != ut) {
+       if (   (NULL != ut)
+           && ('\0' != ut->ut_id[0])) {
                STRNCPY(utent->ut_id, ut->ut_id);
        } else {
                STRNCPY(utent->ut_id, strnul(line) - MIN(strlen(line), countof(utent->ut_id)));