1 /* 2 * Copyright 1998 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 64 int debug = 0; 65 #define dsyslog if (debug) syslog 66 67 struct sockaddr_in sin = { AF_INET }; 68 extern errno; 69 70 char hostname[MAXHOSTNAMELEN]; 71 struct utmpx *utmp = NULL; 72 int nutmp; 73 int uf; 74 unsigned utmpmtime = 0; /* last modification time for utmp */ 75 unsigned utmpsize = 0; /* last malloced size for utmp */ 76 int onalrm(); 77 time_t lastmsgtime; 78 79 #ifndef SYSV 80 int reapchildren(); 81 #else 82 83 #define rindex strrchr 84 #define index strchr 85 #define signal(s, f) sigset((s), (f)) 86 87 #ifndef sigmask 88 #define sigmask(m) (1 << ((m)-1)) 89 #endif 90 91 #define set2mask(setp) ((setp)->__sigbits[0]) 92 #define mask2set(mask, setp) \ 93 ((mask) == -1 ? sigfillset(setp) : (((setp)->__sigbits[0]) = (mask))) 94 95 static sigsetmask(mask) 96 int mask; 97 { 98 sigset_t oset; 99 sigset_t nset; 100 101 (void) sigprocmask(0, (sigset_t *)0, &nset); 102 mask2set(mask, &nset); 103 (void) sigprocmask(SIG_SETMASK, &nset, &oset); 104 return (set2mask(&oset)); 105 } 106 107 static sigblock(mask) 108 int 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 125 main(argc, argv) 126 int argc; 127 char *argv[]; 128 { 129 register int cc; 130 char buf[BUFSIZ]; 131 char msgbuf[100]; 132 struct sockaddr_in from; 133 socklen_t fromlen; 134 int c; 135 extern int optind; 136 extern int getopt(); 137 extern char *optarg; 138 139 openlog("comsat", 0, LOG_DAEMON); 140 141 while ((c = getopt(argc, argv, "d")) != -1) { 142 switch ((char)c) { 143 case'd': 144 debug++; 145 break; 146 default: 147 syslog(LOG_ERR, "invalid argument %s", argv[optind]); 148 exit(1); 149 } 150 } 151 152 /* verify proper invocation */ 153 fromlen = (socklen_t)sizeof (from); 154 if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { 155 fprintf(stderr, "%s: ", argv[0]); 156 perror("getsockname"); 157 _exit(1); 158 } 159 160 #ifdef SYSV 161 chdir("/var/mail"); 162 #else 163 chdir("/var/spool/mail"); 164 #endif /* SYSV */ 165 if ((uf = open(UTMPX_FILE, 0)) < 0) { 166 syslog(LOG_ERR, "%s: %m", UTMPX_FILE); 167 (void) recv(0, msgbuf, sizeof (msgbuf) - 1, 0); 168 exit(1); 169 } 170 (void) time(&lastmsgtime); 171 (void) gethostname(hostname, sizeof (hostname)); 172 onalrm(); 173 (void) signal(SIGALRM, (void (*)())onalrm); 174 (void) signal(SIGTTOU, SIG_IGN); 175 #ifndef SYSV 176 (void) signal(SIGCHLD, reapchildren); 177 #else 178 (void) signal(SIGCHLD, SIG_IGN); /* no zombies */ 179 #endif /* SYSV */ 180 for (;;) { 181 cc = recv(0, msgbuf, sizeof (msgbuf) - 1, 0); 182 if (cc <= 0) { 183 if (errno != EINTR) 184 sleep(1); 185 errno = 0; 186 continue; 187 } 188 if (nutmp == 0) /* no users (yet) */ 189 continue; 190 sigblock(sigmask(SIGALRM)); 191 msgbuf[cc] = 0; 192 (void) time(&lastmsgtime); 193 mailfor(msgbuf); 194 sigsetmask(0); 195 } 196 } 197 198 #ifndef SYSV 199 reapchildren() 200 { 201 202 while (wait3((struct wait *)0, WNOHANG, (struct rusage *)0) > 0) 203 ; 204 } 205 #endif /* SYSV */ 206 207 onalrm() 208 { 209 struct stat statbf; 210 time_t now; 211 212 (void) time(&now); 213 if ((ulong_t)now - (ulong_t)lastmsgtime >= MAXIDLE) 214 exit(0); 215 dsyslog(LOG_DEBUG, "alarm\n"); 216 alarm(15); 217 fstat(uf, &statbf); 218 if (statbf.st_mtime > utmpmtime) { 219 dsyslog(LOG_DEBUG, " changed\n"); 220 utmpmtime = statbf.st_mtime; 221 if (statbf.st_size > utmpsize) { 222 utmpsize = statbf.st_size + 10 * sizeof (struct utmpx); 223 if (utmp) 224 utmp = (struct utmpx *)realloc(utmp, utmpsize); 225 else 226 utmp = (struct utmpx *)malloc(utmpsize); 227 if (! utmp) { 228 dsyslog(LOG_DEBUG, "malloc failed\n"); 229 exit(1); 230 } 231 } 232 lseek(uf, 0, 0); 233 nutmp = read(uf, utmp, statbf.st_size)/sizeof (struct utmpx); 234 } else 235 dsyslog(LOG_DEBUG, " ok\n"); 236 } 237 238 mailfor(name) 239 char *name; 240 { 241 struct utmpx *utp = &utmp[nutmp]; 242 register char *cp; 243 char *rindex(); 244 int offset; 245 246 /* 247 * Don't bother doing anything if nobody is 248 * logged into the system. 249 */ 250 if (utmp == NULL || nutmp == 0) 251 return; 252 dsyslog(LOG_DEBUG, "mailfor %s\n", name); 253 cp = name; 254 while (*cp && *cp != '@') 255 cp++; 256 if (*cp == 0) { 257 dsyslog(LOG_DEBUG, "bad format\n"); 258 return; 259 } 260 *cp = 0; 261 offset = atoi(cp+1); 262 while (--utp >= utmp) 263 if ((utp->ut_type == USER_PROCESS) && 264 (!strncmp(utp->ut_name, name, sizeof (utmp[0].ut_name)))) 265 notify(utp, offset); 266 } 267 268 char *cr; 269 270 notify(utp, offset) 271 struct utmpx *utp; 272 { 273 FILE *tp; 274 struct sgttyb gttybuf; 275 char tty[sizeof (utmp[0].ut_line) + 5]; 276 char name[sizeof (utmp[0].ut_name) + 1]; 277 struct stat stb, stl; 278 time_t timep[2]; 279 struct passwd *pwd; 280 int fd, mbox; 281 282 283 strcpy(tty, "/dev/"); 284 strncat(tty, utp->ut_line, sizeof (utp->ut_line)); 285 dsyslog(LOG_DEBUG, "notify %s on %s\n", utp->ut_name, tty); 286 if (stat(tty, &stb) == -1) { 287 dsyslog(LOG_DEBUG, "can't stat tty\n"); 288 return; 289 } 290 if ((stb.st_mode & 0100) == 0) { 291 dsyslog(LOG_DEBUG, "wrong mode\n"); 292 return; 293 } 294 if (fork()) 295 return; 296 signal(SIGALRM, SIG_DFL); 297 alarm(30); 298 299 strncpy(name, utp->ut_name, sizeof (utp->ut_name)); 300 name[sizeof (name) - 1] = '\0'; 301 302 /* 303 * Do all operations that check protections as the user who 304 * will be getting the biff. 305 */ 306 if ((pwd = getpwnam(name)) == (struct passwd *)-1) { 307 dsyslog(LOG_DEBUG, "getpwnam failed\n"); 308 exit(1); 309 } 310 if (setuid(pwd->pw_uid) == -1) { 311 dsyslog(LOG_DEBUG, "setuid failed\n"); 312 exit(1); 313 } 314 315 /* 316 * We need to make sure that the tty listed in the utmp 317 * file really is a tty device so that a corrupted utmp 318 * file doesn't cause us to over-write a real file. 319 */ 320 if ((fd = open(tty, O_RDWR)) == -1) { 321 dsyslog(LOG_DEBUG, "can't open tty"); 322 exit(1); 323 } 324 if (isatty(fd) == 0) { 325 dsyslog(LOG_DEBUG, "line listed in utmp file is not a tty\n"); 326 exit(1); 327 } 328 329 /* 330 * For the case where the user getting the biff is root, 331 * we need to make sure that the tty we will be sending 332 * the biff to is also owned by root. 333 * 334 * Check after open, to prevent race on open. 335 */ 336 337 if (fstat(fd, &stb) != 0 || stb.st_uid != pwd->pw_uid) { 338 dsyslog(LOG_DEBUG, 339 "tty is not owned by user getting the biff\n"); 340 exit(1); 341 } 342 343 /* 344 * Prevent race by doing fdopen on fd, not fopen 345 * Fopen opens w/ O_CREAT, which is dangerous too 346 */ 347 if ((tp = fdopen(fd, "w")) == 0) { 348 dsyslog(LOG_DEBUG, "fdopen failed\n"); 349 exit(-1); 350 } 351 352 if (ioctl(fd, TIOCGETP, >tybuf) == -1) { 353 dsyslog(LOG_DEBUG, "ioctl TIOCGETP failed\n"); 354 exit(1); 355 } 356 cr = (gttybuf.sg_flags&CRMOD) && !(gttybuf.sg_flags&RAW) ? "" : "\r"; 357 fprintf(tp, "%s\n\007New mail for %s@%.*s\007 has arrived:%s\n", 358 cr, name, sizeof (hostname), hostname, cr); 359 fprintf(tp, "----%s\n", cr); 360 361 if ((mbox = open(name, O_RDONLY)) == -1) { 362 dsyslog(LOG_DEBUG, "can't open mailbox for %s", name); 363 exit(1); 364 } 365 /* 366 * In case of a worldwritable mail spool directory, we must take 367 * care we don't open and read from the wrong file. 368 */ 369 if (fstat(mbox, &stb) == -1 || lstat(name, &stl) == -1) { 370 dsyslog(LOG_DEBUG, "stat() failed on mail file\n"); 371 exit(1); 372 } 373 374 /* 375 * Here we make sure that the file wasn't a hardlink or softlink 376 * while we opened it and that it wasn't changed afterwards 377 */ 378 if (!S_ISREG(stl.st_mode) || 379 stl.st_dev != stb.st_dev || 380 stl.st_ino != stb.st_ino || 381 stl.st_uid != pwd->pw_uid || 382 stb.st_nlink != 1) { 383 dsyslog(LOG_DEBUG, "mail spool file must be plain file\n"); 384 exit(1); 385 } 386 387 timep[0] = stb.st_atime; 388 timep[1] = stb.st_mtime; 389 jkfprintf(tp, name, mbox, offset); 390 utime(name, timep); 391 exit(0); 392 } 393 394 jkfprintf(tp, name, mbox, offset) 395 register FILE *tp; 396 char *name; 397 int mbox; 398 { 399 register FILE *fi; 400 register int linecnt, charcnt; 401 char line[BUFSIZ]; 402 int inheader; 403 404 dsyslog(LOG_DEBUG, "HERE %s's mail starting at %d\n", 405 name, offset); 406 if ((fi = fdopen(mbox, "r")) == NULL) { 407 dsyslog(LOG_DEBUG, "Cant read the mail\n"); 408 return; 409 } 410 411 fseek(fi, offset, L_SET); 412 413 /* 414 * Print the first 7 lines or 560 characters of the new mail 415 * (whichever comes first). Skip header crap other than 416 * From, Subject, To, and Date. 417 */ 418 linecnt = 7; 419 charcnt = 560; 420 inheader = 1; 421 422 423 while (fgets(line, sizeof (line), fi) != NULL) { 424 register char *cp; 425 char *index(); 426 int cnt; 427 int i; 428 429 if (linecnt <= 0 || charcnt <= 0) { 430 fprintf(tp, "...more...%s\n", cr); 431 return; 432 } 433 if (strncmp(line, "From ", 5) == 0) 434 continue; 435 if (inheader && (line[0] == ' ' || line[0] == '\t')) 436 continue; 437 cp = index(line, ':'); 438 if (cp == 0 || (index(line, ' ') && index(line, ' ') < cp)) 439 inheader = 0; 440 else 441 cnt = cp - line; 442 if (inheader && 443 strncmp(line, "Date", cnt) && 444 strncmp(line, "From", cnt) && 445 strncmp(line, "Subject", cnt) && 446 strncmp(line, "To", cnt)) 447 continue; 448 cp = index(line, '\n'); 449 if (cp) 450 *cp = '\0'; 451 452 for (i = strlen(line); i-- > 0; ) 453 if (!isprint(line[i])) 454 line[i] = ' '; 455 456 457 fprintf(tp, "%s%s\n", line, cr); 458 linecnt--, charcnt -= strlen(line); 459 } 460 fprintf(tp, "----%s\n", cr); 461 } 462