From: Karel Zak Date: Tue, 7 Apr 2026 11:49:02 +0000 (+0200) Subject: write, mesg: add S_ISCHR() check for terminal device paths X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a0fe243b9e5ecbfd19bc1fb88d6c362878a6aff0;p=thirdparty%2Futil-linux.git write, mesg: add S_ISCHR() check for terminal device paths Verify that the target path is a character device before proceeding. This hardens both utilities against symlink-to-regular-file attacks as a defense-in-depth measure. Note that the TOCTOU race condition between stat() and freopen()/open() is only theoretical -- on modern Linux systems /dev/pts/ is a kernel-managed devpts filesystem where unprivileged users cannot create or replace entries, and the setgid tty privilege only grants write access to terminal devices, not to arbitrary files. --- diff --git a/term-utils/mesg.c b/term-utils/mesg.c index e51f659a6..d3eea6711 100644 --- a/term-utils/mesg.c +++ b/term-utils/mesg.c @@ -142,6 +142,8 @@ int main(int argc, char *argv[]) if (!*argv) { if (stat(tty, &sb)) err(MESG_EXIT_FAILURE, _("stat of %s failed"), tty); + if (!S_ISCHR(sb.st_mode)) + errx(MESG_EXIT_FAILURE, _("%s: not a character device"), tty); if (sb.st_mode & (S_IWGRP | S_IWOTH)) { puts(_("is y")); return IS_ALLOWED; @@ -154,6 +156,8 @@ int main(int argc, char *argv[]) err(MESG_EXIT_FAILURE, _("cannot open %s"), tty); if (fstat(fd, &sb)) err(MESG_EXIT_FAILURE, _("stat of %s failed"), tty); + if (!S_ISCHR(sb.st_mode)) + errx(MESG_EXIT_FAILURE, _("%s: not a character device"), tty); switch (rpmatch(argv[0])) { case RPMATCH_YES: diff --git a/term-utils/write.c b/term-utils/write.c index 3784f0300..feb6751a2 100644 --- a/term-utils/write.c +++ b/term-utils/write.c @@ -114,6 +114,11 @@ static int check_tty(const char *tty, int *tty_writeable, time_t *tty_atime, int warn("%s", tty); return 1; } + if (!S_ISCHR(s.st_mode)) { + if (showerror) + warnx(_("%s: not a character device"), tty); + return 1; + } if (getuid() == 0) /* root can always write */ *tty_writeable = 1; else {