From a23f2fa9fbb3cb214ed6a8ab71c99bba94f79e92 Mon Sep 17 00:00:00 2001 From: Alex Naumov 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 --- 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