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