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 64 int debug = 0; 65 #define dsyslog if (debug) syslog 66 67 struct sockaddr_in sin = { AF_INET }; 68 69 char hostname[MAXHOSTNAMELEN]; 70 struct utmpx *utmp = NULL; 71 int nutmp; 72 int uf; 73 unsigned utmpmtime = 0; /* last modification time for utmp */ 74 unsigned utmpsize = 0; /* last malloced size for utmp */ 75 time_t lastmsgtime; 76 77 #ifndef SYSV 78 int 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 93 static int 94 sigsetmask(mask) 95 int 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 106 static int 107 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 void jkfprintf(FILE *tp, char *name, int mbox, int offset); 126 void mailfor(char *name); 127 void notify(struct utmpx *utp, int offset); 128 void onalrm(int sig); 129 130 int 131 main(argc, argv) 132 int argc; 133 char *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 205 reapchildren() 206 { 207 208 while (wait3((struct wait *)0, WNOHANG, (struct rusage *)0) > 0) 209 ; 210 } 211 #endif /* SYSV */ 212 213 /* ARGSUSED */ 214 void 215 onalrm(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 246 void 247 mailfor(name) 248 char *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 277 char *cr; 278 279 void 280 notify(utp, offset) 281 struct 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, >tybuf) == -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 404 void 405 jkfprintf(tp, name, mbox, offset) 406 register FILE *tp; 407 char *name; 408 int 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