1/*
2 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6/*
7 * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
8 * All Rights Reserved.
9 */
10
11/*
12 * Copyright (c) 1980 Regents of the University of California.
13 * All rights reserved.
14 *
15 * Redistribution and use in source and binary forms are permitted provided
16 * that: (1) source distributions retain this entire copyright notice and
17 * comment, and (2) distributions including binaries display the following
18 * acknowledgement: ``This product includes software developed by the
19 * University of California, Berkeley and its contributors'' in the
20 * documentation or other materials provided with the distribution and in
21 * all advertising materials mentioning features or use of this software.
22 * Neither the name of the University nor the names of its contributors may
23 * be used to endorse or promote products derived from this software without
24 * specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
26 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
27 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
28 */
29
30#pragma ident	"%Z%%M%	%I%	%E% SMI"
31
32
33#include <sys/types.h>
34#include <sys/socket.h>
35#include <sys/stat.h>
36#include <sys/wait.h>
37#include <sys/file.h>
38#include <fcntl.h>
39#include <ctype.h>
40#include <string.h>
41
42#include <netinet/in.h>
43
44#include <stdio.h>
45#include <sys/ttold.h>
46#include <utmpx.h>
47#include <signal.h>
48#include <errno.h>
49#include <sys/param.h>	/* for MAXHOSTNAMELEN */
50#include <netdb.h>
51#include <syslog.h>
52#include <sys/ioctl.h>
53#include <pwd.h>
54
55/*
56 * comsat
57 */
58
59
60#ifndef UTMPX_FILE
61#define	UTMPX_FILE "/etc/utmpx"
62#endif	/* UTMPX_FILE */
63
64int	debug = 0;
65#define	dsyslog	if (debug) syslog
66
67struct	sockaddr_in sin = { AF_INET };
68
69char	hostname[MAXHOSTNAMELEN];
70struct	utmpx *utmp = NULL;
71int	nutmp;
72int	uf;
73unsigned utmpmtime = 0;			/* last modification time for utmp */
74unsigned utmpsize = 0;			/* last malloced size for utmp */
75time_t	lastmsgtime;
76
77#ifndef SYSV
78int	reapchildren();
79#else
80
81#define	rindex strrchr
82#define	index strchr
83#define	signal(s, f)	sigset((s), (f))
84
85#ifndef sigmask
86#define	sigmask(m)	(1 << ((m)-1))
87#endif
88
89#define	set2mask(setp)	((setp)->__sigbits[0])
90#define	mask2set(mask, setp) \
91	((mask) == -1 ? sigfillset(setp) : (((setp)->__sigbits[0]) = (mask)))
92
93static int
94sigsetmask(mask)
95int mask;
96{
97	sigset_t oset;
98	sigset_t nset;
99
100	(void) sigprocmask(0, (sigset_t *)0, &nset);
101	mask2set(mask, &nset);
102	(void) sigprocmask(SIG_SETMASK, &nset, &oset);
103	return (set2mask(&oset));
104}
105
106static int
107sigblock(mask)
108int mask;
109{
110	sigset_t oset;
111	sigset_t nset;
112
113	(void) sigprocmask(0, (sigset_t *)0, &nset);
114	mask2set(mask, &nset);
115	(void) sigprocmask(SIG_BLOCK, &nset, &oset);
116	return (set2mask(&oset));
117}
118
119#endif /* SYSV */
120
121
122#define	MAXIDLE	120
123#define	NAMLEN (sizeof (uts[0].ut_name) + 1)
124
125void jkfprintf(FILE *tp, char *name, int mbox, int offset);
126void mailfor(char *name);
127void notify(struct utmpx *utp, int offset);
128void onalrm(int sig);
129
130int
131main(argc, argv)
132int argc;
133char *argv[];
134{
135	register int cc;
136	char buf[BUFSIZ];
137	char msgbuf[100];
138	struct sockaddr_in from;
139	socklen_t fromlen;
140	int c;
141	extern int optind;
142	extern int getopt();
143	extern char *optarg;
144
145	openlog("comsat", 0, LOG_DAEMON);
146
147	while ((c = getopt(argc, argv, "d")) != -1) {
148		switch ((char)c) {
149		case'd':
150			debug++;
151			break;
152		default:
153			syslog(LOG_ERR, "invalid argument %s", argv[optind]);
154			exit(1);
155		}
156	}
157
158	/* verify proper invocation */
159	fromlen = (socklen_t)sizeof (from);
160	if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
161		fprintf(stderr, "%s: ", argv[0]);
162		perror("getsockname");
163		_exit(1);
164	}
165
166#ifdef SYSV
167	chdir("/var/mail");
168#else
169	chdir("/var/spool/mail");
170#endif /* SYSV */
171	if ((uf = open(UTMPX_FILE, 0)) < 0) {
172		syslog(LOG_ERR, "%s: %m", UTMPX_FILE);
173		(void) recv(0, msgbuf, sizeof (msgbuf) - 1, 0);
174		exit(1);
175	}
176	(void) time(&lastmsgtime);
177	(void) gethostname(hostname, sizeof (hostname));
178	onalrm(0);
179	(void) signal(SIGALRM, onalrm);
180	(void) signal(SIGTTOU, SIG_IGN);
181#ifndef SYSV
182	(void) signal(SIGCHLD, reapchildren);
183#else
184	(void) signal(SIGCHLD, SIG_IGN); /* no zombies */
185#endif /* SYSV */
186	for (;;) {
187		cc = recv(0, msgbuf, sizeof (msgbuf) - 1, 0);
188		if (cc <= 0) {
189			if (errno != EINTR)
190				sleep(1);
191			errno = 0;
192			continue;
193		}
194		if (nutmp == 0)			/* no users (yet) */
195			continue;
196		sigblock(sigmask(SIGALRM));
197		msgbuf[cc] = 0;
198		(void) time(&lastmsgtime);
199		mailfor(msgbuf);
200		sigsetmask(0);
201	}
202}
203
204#ifndef SYSV
205reapchildren()
206{
207
208	while (wait3((struct wait *)0, WNOHANG, (struct rusage *)0) > 0)
209		;
210}
211#endif /* SYSV */
212
213/* ARGSUSED */
214void
215onalrm(int sig)
216{
217	struct stat statbf;
218	time_t now;
219
220	(void) time(&now);
221	if ((ulong_t)now - (ulong_t)lastmsgtime >= MAXIDLE)
222		exit(0);
223	dsyslog(LOG_DEBUG, "alarm\n");
224	alarm(15);
225	fstat(uf, &statbf);
226	if (statbf.st_mtime > utmpmtime) {
227		dsyslog(LOG_DEBUG, " changed\n");
228		utmpmtime = statbf.st_mtime;
229		if (statbf.st_size > utmpsize) {
230			utmpsize = statbf.st_size + 10 * sizeof (struct utmpx);
231			if (utmp)
232				utmp = (struct utmpx *)realloc(utmp, utmpsize);
233			else
234				utmp = (struct utmpx *)malloc(utmpsize);
235			if (! utmp) {
236				dsyslog(LOG_DEBUG, "malloc failed\n");
237				exit(1);
238			}
239		}
240		lseek(uf, 0, 0);
241		nutmp = read(uf, utmp, statbf.st_size)/sizeof (struct utmpx);
242	} else
243		dsyslog(LOG_DEBUG, " ok\n");
244}
245
246void
247mailfor(name)
248char *name;
249{
250	struct utmpx *utp = &utmp[nutmp];
251	register char *cp;
252	char *rindex();
253	int offset;
254
255	/*
256	 * Don't bother doing anything if nobody is
257	 * logged into the system.
258	 */
259	if (utmp == NULL || nutmp == 0)
260		return;
261	dsyslog(LOG_DEBUG, "mailfor %s\n", name);
262	cp = name;
263	while (*cp && *cp != '@')
264		cp++;
265	if (*cp == 0) {
266		dsyslog(LOG_DEBUG, "bad format\n");
267		return;
268	}
269	*cp = 0;
270	offset = atoi(cp+1);
271	while (--utp >= utmp)
272		if ((utp->ut_type == USER_PROCESS) &&
273		    (!strncmp(utp->ut_name, name, sizeof (utmp[0].ut_name))))
274			notify(utp, offset);
275}
276
277char	*cr;
278
279void
280notify(utp, offset)
281struct utmpx *utp;
282{
283	FILE *tp;
284	struct sgttyb gttybuf;
285	char tty[sizeof (utmp[0].ut_line) + 5];
286	char name[sizeof (utmp[0].ut_name) + 1];
287	struct stat stb, stl;
288	time_t timep[2];
289	struct passwd *pwd;
290	int fd, mbox;
291
292
293	strcpy(tty, "/dev/");
294	strncat(tty, utp->ut_line, sizeof (utp->ut_line));
295	dsyslog(LOG_DEBUG, "notify %s on %s\n", utp->ut_name, tty);
296	if (stat(tty, &stb) == -1) {
297		dsyslog(LOG_DEBUG, "can't stat tty\n");
298		return;
299	}
300	if ((stb.st_mode & 0100) == 0) {
301		dsyslog(LOG_DEBUG, "wrong mode\n");
302		return;
303	}
304	if (fork())
305		return;
306	signal(SIGALRM, SIG_DFL);
307	alarm(30);
308
309	strncpy(name, utp->ut_name, sizeof (utp->ut_name));
310	name[sizeof (name) - 1] = '\0';
311
312	/*
313	 * Do all operations that check protections as the user who
314	 * will be getting the biff.
315	 */
316	if ((pwd = getpwnam(name)) == (struct passwd *)-1) {
317		dsyslog(LOG_DEBUG, "getpwnam failed\n");
318		exit(1);
319	}
320	if (setuid(pwd->pw_uid) == -1) {
321		dsyslog(LOG_DEBUG, "setuid failed\n");
322		exit(1);
323	}
324
325	/*
326	 * We need to make sure that the tty listed in the utmp
327	 * file really is a tty device so that a corrupted utmp
328	 * file doesn't cause us to over-write a real file.
329	 */
330	if ((fd = open(tty, O_RDWR)) == -1) {
331		dsyslog(LOG_DEBUG, "can't open tty");
332		exit(1);
333	}
334	if (isatty(fd) == 0) {
335		dsyslog(LOG_DEBUG, "line listed in utmp file is not a tty\n");
336		exit(1);
337	}
338
339	/*
340	 * For the case where the user getting the biff is root,
341	 * we need to make sure that the tty we will be sending
342	 * the biff to is also owned by root.
343	 *
344	 * Check after open, to prevent race on open.
345	 */
346
347	if (fstat(fd, &stb) != 0 || stb.st_uid != pwd->pw_uid) {
348		dsyslog(LOG_DEBUG,
349		    "tty is not owned by user getting the biff\n");
350		exit(1);
351	}
352
353	/*
354	 * Prevent race by doing fdopen on fd, not fopen
355	 * Fopen opens w/ O_CREAT, which is dangerous too
356	 */
357	if ((tp = fdopen(fd, "w")) == 0) {
358		dsyslog(LOG_DEBUG, "fdopen failed\n");
359		exit(-1);
360	}
361
362	if (ioctl(fd, TIOCGETP, &gttybuf) == -1) {
363		dsyslog(LOG_DEBUG, "ioctl TIOCGETP failed\n");
364		exit(1);
365	}
366	cr = (gttybuf.sg_flags&CRMOD) && !(gttybuf.sg_flags&RAW) ? "" : "\r";
367	fprintf(tp, "%s\n\007New mail for %s@%.*s\007 has arrived:%s\n",
368	    cr, name, sizeof (hostname), hostname, cr);
369	fprintf(tp, "----%s\n", cr);
370
371	if ((mbox = open(name, O_RDONLY)) == -1) {
372		dsyslog(LOG_DEBUG, "can't open mailbox for %s", name);
373		exit(1);
374	}
375	/*
376	 * In case of a worldwritable mail spool directory, we must take
377	 * care we don't open and read from the wrong file.
378	 */
379	if (fstat(mbox, &stb) == -1 || lstat(name, &stl) == -1) {
380		dsyslog(LOG_DEBUG, "stat() failed on mail file\n");
381		exit(1);
382	}
383
384	/*
385	 * Here we make sure that the file wasn't a hardlink or softlink
386	 * while we opened it and that it wasn't changed afterwards
387	 */
388	if (!S_ISREG(stl.st_mode) ||
389	    stl.st_dev != stb.st_dev ||
390	    stl.st_ino != stb.st_ino ||
391	    stl.st_uid != pwd->pw_uid ||
392	    stb.st_nlink != 1) {
393		dsyslog(LOG_DEBUG, "mail spool file must be plain file\n");
394		exit(1);
395	}
396
397	timep[0] = stb.st_atime;
398	timep[1] = stb.st_mtime;
399	jkfprintf(tp, name, mbox, offset);
400	utime(name, timep);
401	exit(0);
402}
403
404void
405jkfprintf(tp, name, mbox, offset)
406register FILE *tp;
407char *name;
408int mbox;
409{
410	register FILE *fi;
411	register int linecnt, charcnt;
412	char line[BUFSIZ];
413	int inheader;
414
415	dsyslog(LOG_DEBUG, "HERE %s's mail starting at %d\n",
416	    name, offset);
417	if ((fi = fdopen(mbox, "r")) == NULL) {
418		dsyslog(LOG_DEBUG, "Cant read the mail\n");
419		return;
420	}
421
422	fseek(fi, offset, L_SET);
423
424	/*
425	 * Print the first 7 lines or 560 characters of the new mail
426	 * (whichever comes first).  Skip header crap other than
427	 * From, Subject, To, and Date.
428	 */
429	linecnt = 7;
430	charcnt = 560;
431	inheader = 1;
432
433
434	while (fgets(line, sizeof (line), fi) != NULL) {
435		register char *cp;
436		char *index();
437		int cnt;
438		int i;
439
440		if (linecnt <= 0 || charcnt <= 0) {
441			fprintf(tp, "...more...%s\n", cr);
442			return;
443		}
444		if (strncmp(line, "From ", 5) == 0)
445			continue;
446		if (inheader && (line[0] == ' ' || line[0] == '\t'))
447			continue;
448		cp = index(line, ':');
449		if (cp == 0 || (index(line, ' ') && index(line, ' ') < cp))
450			inheader = 0;
451		else
452			cnt = cp - line;
453		if (inheader &&
454		    strncmp(line, "Date", cnt) &&
455		    strncmp(line, "From", cnt) &&
456		    strncmp(line, "Subject", cnt) &&
457		    strncmp(line, "To", cnt))
458			continue;
459		cp = index(line, '\n');
460		if (cp)
461			*cp = '\0';
462
463		for (i = strlen(line); i-- > 0; )
464			if (!isprint(line[i]))
465				line[i] = ' ';
466
467
468		fprintf(tp, "%s%s\n", line, cr);
469		linecnt--, charcnt -= strlen(line);
470	}
471	fprintf(tp, "----%s\n", cr);
472}
473