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