@@ -2250,6 +2250,11 @@ dist_patch_DATA = \
%D%/packages/patches/scilab-tbx_build_help.patch \
%D%/packages/patches/scons-test-environment.patch \
%D%/packages/patches/scotch-cmake-remove-metis.patch \
+ %D%/packages/patches/screen-fix-bad-strncpy.patch \
+ %D%/packages/patches/screen-fix-CVE-2025-233.patch \
+ %D%/packages/patches/screen-fix-CVE-2025-46802.patch \
+ %D%/packages/patches/screen-fix-CVE-2025-46804.patch \
+ %D%/packages/patches/screen-fix-CVE-2025-46805.patch \
%D%/packages/patches/screen-hurd-path-max.patch \
%D%/packages/patches/scsh-nonstring-search-path.patch \
%D%/packages/patches/seed-webkit.patch \
new file mode 100644
@@ -0,0 +1,137 @@
+From a23f2fa9fbb3cb214ed6a8ab71c99bba94f79e92 Mon Sep 17 00:00:00 2001
+From: Alex Naumov <alexander_naumov@opensuse.org>
+Date: Wed, 7 May 2025 10:42:55 +0200
+Subject: [PATCH 1/6] logfile: reintroduce lf_secreopen() to fix CVE-2025-23395
+
+In commit 441bca708bd this function was mistakenly removed, which
+introduces a local root exploit vulnerability when running screen in
+setuid-root context.
+
+Committed-By: Matthias Gerstner <matthias.gerstner@suse.de>
+---
+ logfile.c | 27 +++++++++++++++++++++++----
+ logfile.h | 10 ++++++++++
+ screen.c | 19 +++++++++++++++++++
+ 3 files changed, 52 insertions(+), 4 deletions(-)
+
+diff --git a/logfile.c b/logfile.c
+index 65e7205..91dc224 100644
+--- a/logfile.c
++++ b/logfile.c
+@@ -88,10 +88,29 @@ static int logfile_reopen(char *name, int wantfd, Log *l)
+ return -1;
+ }
+ changed_logfile(l);
+- l->st->st_ino = l->st->st_dev = 0;
+ return 0;
+ }
+
++static int (*lf_reopen_fn) (char *, int, struct Log *) = logfile_reopen;
++
++/*
++ * Whenever logfwrite discoveres that it is required to close and
++ * reopen the logfile, the function registered here is called.
++ * If you do not register anything here, the above logfile_reopen()
++ * will be used instead.
++ * Your function should perform the same steps as logfile_reopen():
++ * a) close the original filedescriptor without flushing any output
++ * b) open a new logfile for future output on the same filedescriptor number.
++ * c) zero out st_dev, st_ino to tell the stolen_logfile() indcator to
++ * reinitialise itself.
++ * d) return 0 on success.
++ */
++void logreopen_register(int (*fn) (char *, int, struct Log *))
++{
++ lf_reopen_fn = fn ? fn : logfile_reopen;
++}
++
++
+ /*
+ * If the logfile has been removed, truncated, unlinked or the like,
+ * return nonzero.
+@@ -204,7 +223,7 @@ int logfwrite(Log *l, char *buf, size_t n)
+ {
+ int r;
+
+- if (stolen_logfile(l) && logfile_reopen(l->name, fileno(l->fp), l))
++ if (stolen_logfile(l) && lf_reopen_fn(l->name, fileno(l->fp), l))
+ return -1;
+ r = fwrite(buf, n, 1, l->fp);
+ l->writecount += l->flushcount + 1;
+@@ -219,13 +238,13 @@ int logfflush(Log *l)
+
+ if (!l)
+ for (l = logroot; l; l = l->next) {
+- if (stolen_logfile(l) && logfile_reopen(l->name, fileno(l->fp), l))
++ if (stolen_logfile(l) && lf_reopen_fn(l->name, fileno(l->fp), l))
+ return -1;
+ r |= fflush(l->fp);
+ l->flushcount++;
+ changed_logfile(l);
+ } else {
+- if (stolen_logfile(l) && logfile_reopen(l->name, fileno(l->fp), l))
++ if (stolen_logfile(l) && lf_reopen_fn(l->name, fileno(l->fp), l))
+ return -1;
+ r = fflush(l->fp);
+ l->flushcount++;
+diff --git a/logfile.h b/logfile.h
+index dbc9c2c..569a90e 100644
+--- a/logfile.h
++++ b/logfile.h
+@@ -71,6 +71,16 @@ int logfwrite (Log *, char *, size_t);
+ */
+ int logfflush (Log *ifany);
+
++/*
++ * a reopen function may be registered here, in case you want to bring your
++ * own (more secure open), it may come along with a private data pointer.
++ * this function is called, whenever logfwrite/logfflush detect that the
++ * file has been (re)moved, truncated or changed by someone else.
++ * if you provide NULL as parameter to logreopen_register, the builtin
++ * reopen function will be reactivated.
++ */
++void logreopen_register (int (*fn) (char *, int, struct Log *) );
++
+ /*
+ * Your custom reopen function is required to reuse the exact
+ * filedescriptor.
+diff --git a/screen.c b/screen.c
+index a79c3b1..728e717 100644
+--- a/screen.c
++++ b/screen.c
+@@ -199,6 +199,21 @@ static int GotSigChld;
+ /********************************************************************/
+ /********************************************************************/
+
++static int lf_secreopen(char *name, int wantfd, struct Log *l)
++{
++ int got_fd;
++
++ close(wantfd);
++ if (((got_fd = secopen(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) || lf_move_fd(got_fd, wantfd) < 0) {
++ logfclose(l);
++ return -1;
++ }
++ l->st->st_ino = l->st->st_dev = 0;
++ return 0;
++}
++
++
++
+ static struct passwd *getpwbyname(char *name, struct passwd *ppp)
+ {
+ int n;
+@@ -349,6 +364,10 @@ int main(int argc, char **argv)
+ #ifdef ENABLE_TELNET
+ af = AF_UNSPEC;
+ #endif
++ /* lf_secreopen() is vital for the secure operation in setuid-root context.
++ * Do not remove it
++ */
++ logreopen_register(lf_secreopen);
+
+ real_uid = getuid();
+ real_gid = getgid();
+--
+2.49.0
+
new file mode 100644
@@ -0,0 +1,113 @@
+From 5a5383b312b2422689ca0220ac1557885b6ce67d Mon Sep 17 00:00:00 2001
+From: Matthias Gerstner <matthias.gerstner@suse.de>
+Date: Wed, 7 May 2025 10:56:17 +0200
+Subject: [PATCH 4/6] attacher.c: prevent temporary 0666 mode on PTYs to fix
+ CVE-2025-46802
+
+This temporary chmod of the PTY to mode 0666 is most likely a remnant of
+past times, before the PTY file descriptor was passed to the target
+session via the UNIX domain socket.
+
+This chmod() causes a race condition during which any other user in the
+system can open the PTY for reading and writing, and thus allows PTY
+hijacking.
+
+Simply remove this logic completely.
+---
+ attacher.c | 14 --------------
+ screen.c | 12 ------------
+ screen.h | 2 --
+ 3 files changed, 28 deletions(-)
+
+diff --git a/attacher.c b/attacher.c
+index 4e1a77e..e5a48b0 100644
+--- a/attacher.c
++++ b/attacher.c
+@@ -127,9 +127,6 @@ int Attach(int how)
+ xseteuid(multi_uid);
+ xseteuid(own_uid);
+ #endif
+- if (chmod(attach_tty, 0666))
+- Panic(errno, "chmod %s", attach_tty);
+- tty_oldmode = tty_mode;
+ }
+
+ memset((char *)&m, 0, sizeof(Message));
+@@ -279,12 +276,6 @@ int Attach(int how)
+ pause(); /* wait for SIGCONT */
+ xsignal(SIGCONT, SIG_DFL);
+ ContinuePlease = false;
+- xseteuid(own_uid);
+- if (tty_oldmode >= 0)
+- if (chmod(attach_tty, tty_oldmode))
+- Panic(errno, "chmod %s", attach_tty);
+- tty_oldmode = -1;
+- xseteuid(real_uid);
+ }
+ rflag = 0;
+ return 1;
+@@ -334,11 +325,6 @@ void AttacherFinit(int sigsig)
+ close(s);
+ }
+ }
+- if (tty_oldmode >= 0) {
+- if (setuid(own_uid))
+- Panic(errno, "setuid");
+- chmod(attach_tty, tty_oldmode);
+- }
+ exit(0);
+ }
+
+diff --git a/screen.c b/screen.c
+index 728e717..fb61c7f 100644
+--- a/screen.c
++++ b/screen.c
+@@ -145,8 +145,6 @@ bool hastruecolor = false;
+
+ char *multi;
+ int multiattach;
+-int tty_mode;
+-int tty_oldmode = -1;
+
+ char HostName[MAXSTR];
+ pid_t MasterPid;
+@@ -766,7 +764,6 @@ int main(int argc, char **argv)
+
+ /* ttyname implies isatty */
+ SetTtyname(true, &st);
+- tty_mode = (int)st.st_mode & 0777;
+
+ fl = fcntl(0, F_GETFL, 0);
+ if (fl != -1 && (fl & (O_RDWR | O_RDONLY | O_WRONLY)) == O_RDWR)
+@@ -1570,15 +1567,6 @@ void Panic(int err, const char *fmt, ...)
+ if (D_userpid)
+ Kill(D_userpid, SIG_BYE);
+ }
+- if (tty_oldmode >= 0) {
+-#if defined(HAVE_SETEUID)
+- if (setuid(own_uid))
+- xseteuid(own_uid); /* may be a loop. sigh. */
+-#else
+- setuid(own_uid);
+-#endif
+- chmod(attach_tty, tty_oldmode);
+- }
+ eexit(1);
+ }
+
+diff --git a/screen.h b/screen.h
+index 308c365..410b4f4 100644
+--- a/screen.h
++++ b/screen.h
+@@ -291,8 +291,6 @@ extern int nversion;
+ extern uid_t own_uid;
+ extern int queryflag;
+ extern int rflag;
+-extern int tty_mode;
+-extern int tty_oldmode;
+ extern pid_t MasterPid;
+ extern int MsgMinWait;
+ extern int MsgWait;
+--
+2.49.0
+
new file mode 100644
@@ -0,0 +1,130 @@
+From 49473441c17006856268f37249e62a99a7901741 Mon Sep 17 00:00:00 2001
+From: Matthias Gerstner <matthias.gerstner@suse.de>
+Date: Wed, 7 May 2025 11:25:25 +0200
+Subject: [PATCH 5/6] Avoid file existence test information leaks to fix
+ CVE-2025-46804
+
+In setuid-root context the current error messages give away whether
+certain paths not accessible by the real user exist and what type they
+have. To prevent this only output generic error messages in setuid-root
+context.
+
+In some situations, when an error is pertaining a directory and the
+directory is owner by the real user then we can still output more
+detailed diagnostics.
+
+This change can lead to less helpful error messages when Screen is
+install setuid-root. More complex changes would be needed to avoid this
+(e.g. only open the `SocketPath` with raised privileges when
+multi-attach is requested).
+
+There might still be lingering some code paths that allow such
+information leaks, since `SocketPath` is a global variable that is used
+across the code base. The majority of issues should be caught with this
+fix, however.
+---
+ screen.c | 54 ++++++++++++++++++++++++++++++++++++++++++------------
+ socket.c | 9 +++++++--
+ 2 files changed, 49 insertions(+), 14 deletions(-)
+
+diff --git a/screen.c b/screen.c
+index fb61c7f..eabbdc2 100644
+--- a/screen.c
++++ b/screen.c
+@@ -862,22 +862,47 @@ int main(int argc, char **argv)
+ #endif
+ }
+
+- if (stat(SocketPath, &st) == -1)
+- Panic(errno, "Cannot access %s", SocketPath);
+- else if (!S_ISDIR(st.st_mode))
+- Panic(0, "%s is not a directory.", SocketPath);
++ if (stat(SocketPath, &st) == -1) {
++ if (eff_uid == real_uid) {
++ Panic(errno, "Cannot access %s", SocketPath);
++ } else {
++ Panic(0, "Error accessing %s", SocketPath);
++ }
++ }
++ else if (!S_ISDIR(st.st_mode)) {
++ if (eff_uid == real_uid || st.st_uid == real_uid) {
++ Panic(0, "%s is not a directory.", SocketPath);
++ } else {
++ Panic(0, "Error accessing %s", SocketPath);
++ }
++ }
+ if (multi) {
+- if (st.st_uid != multi_uid)
+- Panic(0, "%s is not the owner of %s.", multi, SocketPath);
++ if (st.st_uid != multi_uid) {
++ if (eff_uid == real_uid || st.st_uid == real_uid) {
++ Panic(0, "%s is not the owner of %s.", multi, SocketPath);
++ } else {
++ Panic(0, "Error accessing %s", SocketPath);
++ }
++ }
+ } else {
+ #ifdef SOCKET_DIR /* if SOCKETDIR is not defined, the socket is in $HOME.
+ in that case it does not make sense to compare uids. */
+- if (st.st_uid != real_uid)
+- Panic(0, "You are not the owner of %s.", SocketPath);
++ if (st.st_uid != real_uid) {
++ if (eff_uid == real_uid) {
++ Panic(0, "You are not the owner of %s.", SocketPath);
++ } else {
++ Panic(0, "Error accessing %s", SocketPath);
++ }
++ }
+ #endif
+ }
+- if ((st.st_mode & 0777) != 0700)
+- Panic(0, "Directory %s must have mode 700.", SocketPath);
++ if ((st.st_mode & 0777) != 0700) {
++ if (eff_uid == real_uid || st.st_uid == real_uid) {
++ Panic(0, "Directory %s must have mode 700.", SocketPath);
++ } else {
++ Panic(0, "Error accessing %s", SocketPath);
++ }
++ }
+ if (SocketMatch && strchr(SocketMatch, '/'))
+ Panic(0, "Bad session name '%s'", SocketMatch);
+ SocketName = SocketPath + strlen(SocketPath) + 1;
+@@ -902,8 +927,13 @@ int main(int argc, char **argv)
+ else
+ exit(9 + (fo || oth ? 1 : 0) + fo);
+ }
+- if (fo == 0)
+- Panic(0, "No Sockets found in %s.\n", SocketPath);
++ if (fo == 0) {
++ if (eff_uid == real_uid || st.st_uid == real_uid) {
++ Panic(0, "No Sockets found in %s.\n", SocketPath);
++ } else {
++ Panic(0, "Error accessing %s", SocketPath);
++ }
++ }
+ Msg(0, "%d Socket%s in %s.", fo, fo > 1 ? "s" : "", SocketPath);
+ eexit(0);
+ }
+diff --git a/socket.c b/socket.c
+index 5709a24..d0b361a 100644
+--- a/socket.c
++++ b/socket.c
+@@ -148,8 +148,13 @@ int FindSocket(int *fdp, int *nfoundp, int *notherp, char *match)
+ xseteuid(real_uid);
+ xsetegid(real_gid);
+
+- if ((dirp = opendir(SocketPath)) == NULL)
+- Panic(errno, "Cannot opendir %s", SocketPath);
++ if ((dirp = opendir(SocketPath)) == NULL) {
++ if (eff_uid == real_uid) {
++ Panic(errno, "Cannot opendir %s", SocketPath);
++ } else {
++ Panic(0, "Error accessing %s", SocketPath);
++ }
++ }
+
+ slist = NULL;
+ slisttail = &slist;
+--
+2.49.0
+
new file mode 100644
@@ -0,0 +1,115 @@
+From d993aacb892ee7aa83c0e21174c8b65b191802d5 Mon Sep 17 00:00:00 2001
+From: Matthias Gerstner <matthias.gerstner@suse.de>
+Date: Wed, 7 May 2025 12:30:39 +0200
+Subject: [PATCH 6/6] socket.c: don't send signals with root privileges to fix
+ CVE-2025-46805
+
+The CheckPid() function was introduced to address CVE-2023-24626, to
+prevent sending SIGCONT and SIGHUP to arbitrary PIDs in the system. This
+fix still suffers from a TOCTOU race condition. The client can replace
+itself by a privileged process, or try to cycle PIDs until a privileged
+process receives the original PID.
+
+To prevent this, always send signals using the real privileges. Keep
+CheckPid() for error diagnostics. If sending the actual signal fails
+later on then there will be no more error reporting.
+
+It seems the original bugfix already introduced a regression when
+attaching to another's user session that is not owned by root. In this
+case the target sessions runs with real uid X, while for sending a
+signal to the `pid` provided by the client real uid Y (or root
+privileges) are required.
+
+This is hard to properly fix without this regression. On Linux pidfds
+could be used to allow safely sending signals to other PIDs as root
+without involving race conditions. In this case the client PID should
+also be obtained via the UNIX domain socket's SO_PEERCRED option,
+though.
+---
+ socket.c | 21 +++++++++++++--------
+ 1 file changed, 13 insertions(+), 8 deletions(-)
+
+diff --git a/socket.c b/socket.c
+index d0b361a..c715519 100644
+--- a/socket.c
++++ b/socket.c
+@@ -91,6 +91,11 @@ static void AskPassword(Message *);
+ static bool CheckPassword(const char *password);
+ static void PasswordProcessInput(char *, size_t);
+
++static void KillUnpriv(pid_t pid, int sig) {
++ UserContext();
++ UserReturn(kill(pid, sig));
++}
++
+ #define SOCKMODE (S_IWRITE | S_IREAD | (displays ? S_IEXEC : 0) | (multi ? 1 : 0))
+
+ /*
+@@ -611,7 +616,7 @@ static int CreateTempDisplay(Message *m, int recvfd, Window *win)
+ Msg(errno, "Could not perform necessary sanity "
+ "checks on pts device.");
+ close(i);
+- Kill(pid, SIG_BYE);
++ KillUnpriv(pid, SIG_BYE);
+ return -1;
+ }
+ if (strcmp(ttyname_in_ns, m->m_tty)) {
+@@ -620,7 +625,7 @@ static int CreateTempDisplay(Message *m, int recvfd, Window *win)
+ ttyname_in_ns,
+ m->m_tty[0] != '\0' ? m->m_tty : "(null)");
+ close(i);
+- Kill(pid, SIG_BYE);
++ KillUnpriv(pid, SIG_BYE);
+ return -1;
+ }
+ /* m->m_tty so far contains the actual name of the pts
+@@ -638,24 +643,24 @@ static int CreateTempDisplay(Message *m, int recvfd, Window *win)
+ "Attach: passed fd does not match tty: %s - %s!",
+ m->m_tty, myttyname ? myttyname : "NULL");
+ close(i);
+- Kill(pid, SIG_BYE);
++ KillUnpriv(pid, SIG_BYE);
+ return -1;
+ }
+ } else if ((i = secopen(m->m_tty, O_RDWR | O_NONBLOCK, 0)) < 0) {
+ Msg(errno, "Attach: Could not open %s!", m->m_tty);
+- Kill(pid, SIG_BYE);
++ KillUnpriv(pid, SIG_BYE);
+ return -1;
+ }
+
+ if (attach)
+- Kill(pid, SIGCONT);
++ KillUnpriv(pid, SIGCONT);
+
+ if (attach) {
+ if (display || win) {
+ int unused_result = write(i, "Attaching from inside of screen?\n", 33);
+ (void)unused_result; /* unused */
+ close(i);
+- Kill(pid, SIG_BYE);
++ KillUnpriv(pid, SIG_BYE);
+ Msg(0, "Attach msg ignored: coming from inside.");
+ return -1;
+ }
+@@ -678,7 +683,7 @@ static int CreateTempDisplay(Message *m, int recvfd, Window *win)
+ (void)unused_result; /* unused */
+ close(i);
+ Msg(0, "Attach: could not make display for user %s", user);
+- Kill(pid, SIG_BYE);
++ KillUnpriv(pid, SIG_BYE);
+ return -1;
+ }
+ if (attach) {
+@@ -884,7 +889,7 @@ void ReceiveMsg(void)
+ Msg(0, "Query attempt with bad pid(%d)!", m.m.command.apid);
+ }
+ else {
+- Kill(m.m.command.apid, (queryflag >= 0) ? SIGCONT : SIG_BYE); /* Send SIG_BYE if an error happened */
++ KillUnpriv(m.m.command.apid, (queryflag >= 0) ? SIGCONT : SIG_BYE); /* Send SIG_BYE if an error happened */
+ queryflag = -1;
+ }
+ }
+--
+2.49.0
+
new file mode 100644
@@ -0,0 +1,60 @@
+From e61649242afc42213e7fd3bb8b3dbea33be96761 Mon Sep 17 00:00:00 2001
+From: Alex Naumov <alexander_naumov@opensuse.org>
+Date: Wed, 7 May 2025 10:49:24 +0200
+Subject: [PATCH 3/6] attacher.c: fix bad strncpy() which can lead to a buffer
+ overflow
+
+`strncpy()` always pads the destination buffer with zeroes, regardless
+of the length of the input string. Passing `MAXPATHLEN` in every `for`
+loop iteration will cause a buffer write overflow past the end of the
+`m.m.command.cmd` buffer.
+
+This becomes visible on systems that compile Screen with the
+`_FORTIFY_SOURCE` macro enabled when passing more than one parameter,
+for example like this:
+
+```
+screen -S myinstance -X blankerprg /path/to/blanker
+*** buffer overflow detected ***: terminated
+Aborted (core dumped)
+```
+
+This is not security relevant, since only zeroes are written past the
+end of the buffer and only other message buffer fields can be reached,
+no internal state of Screen can be changed.
+
+Committed-By: Matthias Gerstner <matthias.gerstner@suse.de>
+---
+ attacher.c | 15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+diff --git a/attacher.c b/attacher.c
+index d8de9d4..4e1a77e 100644
+--- a/attacher.c
++++ b/attacher.c
+@@ -457,13 +457,16 @@ void SendCmdMessage(char *sty, char *match, char **av, int query)
+ }
+ p = m.m.command.cmd;
+ n = 0;
++ size_t space_left = ARRAY_SIZE(m.m.command.cmd);
++
+ for (; *av && n < MAXARGS - 1; ++av, ++n) {
+- size_t len;
+- len = strlen(*av) + 1;
+- if (p + len >= m.m.command.cmd + ARRAY_SIZE(m.m.command.cmd) - 1)
+- break;
+- strncpy(p, *av, MAXPATHLEN);
+- p += len;
++ int printed = snprintf(p, space_left, "%s", *av);
++ if (printed < 0 || (size_t)printed >= space_left)
++ Panic(0, "Total length of the command to send too large.\n");
++
++ printed += 1; // add null terminator
++ p += printed;
++ space_left -= printed;
+ }
+ *p = 0;
+ m.m.command.nargs = n;
+--
+2.49.0
+
@@ -52,7 +52,12 @@ (define-public screen
(method url-fetch)
(uri (string-append "mirror://gnu/screen/screen-"
version ".tar.gz"))
- (patches (search-patches "screen-hurd-path-max.patch"))
+ (patches (search-patches "screen-hurd-path-max.patch"
+ "screen-fix-CVE-2025-233.patch"
+ "screen-fix-CVE-2025-46802.patch"
+ "screen-fix-CVE-2025-46804.patch"
+ "screen-fix-CVE-2025-46805.patch"
+ "screen-fix-bad-strncpy.patch"))
(sha256
(base32 "0wa9v6p7cna2scpimpvk9pgxaah80f4q0f2kln37qp0f1b83jjph"))))
(build-system gnu-build-system)
@@ -66,6 +71,10 @@ (define-public screen
#~(list
;; GNU_SOURCE must be defined for mallocmock_reset() to be defined
"CFLAGS=-O2 -g -D_GNU_SOURCE=1"
+ ;; As of 5.0.0, Screen creates world-writable PTYs by default, whereas
+ ;; previously the configure script used a safer mode of 620.
+ ;; See also <https://www.openwall.com/lists/oss-security/2025/05/12/1>.
+ "--with-pty-mode=620"
;; By default, screen supports 16 colors, but we want 256 when
;; ~/.screenrc contains 'term xterm-256color'.
"--enable-colors256")))
@@ -78,7 +87,8 @@ (define-public screen
then manages the different virtual terminals, allowing you to easily switch
between them, to detach them from the current session, or even splitting the
view to show two terminals at once.")
- (license gpl2+)))
+ (license gpl2+)
+ (properties `((lint-hidden-cve . ("CVE-2025-46803"))))))
(define-public dtach
(package