git: 4a4338d94401 - main - comsat: Don't read arbitrary files

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Mon, 28 Jul 2025 15:30:15 UTC
The branch main has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=4a4338d94401f0012380d4f1a4d332bd6d44fa8e

commit 4a4338d94401f0012380d4f1a4d332bd6d44fa8e
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2025-07-28 15:28:26 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2025-07-28 15:28:59 +0000

    comsat: Don't read arbitrary files
    
    When processing a notification, instead of accepting any file name
    that doesn't begin with a slash, accept only file names that don't
    contain any slashes at all.  This makes it possible to notify a
    user about a mailbox that doesn't bear their name, as long as they
    are permitted to read it, but prevents comsat from reading files
    outside the mail spool.
    
    PR:             270404
    MFC after:      1 week
    Reviewed by:    emaste
    Differential Revision:  https://reviews.freebsd.org/D51580
---
 libexec/comsat/comsat.c | 38 +++++++++++++-------------------------
 1 file changed, 13 insertions(+), 25 deletions(-)

diff --git a/libexec/comsat/comsat.c b/libexec/comsat/comsat.c
index d5d1eedeb5f3..60d4f65519d0 100644
--- a/libexec/comsat/comsat.c
+++ b/libexec/comsat/comsat.c
@@ -113,29 +113,24 @@ mailfor(char *name)
 	char *file;
 	off_t offset;
 	int folder;
-	char buf[sizeof(_PATH_MAILDIR) + sizeof(utp->ut_user) + 1];
-	char buf2[sizeof(_PATH_MAILDIR) + sizeof(utp->ut_user) + 1];
+	char buf[MAXPATHLEN];
 
-	if (!(cp = strchr(name, '@')))
+	if ((cp = strchr(name, '@')) == NULL)
 		return;
 	*cp = '\0';
 	offset = strtoll(cp + 1, NULL, 10);
-	if (!(cp = strchr(cp + 1, ':')))
-		file = name;
-	else
-		file = cp + 1;
-	sprintf(buf, "%s/%.*s", _PATH_MAILDIR, (int)sizeof(utp->ut_user),
-	    name);
-	if (*file != '/') {
-		sprintf(buf2, "%s/%.*s", _PATH_MAILDIR,
-		    (int)sizeof(utp->ut_user), file);
-		file = buf2;
+	if ((cp = strchr(cp + 1, ':')) != NULL &&
+	    strchr((file = cp + 1), '/') == NULL) {
+		snprintf(buf, sizeof(buf), "%s/%s", _PATH_MAILDIR, file);
+		folder = 1;
+	} else {
+		snprintf(buf, sizeof(buf), "%s/%s", _PATH_MAILDIR, name);
+		folder = 0;
 	}
-	folder = strcmp(buf, file);
 	setutxent();
 	while ((utp = getutxent()) != NULL)
 		if (utp->ut_type == USER_PROCESS && !strcmp(utp->ut_user, name))
-			notify(utp, file, offset, folder);
+			notify(utp, buf, offset, folder);
 	endutxent();
 }
 
@@ -159,8 +154,7 @@ notify(struct utmpx *utp, char file[], off_t offset, int folder)
 		    utp->ut_line);
 		return;
 	}
-	(void)snprintf(tty, sizeof(tty), "%s%.*s",
-	    _PATH_DEV, (int)sizeof(utp->ut_line), utp->ut_line);
+	(void)snprintf(tty, sizeof(tty), "%s%s", _PATH_DEV, utp->ut_line);
 	if (stat(tty, &stb) == -1 || !(stb.st_mode & (S_IXUSR | S_IXGRP))) {
 		dsyslog(LOG_DEBUG, "%s: wrong mode on %s", utp->ut_user, tty);
 		return;
@@ -189,24 +183,18 @@ notify(struct utmpx *utp, char file[], off_t offset, int folder)
 	    setuid(p->pw_uid) == -1)
 		return;
 
-	switch (stb.st_mode & (S_IXUSR | S_IXGRP)) {
-	case S_IXUSR:
-	case (S_IXUSR | S_IXGRP):
+	if (stb.st_mode & S_IXUSR) {
 		(void)fprintf(tp, 
 		    "%s\007New mail for %s@%.*s\007 has arrived%s%s%s:%s----%s",
 		    cr, utp->ut_user, (int)sizeof(hostname), hostname,
 		    folder ? cr : "", folder ? "to " : "", folder ? file : "",
 		    cr, cr);
 		jkfprintf(tp, file, offset);
-		break;
-	case S_IXGRP:
+	} else if (stb.st_mode & S_IXGRP) {
 		(void)fprintf(tp, "\007");
 		(void)fflush(tp);      
 		(void)sleep(1);
 		(void)fprintf(tp, "\007");
-		break;
-	default:
-		break;
 	}	
 	(void)fclose(tp);
 	_exit(0);