From 6d7cc5a29aa30a0841bc7b9e0b731b7eafbaf68c Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 25 May 2026 12:07:57 +0200 Subject: [PATCH] write: always use utmp as fallback, use mem2strcpy() for utmp strings Resolves: RHEL-157244 --- 0022-write-always-use-utmp-as-fallback.patch | 122 ++++++++++++++++++ ...rite-use-mem2strcpy-for-utmp-strings.patch | 74 +++++++++++ util-linux.spec | 3 + 3 files changed, 199 insertions(+) create mode 100644 0022-write-always-use-utmp-as-fallback.patch create mode 100644 0023-write-use-mem2strcpy-for-utmp-strings.patch diff --git a/0022-write-always-use-utmp-as-fallback.patch b/0022-write-always-use-utmp-as-fallback.patch new file mode 100644 index 0000000..9b92a8e --- /dev/null +++ b/0022-write-always-use-utmp-as-fallback.patch @@ -0,0 +1,122 @@ +From 1a06fc9c79b27fb842f000d26b1122d63411c5d9 Mon Sep 17 00:00:00 2001 +From: Karel Zak +Date: Wed, 13 May 2026 11:13:03 +0200 +Subject: [PATCH 22/23] write: always use utmp as fallback +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The systemd session list may not cover all terminals (e.g., screen, +tmux, or old-style logins). Remove the "if systemd else utmp" pattern +and always fall through to utmp when the target user or tty is not +found in systemd sessions. + +Also make sd_get_sessions() and sd_session_get_username() failures +non-fatal — silently fall through to utmp instead of calling errx(). + +Addresses: https://redhat.atlassian.net/browse/RHEL-157244 +Signed-off-by: Karel Zak +(cherry picked from commit 3d84e9af768f904660d962720c4c03735e86472b) +--- + term-utils/write.c | 50 +++++++++++++++++++++++----------------------- + 1 file changed, 25 insertions(+), 25 deletions(-) + +diff --git a/term-utils/write.c b/term-utils/write.c +index 3784f0300..fb19e35f4 100644 +--- a/term-utils/write.c ++++ b/term-utils/write.c +@@ -140,17 +140,16 @@ static int check_utmp(const struct write_control *ctl) + if (sd_booted() > 0) { + char **sessions_list; + int sessions = sd_get_sessions(&sessions_list); ++ + if (sessions < 0) +- errx(EXIT_FAILURE, _("error getting sessions: %s"), +- strerror(-sessions)); ++ goto utmp; + + for (int i = 0; i < sessions; i++) { + + char *name, *tty; +- int r; + +- if ((r = sd_session_get_username(sessions_list[i], &name)) < 0) +- errx(EXIT_FAILURE, _("get user name failed: %s"), strerror (-r)); ++ if (sd_session_get_username(sessions_list[i], &name) < 0) ++ continue; + if (sd_session_get_tty(sessions_list[i], &tty) < 0) { + free(name); + continue; +@@ -169,23 +168,24 @@ static int check_utmp(const struct write_control *ctl) + for (int i = 0; i < sessions; i++) + free(sessions_list[i]); + free(sessions_list); +- } else { +-#endif +- utmpxname(_PATH_UTMP); +- setutxent(); + +- while ((u = getutxent())) { +- if (strncmp(ctl->dst_login, u->ut_user, sizeof(u->ut_user)) == 0 && +- strncmp(ctl->dst_tty_name, u->ut_line, sizeof(u->ut_line)) == 0) { +- res = 0; +- break; +- } +- } +- +- endutxent(); +-#if defined(USE_SYSTEMD) && HAVE_DECL_SD_SESSION_GET_USERNAME == 1 ++ if (res == 0) ++ return res; + } ++utmp: + #endif ++ utmpxname(_PATH_UTMP); ++ setutxent(); ++ ++ while ((u = getutxent())) { ++ if (strncmp(ctl->dst_login, u->ut_user, sizeof(u->ut_user)) == 0 && ++ strncmp(ctl->dst_tty_name, u->ut_line, sizeof(u->ut_line)) == 0) { ++ res = 0; ++ break; ++ } ++ } ++ ++ endutxent(); + return res; + } + +@@ -211,16 +211,15 @@ static void search_utmp(struct write_control *ctl) + char path[256]; + char **sessions_list; + int sessions = sd_get_sessions(&sessions_list); ++ + if (sessions < 0) +- errx(EXIT_FAILURE, _("error getting sessions: %s"), +- strerror(-sessions)); ++ goto utmp; + + for (int i = 0; i < sessions; i++) { + char *name, *tty; +- int r; + +- if ((r = sd_session_get_username(sessions_list[i], &name)) < 0) +- errx(EXIT_FAILURE, _("get user name failed: %s"), strerror (-r)); ++ if (sd_session_get_username(sessions_list[i], &name) < 0) ++ continue; + + if (strcmp(ctl->dst_login, name) != 0) { + free(name); +@@ -266,7 +265,8 @@ static void search_utmp(struct write_control *ctl) + for (int i = 0; i < sessions; i++) + free(sessions_list[i]); + free(sessions_list); +- } else ++ } ++utmp: + #endif + { + char path[sizeof(u->ut_line) + 6]; +-- +2.52.0 + diff --git a/0023-write-use-mem2strcpy-for-utmp-strings.patch b/0023-write-use-mem2strcpy-for-utmp-strings.patch new file mode 100644 index 0000000..486e87a --- /dev/null +++ b/0023-write-use-mem2strcpy-for-utmp-strings.patch @@ -0,0 +1,74 @@ +From dca96d57504b09c2abfca844689e521d44dcde23 Mon Sep 17 00:00:00 2001 +From: Karel Zak +Date: Wed, 13 May 2026 11:22:59 +0200 +Subject: [PATCH 23/23] write: use mem2strcpy() for utmp strings + +The utmp fields ut_user and ut_line are fixed-size buffers not +guaranteed to be null-terminated. Using strncmp(), snprintf() or +memcmp() directly on these fields can read beyond the buffer content. + +Use mem2strcpy() to safely copy utmp fields into properly terminated +local buffers before any string operations. + +Signed-off-by: Karel Zak +(cherry picked from commit 2483e99e14a1ed24129e5255804a679c56493c25) +--- + term-utils/write.c | 21 ++++++++++++++++----- + 1 file changed, 16 insertions(+), 5 deletions(-) + +diff --git a/term-utils/write.c b/term-utils/write.c +index fb19e35f4..ae550e268 100644 +--- a/term-utils/write.c ++++ b/term-utils/write.c +@@ -178,8 +178,14 @@ utmp: + setutxent(); + + while ((u = getutxent())) { +- if (strncmp(ctl->dst_login, u->ut_user, sizeof(u->ut_user)) == 0 && +- strncmp(ctl->dst_tty_name, u->ut_line, sizeof(u->ut_line)) == 0) { ++ char user[sizeof(u->ut_user) + 1]; ++ char line[sizeof(u->ut_line) + 1]; ++ ++ mem2strcpy(user, u->ut_user, sizeof(u->ut_user), sizeof(user)); ++ mem2strcpy(line, u->ut_line, sizeof(u->ut_line), sizeof(line)); ++ ++ if (strcmp(ctl->dst_login, user) == 0 && ++ strcmp(ctl->dst_tty_name, line) == 0) { + res = 0; + break; + } +@@ -269,23 +275,28 @@ static void search_utmp(struct write_control *ctl) + utmp: + #endif + { ++ char user[sizeof(u->ut_user) + 1]; ++ char line[sizeof(u->ut_line) + 1]; + char path[sizeof(u->ut_line) + 6]; + + utmpxname(_PATH_UTMP); + setutxent(); + + while ((u = getutxent())) { +- if (strncmp(ctl->dst_login, u->ut_user, sizeof(u->ut_user)) != 0) ++ mem2strcpy(user, u->ut_user, sizeof(u->ut_user), sizeof(user)); ++ mem2strcpy(line, u->ut_line, sizeof(u->ut_line), sizeof(line)); ++ ++ if (strcmp(ctl->dst_login, user) != 0) + continue; + num_ttys++; +- snprintf(path, sizeof(path), "/dev/%s", u->ut_line); ++ snprintf(path, sizeof(path), "/dev/%s", line); + if (check_tty(path, &tty_writeable, &tty_atime, 0)) + /* bad term? skip */ + continue; + if (ctl->src_uid && !tty_writeable) + /* skip ttys with msgs off */ + continue; +- if (memcmp(u->ut_line, ctl->src_tty_name, strlen(ctl->src_tty_name) + 1) == 0) { ++ if (strcmp(line, ctl->src_tty_name) == 0) { + user_is_me = 1; + /* don't write to yourself */ + continue; +-- +2.52.0 + diff --git a/util-linux.spec b/util-linux.spec index 095b32c..01bbd12 100644 --- a/util-linux.spec +++ b/util-linux.spec @@ -146,6 +146,9 @@ Patch20: 0020-login-utils-fix-setpwnam-buffer-use-CVE-2025-14104.patch ### # RHEL-148508 - sys-utils/lscpu: Change object type to SCOLS_JSON_STRING if data == Patch21: 0021-sys-utils-lscpu-Change-object-type-to-SCOLS_JSON_STR.patch +# RHEL-157244 - write: always use utmp as fallback +Patch22: 0022-write-always-use-utmp-as-fallback.patch +Patch23: 0023-write-use-mem2strcpy-for-utmp-strings.patch %description