xref: /freebsd/libexec/comsat/comsat.c (revision 59c8e88e72633afbc47a4ace0d2170d00d51f7dc)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1980, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/param.h>
33 #include <sys/socket.h>
34 #include <sys/stat.h>
35 #include <sys/file.h>
36 #include <sys/wait.h>
37 
38 #include <netinet/in.h>
39 
40 #include <ctype.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <netdb.h>
44 #include <paths.h>
45 #include <pwd.h>
46 #include <termios.h>
47 #include <signal.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <syslog.h>
52 #include <unistd.h>
53 #include <utmpx.h>
54 
55 static int	debug = 0;
56 #define	dsyslog	if (debug) syslog
57 
58 #define MAXIDLE	120
59 
60 static char	hostname[MAXHOSTNAMELEN];
61 
62 static void	jkfprintf(FILE *, char[], char[], off_t);
63 static void	mailfor(char *);
64 static void	notify(struct utmpx *, char[], off_t, int);
65 static void	reapchildren(int);
66 
67 int
68 main(int argc __unused, char *argv[] __unused)
69 {
70 	struct sockaddr_in from;
71 	socklen_t fromlen;
72 	int cc;
73 	char msgbuf[256];
74 
75 	/* verify proper invocation */
76 	fromlen = sizeof(from);
77 	if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0)
78 		err(1, "getsockname");
79 	openlog("comsat", LOG_PID, LOG_DAEMON);
80 	if (chdir(_PATH_MAILDIR)) {
81 		syslog(LOG_ERR, "chdir: %s: %m", _PATH_MAILDIR);
82 		(void) recv(0, msgbuf, sizeof(msgbuf) - 1, 0);
83 		exit(1);
84 	}
85 	(void)gethostname(hostname, sizeof(hostname));
86 	(void)signal(SIGTTOU, SIG_IGN);
87 	(void)signal(SIGCHLD, reapchildren);
88 	for (;;) {
89 		cc = recv(0, msgbuf, sizeof(msgbuf) - 1, 0);
90 		if (cc <= 0) {
91 			if (errno != EINTR)
92 				sleep(1);
93 			errno = 0;
94 			continue;
95 		}
96 		msgbuf[cc] = '\0';
97 		mailfor(msgbuf);
98 		sigsetmask(0L);
99 	}
100 }
101 
102 static void
103 reapchildren(int signo __unused)
104 {
105 	while (wait3(NULL, WNOHANG, NULL) > 0);
106 }
107 
108 static void
109 mailfor(char *name)
110 {
111 	struct utmpx *utp;
112 	char *cp;
113 	char *file;
114 	off_t offset;
115 	int folder;
116 	char buf[sizeof(_PATH_MAILDIR) + sizeof(utp->ut_user) + 1];
117 	char buf2[sizeof(_PATH_MAILDIR) + sizeof(utp->ut_user) + 1];
118 
119 	if (!(cp = strchr(name, '@')))
120 		return;
121 	*cp = '\0';
122 	offset = strtoll(cp + 1, NULL, 10);
123 	if (!(cp = strchr(cp + 1, ':')))
124 		file = name;
125 	else
126 		file = cp + 1;
127 	sprintf(buf, "%s/%.*s", _PATH_MAILDIR, (int)sizeof(utp->ut_user),
128 	    name);
129 	if (*file != '/') {
130 		sprintf(buf2, "%s/%.*s", _PATH_MAILDIR,
131 		    (int)sizeof(utp->ut_user), file);
132 		file = buf2;
133 	}
134 	folder = strcmp(buf, file);
135 	setutxent();
136 	while ((utp = getutxent()) != NULL)
137 		if (utp->ut_type == USER_PROCESS && !strcmp(utp->ut_user, name))
138 			notify(utp, file, offset, folder);
139 	endutxent();
140 }
141 
142 static const char *cr;
143 
144 static void
145 notify(struct utmpx *utp, char file[], off_t offset, int folder)
146 {
147 	FILE *tp;
148 	struct stat stb;
149 	struct termios tio;
150 	char tty[20];
151 	const char *s = utp->ut_line;
152 
153 	if (strncmp(s, "pts/", 4) == 0)
154 		s += 4;
155 	if (strchr(s, '/')) {
156 		/* A slash is an attempt to break security... */
157 		syslog(LOG_AUTH | LOG_NOTICE, "Unexpected `/' in `%s'",
158 		    utp->ut_line);
159 		return;
160 	}
161 	(void)snprintf(tty, sizeof(tty), "%s%.*s",
162 	    _PATH_DEV, (int)sizeof(utp->ut_line), utp->ut_line);
163 	if (stat(tty, &stb) == -1 || !(stb.st_mode & (S_IXUSR | S_IXGRP))) {
164 		dsyslog(LOG_DEBUG, "%s: wrong mode on %s", utp->ut_user, tty);
165 		return;
166 	}
167 	dsyslog(LOG_DEBUG, "notify %s on %s", utp->ut_user, tty);
168 	switch (fork()) {
169 	case -1:
170 		syslog(LOG_NOTICE, "fork failed (%m)");
171 		return;
172 	case 0:
173 		break;
174 	default:
175 		return;
176 	}
177 	if ((tp = fopen(tty, "w")) == NULL) {
178 		dsyslog(LOG_ERR, "%s: %s", tty, strerror(errno));
179 		_exit(1);
180 	}
181 	(void)tcgetattr(fileno(tp), &tio);
182 	cr = ((tio.c_oflag & (OPOST|ONLCR)) == (OPOST|ONLCR)) ?  "\n" : "\n\r";
183 	switch (stb.st_mode & (S_IXUSR | S_IXGRP)) {
184 	case S_IXUSR:
185 	case (S_IXUSR | S_IXGRP):
186 		(void)fprintf(tp,
187 		    "%s\007New mail for %s@%.*s\007 has arrived%s%s%s:%s----%s",
188 		    cr, utp->ut_user, (int)sizeof(hostname), hostname,
189 		    folder ? cr : "", folder ? "to " : "", folder ? file : "",
190 		    cr, cr);
191 		jkfprintf(tp, utp->ut_user, file, offset);
192 		break;
193 	case S_IXGRP:
194 		(void)fprintf(tp, "\007");
195 		(void)fflush(tp);
196 		(void)sleep(1);
197 		(void)fprintf(tp, "\007");
198 		break;
199 	default:
200 		break;
201 	}
202 	(void)fclose(tp);
203 	_exit(0);
204 }
205 
206 static void
207 jkfprintf(FILE *tp, char user[], char file[], off_t offset)
208 {
209 	unsigned char *cp, ch;
210 	FILE *fi;
211 	int linecnt, charcnt, inheader;
212 	struct passwd *p;
213 	unsigned char line[BUFSIZ];
214 
215 	/* Set effective uid to user in case mail drop is on nfs */
216 	if ((p = getpwnam(user)) != NULL)
217 		(void) setuid(p->pw_uid);
218 
219 	if ((fi = fopen(file, "r")) == NULL)
220 		return;
221 
222 	(void)fseeko(fi, offset, SEEK_CUR);
223 	/*
224 	 * Print the first 7 lines or 560 characters of the new mail
225 	 * (whichever comes first).  Skip header crap other than
226 	 * From, Subject, To, and Date.
227 	 */
228 	linecnt = 7;
229 	charcnt = 560;
230 	inheader = 1;
231 	while (fgets(line, sizeof(line), fi) != NULL) {
232 		if (inheader) {
233 			if (line[0] == '\n') {
234 				inheader = 0;
235 				continue;
236 			}
237 			if (line[0] == ' ' || line[0] == '\t' ||
238 			    (strncmp(line, "From:", 5) &&
239 			    strncmp(line, "Subject:", 8)))
240 				continue;
241 		}
242 		if (linecnt <= 0 || charcnt <= 0) {
243 			(void)fprintf(tp, "...more...%s", cr);
244 			(void)fclose(fi);
245 			return;
246 		}
247 		/* strip weird stuff so can't trojan horse stupid terminals */
248 		for (cp = line; (ch = *cp) && ch != '\n'; ++cp, --charcnt) {
249 			/* disable upper controls and enable all other
250 			   8bit codes due to lack of locale knowledge
251 			 */
252 			if (((ch & 0x80) && ch < 0xA0) ||
253 			    (!(ch & 0x80) && !isprint(ch) &&
254 			     !isspace(ch) && ch != '\a' && ch != '\b')
255 			   ) {
256 				if (ch & 0x80) {
257 					ch &= ~0x80;
258 					(void)fputs("M-", tp);
259 				}
260 				if (iscntrl(ch)) {
261 					ch ^= 0x40;
262 					(void)fputc('^', tp);
263 				}
264 			}
265 			(void)fputc(ch, tp);
266 		}
267 		(void)fputs(cr, tp);
268 		--linecnt;
269 	}
270 	(void)fprintf(tp, "----%s\n", cr);
271 	(void)fclose(fi);
272 }
273