1 /* 2 * Copyright (c) 1998 Sendmail, Inc. All rights reserved. 3 * Copyright (c) 1990, 1993, 1994 4 * The Regents of the University of California. All rights reserved. 5 * 6 * By using this file, you agree to the terms and conditions set 7 * forth in the LICENSE file which can be found at the top level 8 * of the sendmail distribution. 9 */ 10 11 /* 12 * Copyright 1994-2007 Sun Microsystems, Inc. All rights reserved. 13 * Use is subject to license terms. 14 */ 15 16 #ifndef lint 17 static char copyright[] = 18 "@(#) Copyright (c) 1990, 1993, 1994\n\ 19 The Regents of the University of California. All rights reserved.\n"; 20 #endif /* not lint */ 21 22 #ifndef lint 23 static char sccsid[] = "@(#)mail.local.c 8.83 (Berkeley) 12/17/98"; 24 static char sccsi2[] = "%W% (Sun) %G%"; 25 #endif /* not lint */ 26 27 #include <sys/param.h> 28 #include <sys/stat.h> 29 #include <sys/socket.h> 30 #include <sys/file.h> 31 32 #include <netinet/in.h> 33 34 #include <errno.h> 35 #include <fcntl.h> 36 #include <netdb.h> 37 #include <pwd.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <signal.h> 41 #include <ctype.h> 42 #include <string.h> 43 #include <sysexits.h> 44 #include <time.h> 45 #include <unistd.h> 46 #include <maillock.h> 47 #include <grp.h> 48 49 #ifdef __STDC__ 50 #include <stdarg.h> 51 #else 52 #include <varargs.h> 53 #endif 54 55 #include <syslog.h> 56 57 #include <sysexits.h> 58 #include <ctype.h> 59 60 #include <sm/conf.h> 61 #include <sendmail/pathnames.h> 62 63 /* 64 ** If you don't have flock, you could try using lockf instead. 65 */ 66 67 #ifdef LDA_USE_LOCKF 68 # define flock(a, b) lockf(a, b, 0) 69 # ifdef LOCK_EX 70 # undef LOCK_EX 71 # endif /* LOCK_EX */ 72 # define LOCK_EX F_LOCK 73 #endif /* LDA_USE_LOCKF */ 74 75 #ifndef LOCK_EX 76 # include <sys/file.h> 77 #endif /* ! LOCK_EX */ 78 79 #ifndef MAILER_DAEMON 80 # define MAILER_DAEMON "MAILER-DAEMON" 81 #endif 82 83 typedef int bool; 84 85 #define FALSE 0 86 #define TRUE 1 87 88 bool EightBitMime = TRUE; /* advertise 8BITMIME in LMTP */ 89 static int eval = EX_OK; /* sysexits.h error value. */ 90 static int lmtpmode = 0; 91 bool bouncequota = FALSE; /* permanent error when over quota */ 92 93 #define _PATH_MAILDIR "/var/mail" 94 #define _PATH_LOCTMP "/tmp/local.XXXXXX" 95 #define _PATH_LOCHTMP "/tmp/lochd.XXXXXX" 96 #define FALSE 0 97 #define TRUE 1 98 #define MAXLINE 2048 99 100 static void deliver(int, int, char *, bool); 101 static void e_to_sys(int); 102 static void err(const char *fmt, ...); 103 static void notifybiff(char *); 104 static void store(char *, int); 105 static void usage(void); 106 static void vwarn(); 107 static void warn(const char *fmt, ...); 108 static void mailerr(const char *, const char *, ...); 109 static void sigterm_handler(); 110 111 static char unix_from_line[MAXLINE]; 112 static int ulen; 113 static int content_length; 114 static int bfd, hfd; /* temp file */ 115 static uid_t src_uid, targ_uid, saved_uid; 116 static int sigterm_caught; 117 118 int 119 main(argc, argv) 120 int argc; 121 char *argv[]; 122 { 123 struct passwd *pw; 124 int ch; 125 uid_t uid; 126 char *from; 127 struct group *grpptr; 128 void dolmtp(); 129 130 openlog("mail.local", 0, LOG_MAIL); 131 132 from = NULL; 133 pw = NULL; 134 sigterm_caught = FALSE; 135 136 (void) sigset(SIGTERM, sigterm_handler); 137 138 while ((ch = getopt(argc, argv, "7bdf:r:l")) != EOF) 139 switch (ch) { 140 case '7': /* Do not advertise 8BITMIME */ 141 EightBitMime = FALSE; 142 break; 143 144 case 'b': /* bounce mail when over quota. */ 145 bouncequota = TRUE; 146 break; 147 148 case 'd': /* Backward compatible. */ 149 break; 150 case 'f': 151 case 'r': /* Backward compatible. */ 152 if (from != NULL) { 153 warn("multiple -f options"); 154 usage(); 155 } 156 from = optarg; 157 break; 158 case 'l': 159 lmtpmode++; 160 break; 161 case '?': 162 default: 163 usage(); 164 } 165 argc -= optind; 166 argv += optind; 167 168 notifybiff(NULL); /* initialize biff structures */ 169 170 /* 171 * We expect sendmail will invoke us with saved id 0 172 * We then do setgid and setuid defore delivery 173 * setgid to mail group 174 */ 175 if ((grpptr = getgrnam("mail")) != NULL) 176 (void) setgid(grpptr->gr_gid); 177 saved_uid = geteuid(); 178 179 if (lmtpmode) { 180 if (saved_uid != 0) { 181 warn("only super-user can use -l option"); 182 exit(EX_CANTCREAT); 183 } 184 dolmtp(bouncequota); 185 } 186 187 if (!*argv) 188 usage(); 189 190 /* 191 * If from not specified, use the name from getlogin() if the 192 * uid matches, otherwise, use the name from the password file 193 * corresponding to the uid. 194 */ 195 uid = getuid(); 196 if (!from && (!(from = getlogin()) || 197 !(pw = getpwnam(from)) || pw->pw_uid != uid)) 198 from = (pw = getpwuid(uid)) ? pw->pw_name : "???"; 199 src_uid = pw ? pw->pw_uid : uid; 200 201 /* 202 * There is no way to distinguish the error status of one delivery 203 * from the rest of the deliveries. So, if we failed hard on one 204 * or more deliveries, but had no failures on any of the others, we 205 * return a hard failure. If we failed temporarily on one or more 206 * deliveries, we return a temporary failure regardless of the other 207 * failures. This results in the delivery being reattempted later 208 * at the expense of repeated failures and multiple deliveries. 209 */ 210 211 for (store(from, 0); *argv; ++argv) 212 deliver(hfd, bfd, *argv, bouncequota); 213 return (eval); 214 } 215 216 void 217 sigterm_handler() 218 { 219 sigterm_caught = TRUE; 220 (void) sigignore(SIGTERM); 221 } 222 223 char * 224 parseaddr(s) 225 char *s; 226 { 227 char *p; 228 int len; 229 230 if (*s++ != '<') 231 return NULL; 232 233 p = s; 234 235 /* at-domain-list */ 236 while (*p == '@') { 237 p++; 238 if (*p == '[') { 239 p++; 240 while (isascii(*p) && 241 (isalnum(*p) || *p == '.' || 242 *p == '-' || *p == ':')) 243 p++; 244 if (*p++ != ']') 245 return NULL; 246 } else { 247 while ((isascii(*p) && isalnum(*p)) || 248 strchr(".-_", *p)) 249 p++; 250 } 251 if (*p == ',' && p[1] == '@') 252 p++; 253 else if (*p == ':' && p[1] != '@') 254 p++; 255 else 256 return NULL; 257 } 258 259 s = p; 260 261 /* local-part */ 262 if (*p == '\"') { 263 p++; 264 while (*p && *p != '\"') { 265 if (*p == '\\') { 266 if (!*++p) 267 return NULL; 268 } 269 p++; 270 } 271 if (!*p++) 272 return NULL; 273 } else { 274 while (*p && *p != '@' && *p != '>') { 275 if (*p == '\\') { 276 if (!*++p) 277 return NULL; 278 } else { 279 if (*p <= ' ' || (*p & 128) || 280 strchr("<>()[]\\,;:\"", *p)) 281 return NULL; 282 } 283 p++; 284 } 285 } 286 287 /* @domain */ 288 if (*p == '@') { 289 p++; 290 if (*p == '[') { 291 p++; 292 while (isascii(*p) && 293 (isalnum(*p) || *p == '.' || 294 *p == '-' || *p == ':')) 295 p++; 296 if (*p++ != ']') 297 return NULL; 298 } else { 299 while ((isascii(*p) && isalnum(*p)) || 300 strchr(".-_", *p)) 301 p++; 302 } 303 } 304 305 if (*p++ != '>') 306 return NULL; 307 if (*p && *p != ' ') 308 return NULL; 309 len = p - s - 1; 310 311 if (*s == '\0' || len <= 0) 312 { 313 s = MAILER_DAEMON; 314 len = strlen(s); 315 } 316 317 p = malloc(len + 1); 318 if (p == NULL) { 319 printf("421 4.3.0 memory exhausted\r\n"); 320 exit(EX_TEMPFAIL); 321 } 322 323 strncpy(p, s, len); 324 p[len] = '\0'; 325 return p; 326 } 327 328 char * 329 process_recipient(addr) 330 char *addr; 331 { 332 if (getpwnam(addr) == NULL) { 333 return "550 5.1.1 user unknown"; 334 } 335 336 return NULL; 337 } 338 339 #define RCPT_GROW 30 340 341 void 342 dolmtp(bouncequota) 343 bool bouncequota; 344 { 345 char *return_path = NULL; 346 char **rcpt_addr = NULL; 347 int rcpt_num = 0; 348 int rcpt_alloc = 0; 349 bool gotlhlo = FALSE; 350 char myhostname[MAXHOSTNAMELEN]; 351 char buf[4096]; 352 char *err; 353 char *p; 354 int i; 355 356 gethostname(myhostname, sizeof myhostname - 1); 357 358 printf("220 %s LMTP ready\r\n", myhostname); 359 for (;;) { 360 if (sigterm_caught) { 361 for (; rcpt_num > 0; rcpt_num--) 362 printf("451 4.3.0 shutting down\r\n"); 363 exit(EX_OK); 364 } 365 fflush(stdout); 366 if (fgets(buf, sizeof(buf)-1, stdin) == NULL) { 367 exit(EX_OK); 368 } 369 p = buf + strlen(buf) - 1; 370 if (p >= buf && *p == '\n') 371 *p-- = '\0'; 372 if (p >= buf && *p == '\r') 373 *p-- = '\0'; 374 375 switch (buf[0]) { 376 377 case 'd': 378 case 'D': 379 if (strcasecmp(buf, "data") == 0) { 380 if (rcpt_num == 0) { 381 printf("503 5.5.1 No recipients\r\n"); 382 continue; 383 } 384 store(return_path, rcpt_num); 385 if (bfd == -1 || hfd == -1) 386 continue; 387 388 for (i = 0; i < rcpt_num; i++) { 389 p = strchr(rcpt_addr[i], '+'); 390 if (p != NULL) 391 *p++ = '\0'; 392 deliver(hfd, bfd, rcpt_addr[i], 393 bouncequota); 394 } 395 close(bfd); 396 close(hfd); 397 goto rset; 398 } 399 goto syntaxerr; 400 /* NOTREACHED */ 401 break; 402 403 case 'l': 404 case 'L': 405 if (strncasecmp(buf, "lhlo ", 5) == 0) 406 { 407 /* check for duplicate per RFC 1651 4.2 */ 408 if (gotlhlo) 409 { 410 printf("503 %s Duplicate LHLO\r\n", 411 myhostname); 412 continue; 413 } 414 gotlhlo = TRUE; 415 printf("250-%s\r\n", myhostname); 416 if (EightBitMime) 417 printf("250-8BITMIME\r\n"); 418 printf("250-ENHANCEDSTATUSCODES\r\n"); 419 printf("250 PIPELINING\r\n"); 420 continue; 421 } 422 goto syntaxerr; 423 /* NOTREACHED */ 424 break; 425 426 case 'm': 427 case 'M': 428 if (strncasecmp(buf, "mail ", 5) == 0) { 429 if (return_path != NULL) { 430 printf("503 5.5.1 Nested MAIL command\r\n"); 431 continue; 432 } 433 if (strncasecmp(buf+5, "from:", 5) != 0 || 434 ((return_path = parseaddr(buf+10)) == NULL)) { 435 printf("501 5.5.4 Syntax error in parameters\r\n"); 436 continue; 437 } 438 printf("250 2.5.0 ok\r\n"); 439 continue; 440 } 441 goto syntaxerr; 442 443 case 'n': 444 case 'N': 445 if (strcasecmp(buf, "noop") == 0) { 446 printf("250 2.0.0 ok\r\n"); 447 continue; 448 } 449 goto syntaxerr; 450 451 case 'q': 452 case 'Q': 453 if (strcasecmp(buf, "quit") == 0) { 454 printf("221 2.0.0 bye\r\n"); 455 exit(EX_OK); 456 } 457 goto syntaxerr; 458 459 case 'r': 460 case 'R': 461 if (strncasecmp(buf, "rcpt ", 5) == 0) { 462 if (return_path == NULL) { 463 printf("503 5.5.1 Need MAIL command\r\n"); 464 continue; 465 } 466 if (rcpt_num >= rcpt_alloc) { 467 rcpt_alloc += RCPT_GROW; 468 rcpt_addr = (char **) 469 realloc((char *)rcpt_addr, 470 rcpt_alloc * sizeof(char **)); 471 if (rcpt_addr == NULL) { 472 printf("421 4.3.0 memory exhausted\r\n"); 473 exit(EX_TEMPFAIL); 474 } 475 } 476 if (strncasecmp(buf+5, "to:", 3) != 0 || 477 ((rcpt_addr[rcpt_num] = parseaddr(buf+8)) == NULL)) { 478 printf("501 5.5.4 Syntax error in parameters\r\n"); 479 continue; 480 } 481 if ((err = process_recipient(rcpt_addr[rcpt_num])) != NULL) { 482 printf("%s\r\n", err); 483 continue; 484 } 485 rcpt_num++; 486 printf("250 2.1.5 ok\r\n"); 487 continue; 488 } 489 else if (strcasecmp(buf, "rset") == 0) { 490 printf("250 2.0.0 ok\r\n"); 491 492 rset: 493 while (rcpt_num > 0) { 494 free(rcpt_addr[--rcpt_num]); 495 } 496 if (return_path != NULL) 497 free(return_path); 498 return_path = NULL; 499 continue; 500 } 501 goto syntaxerr; 502 503 case 'v': 504 case 'V': 505 if (strncasecmp(buf, "vrfy ", 5) == 0) { 506 printf("252 2.3.3 try RCPT to attempt delivery\r\n"); 507 continue; 508 } 509 goto syntaxerr; 510 511 default: 512 syntaxerr: 513 printf("500 5.5.2 Syntax error\r\n"); 514 continue; 515 } 516 } 517 } 518 519 static void 520 store(from, lmtprcpts) 521 char *from; 522 int lmtprcpts; 523 { 524 FILE *fp = NULL; 525 time_t tval; 526 bool fullline = TRUE; /* current line is terminated */ 527 bool prevfl; /* previous line was terminated */ 528 char line[MAXLINE]; 529 FILE *bfp, *hfp; 530 char *btn, *htn; 531 int in_header_section; 532 int newfd; 533 534 bfd = -1; 535 hfd = -1; 536 btn = strdup(_PATH_LOCTMP); 537 if ((bfd = mkstemp(btn)) == -1 || (bfp = fdopen(bfd, "w+")) == NULL) { 538 if (bfd != -1) 539 (void) close(bfd); 540 if (lmtprcpts) { 541 printf("451 4.3.0 unable to open temporary file\r\n"); 542 return; 543 } else { 544 mailerr("451 4.3.0", "unable to open temporary file"); 545 exit(eval); 546 } 547 } 548 (void) unlink(btn); 549 free(btn); 550 551 if (lmtpmode) { 552 printf("354 go ahead\r\n"); 553 fflush(stdout); 554 } 555 556 htn = strdup(_PATH_LOCHTMP); 557 if ((hfd = mkstemp(htn)) == -1 || (hfp = fdopen(hfd, "w+")) == NULL) { 558 if (hfd != -1) 559 (void) close(hfd); 560 e_to_sys(errno); 561 err("unable to open temporary file"); 562 } 563 (void) unlink(htn); 564 free(htn); 565 566 in_header_section = TRUE; 567 content_length = 0; 568 fp = hfp; 569 570 line[0] = '\0'; 571 while (fgets(line, sizeof(line), stdin) != (char *)NULL) 572 { 573 size_t line_len = 0; 574 int peek; 575 576 prevfl = fullline; /* preserve state of previous line */ 577 while (line[line_len] != '\n' && line_len < sizeof(line) - 2) 578 line_len++; 579 line_len++; 580 581 /* Check for dot-stuffing */ 582 if (prevfl && lmtprcpts && line[0] == '.') 583 { 584 if (line[1] == '\n' || 585 (line[1] == '\r' && line[2] == '\n')) 586 goto lmtpdot; 587 memcpy(line, line + 1, line_len); 588 line_len--; 589 } 590 591 /* Check to see if we have the full line from fgets() */ 592 fullline = FALSE; 593 if (line_len > 0) 594 { 595 if (line[line_len - 1] == '\n') 596 { 597 if (line_len >= 2 && 598 line[line_len - 2] == '\r') 599 { 600 line[line_len - 2] = '\n'; 601 line[line_len - 1] = '\0'; 602 line_len--; 603 } 604 fullline = TRUE; 605 } 606 else if (line[line_len - 1] == '\r') 607 { 608 /* Did we just miss the CRLF? */ 609 peek = fgetc(stdin); 610 if (peek == '\n') 611 { 612 line[line_len - 1] = '\n'; 613 fullline = TRUE; 614 } 615 else 616 (void) ungetc(peek, stdin); 617 } 618 } 619 else 620 fullline = TRUE; 621 622 if (prevfl && line[0] == '\n' && in_header_section) { 623 in_header_section = FALSE; 624 if (fflush(fp) == EOF || ferror(fp)) { 625 if (lmtprcpts) { 626 while (lmtprcpts--) 627 printf("451 4.3.0 temporary file write error\r\n"); 628 fclose(fp); 629 return; 630 } else { 631 mailerr("451 4.3.0", 632 "temporary file write error"); 633 fclose(fp); 634 exit(eval); 635 } 636 } 637 fp = bfp; 638 continue; 639 } 640 641 if (in_header_section) { 642 if (strncasecmp("Content-Length:", line, 15) == 0) { 643 continue; /* skip this header */ 644 } 645 } else 646 content_length += strlen(line); 647 (void) fwrite(line, sizeof(char), line_len, fp); 648 if (ferror(fp)) { 649 if (lmtprcpts) { 650 while (lmtprcpts--) 651 printf("451 4.3.0 temporary file write error\r\n"); 652 fclose(fp); 653 return; 654 } else { 655 mailerr("451 4.3.0", 656 "temporary file write error"); 657 fclose(fp); 658 exit(eval); 659 } 660 } 661 } 662 if (sigterm_caught) { 663 if (lmtprcpts) 664 while (lmtprcpts--) 665 printf("451 4.3.0 shutting down\r\n"); 666 else 667 mailerr("451 4.3.0", "shutting down"); 668 fclose(fp); 669 exit(eval); 670 } 671 672 if (lmtprcpts) { 673 /* Got a premature EOF -- toss message and exit */ 674 exit(EX_OK); 675 } 676 677 /* If message not newline terminated, need an extra. */ 678 if (!strchr(line, '\n')) { 679 (void) putc('\n', fp); 680 content_length++; 681 } 682 683 lmtpdot: 684 685 /* Output a newline; note, empty messages are allowed. */ 686 (void) putc('\n', fp); 687 688 if (fflush(fp) == EOF || ferror(fp)) { 689 if (lmtprcpts) { 690 while (lmtprcpts--) { 691 printf("451 4.3.0 temporary file write error\r\n"); 692 } 693 fclose(fp); 694 return; 695 } else { 696 mailerr("451 4.3.0", "temporary file write error"); 697 fclose(fp); 698 exit(eval); 699 } 700 } 701 702 if ((newfd = dup(bfd)) >= 0) { 703 fclose(bfp); 704 bfd = newfd; 705 } 706 if ((newfd = dup(hfd)) >= 0) { 707 fclose(hfp); 708 hfd = newfd; 709 } 710 (void) time(&tval); 711 (void) snprintf(unix_from_line, sizeof (unix_from_line), "From %s %s", 712 from, ctime(&tval)); 713 ulen = strlen(unix_from_line); 714 } 715 716 static void 717 handle_error(err_num, bouncequota, path) 718 int err_num; 719 bool bouncequota; 720 char *path; 721 { 722 #ifdef EDQUOT 723 if (err_num == EDQUOT && bouncequota) { 724 mailerr("552 5.2.2", "%s: %s", path, sm_errstring(err_num)); 725 } else 726 #endif /* EDQUOT */ 727 mailerr("450 4.2.0", "%s: %s", path, sm_errstring(err_num)); 728 } 729 730 static void 731 deliver(hfd, bfd, name, bouncequota) 732 int hfd; 733 int bfd; 734 char *name; 735 bool bouncequota; 736 { 737 struct stat fsb, sb; 738 int mbfd = -1, nr, nw = 0, off; 739 char biffmsg[100], buf[8*1024], path[MAXPATHLEN]; 740 off_t curoff, cursize; 741 int len; 742 struct passwd *pw = NULL; 743 744 /* 745 * Disallow delivery to unknown names -- special mailboxes 746 * can be handled in the sendmail aliases file. 747 */ 748 if ((pw = getpwnam(name)) == NULL) { 749 eval = EX_TEMPFAIL; 750 mailerr("451 4.3.0", "cannot lookup name: %s", name); 751 return; 752 } 753 endpwent(); 754 755 if (sigterm_caught) { 756 mailerr("451 4.3.0", "shutting down"); 757 return; 758 } 759 760 /* mailbox may be NFS mounted, seteuid to user */ 761 targ_uid = pw->pw_uid; 762 (void) seteuid(targ_uid); 763 764 if ((saved_uid != 0) && (src_uid != targ_uid)) { 765 /* 766 * If saved_uid == 0 (root), anything is OK; this is 767 * as it should be. But to prevent a random user from 768 * calling "mail.local foo" in an attempt to hijack 769 * foo's mail-box, make sure src_uid == targ_uid o/w. 770 */ 771 warn("%s: wrong owner (is %d, should be %d)", 772 name, src_uid, targ_uid); 773 eval = EX_CANTCREAT; 774 return; 775 } 776 777 path[0] = '\0'; 778 (void) snprintf(path, sizeof (path), "%s/%s", _PATH_MAILDIR, name); 779 780 /* 781 * If the mailbox is linked or a symlink, fail. There's an obvious 782 * race here, that the file was replaced with a symbolic link after 783 * the lstat returned, but before the open. We attempt to detect 784 * this by comparing the original stat information and information 785 * returned by an fstat of the file descriptor returned by the open. 786 * 787 * NB: this is a symptom of a larger problem, that the mail spooling 788 * directory is writeable by the wrong users. If that directory is 789 * writeable, system security is compromised for other reasons, and 790 * it cannot be fixed here. 791 * 792 * If we created the mailbox, set the owner/group. If that fails, 793 * just return. Another process may have already opened it, so we 794 * can't unlink it. Historically, binmail set the owner/group at 795 * each mail delivery. We no longer do this, assuming that if the 796 * ownership or permissions were changed there was a reason. 797 * 798 * XXX 799 * open(2) should support flock'ing the file. 800 */ 801 tryagain: 802 /* should check lock status, but... maillock return no value */ 803 maillock(name, 10); 804 805 if (sigterm_caught) { 806 mailerr("451 4.3.0", "shutting down"); 807 goto err0; 808 } 809 810 if (lstat(path, &sb)) { 811 mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY, 812 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); 813 if (mbfd != -1) 814 (void) fchmod(mbfd, 0660); 815 816 817 if (mbfd == -1) { 818 if (errno == EEXIST) { 819 mailunlock(); 820 goto tryagain; 821 } 822 } 823 } else if (sb.st_nlink != 1) { 824 mailerr("550 5.2.0", "%s: too many links", path); 825 goto err0; 826 } else if (!S_ISREG(sb.st_mode)) { 827 mailerr("550 5.2.0", "%s: irregular file", path); 828 goto err0; 829 } else { 830 mbfd = open(path, O_APPEND|O_WRONLY, 0); 831 if (mbfd != -1 && 832 (fstat(mbfd, &fsb) || fsb.st_nlink != 1 || 833 S_ISLNK(fsb.st_mode) || sb.st_dev != fsb.st_dev || 834 sb.st_ino != fsb.st_ino)) { 835 eval = EX_TEMPFAIL; 836 mailerr("550 5.2.0", 837 "%s: fstat: file changed after open", path); 838 goto err1; 839 } 840 } 841 842 if (mbfd == -1) { 843 mailerr("450 4.2.0", "%s: %s", path, strerror(errno)); 844 goto err0; 845 } 846 847 if (sigterm_caught) { 848 mailerr("451 4.3.0", "shutting down"); 849 goto err0; 850 } 851 852 /* Get the starting offset of the new message for biff. */ 853 curoff = lseek(mbfd, (off_t)0, SEEK_END); 854 (void) snprintf(biffmsg, sizeof (biffmsg), "%s@%ld\n", name, curoff); 855 856 /* Copy the message into the file. */ 857 if (lseek(hfd, (off_t)0, SEEK_SET) == (off_t)-1) { 858 mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 859 goto err1; 860 } 861 /* Copy the message into the file. */ 862 if (lseek(bfd, (off_t)0, SEEK_SET) == (off_t)-1) { 863 mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 864 goto err1; 865 } 866 if ((write(mbfd, unix_from_line, ulen)) != ulen) { 867 handle_error(errno, bouncequota, path); 868 goto err2; 869 } 870 871 if (sigterm_caught) { 872 mailerr("451 4.3.0", "shutting down"); 873 goto err2; 874 } 875 876 while ((nr = read(hfd, buf, sizeof (buf))) > 0) 877 for (off = 0; off < nr; nr -= nw, off += nw) 878 if ((nw = write(mbfd, buf + off, nr)) < 0) 879 { 880 handle_error(errno, bouncequota, path); 881 goto err2; 882 } 883 if (nr < 0) { 884 handle_error(errno, bouncequota, path); 885 goto err2; 886 } 887 888 if (sigterm_caught) { 889 mailerr("451 4.3.0", "shutting down"); 890 goto err2; 891 } 892 893 (void) snprintf(buf, sizeof (buf), "Content-Length: %d\n\n", 894 content_length); 895 len = strlen(buf); 896 if (write(mbfd, buf, len) != len) { 897 handle_error(errno, bouncequota, path); 898 goto err2; 899 } 900 901 if (sigterm_caught) { 902 mailerr("451 4.3.0", "shutting down"); 903 goto err2; 904 } 905 906 while ((nr = read(bfd, buf, sizeof (buf))) > 0) { 907 for (off = 0; off < nr; nr -= nw, off += nw) 908 if ((nw = write(mbfd, buf + off, nr)) < 0) { 909 handle_error(errno, bouncequota, path); 910 goto err2; 911 } 912 if (sigterm_caught) { 913 mailerr("451 4.3.0", "shutting down"); 914 goto err2; 915 } 916 } 917 if (nr < 0) { 918 handle_error(errno, bouncequota, path); 919 goto err2; 920 } 921 922 /* Flush to disk, don't wait for update. */ 923 if (fsync(mbfd)) { 924 handle_error(errno, bouncequota, path); 925 err2: if (mbfd >= 0) 926 (void)ftruncate(mbfd, curoff); 927 err1: (void)close(mbfd); 928 err0: mailunlock(); 929 (void)seteuid(saved_uid); 930 return; 931 } 932 933 /* 934 ** Save the current size so if the close() fails below 935 ** we can make sure no other process has changed the mailbox 936 ** between the failed close and the re-open()/re-lock(). 937 ** If something else has changed the size, we shouldn't 938 ** try to truncate it as we may do more harm then good 939 ** (e.g., truncate a later message delivery). 940 */ 941 942 if (fstat(mbfd, &sb) < 0) 943 cursize = 0; 944 else 945 cursize = sb.st_size; 946 947 /* Close and check -- NFS doesn't write until the close. */ 948 if (close(mbfd)) 949 { 950 handle_error(errno, bouncequota, path); 951 mbfd = open(path, O_WRONLY, 0); 952 if (mbfd < 0 || 953 cursize == 0 954 || flock(mbfd, LOCK_EX) < 0 || 955 fstat(mbfd, &sb) < 0 || 956 sb.st_size != cursize || 957 sb.st_nlink != 1 || 958 !S_ISREG(sb.st_mode) || 959 sb.st_dev != fsb.st_dev || 960 sb.st_ino != fsb.st_ino || 961 sb.st_uid != fsb.st_uid) 962 { 963 /* Don't use a bogus file */ 964 if (mbfd >= 0) 965 { 966 (void) close(mbfd); 967 mbfd = -1; 968 } 969 } 970 971 /* Attempt to truncate back to pre-write size */ 972 goto err2; 973 } else 974 notifybiff(biffmsg); 975 976 mailunlock(); 977 978 (void)seteuid(saved_uid); 979 980 if (lmtpmode) { 981 printf("250 2.1.5 %s OK\r\n", name); 982 } 983 } 984 985 static void 986 notifybiff(msg) 987 char *msg; 988 { 989 static struct sockaddr_in addr; 990 static int f = -1; 991 struct hostent *hp; 992 struct servent *sp; 993 int len; 994 995 if (msg == NULL) { 996 /* Be silent if biff service not available. */ 997 if ((sp = getservbyname("biff", "udp")) == NULL) 998 return; 999 if ((hp = gethostbyname("localhost")) == NULL) { 1000 warn("localhost: %s", strerror(errno)); 1001 return; 1002 } 1003 addr.sin_family = hp->h_addrtype; 1004 (void) memmove(&addr.sin_addr, hp->h_addr, hp->h_length); 1005 addr.sin_port = sp->s_port; 1006 return; 1007 } 1008 1009 if (addr.sin_family == 0) 1010 return; /* did not initialize */ 1011 1012 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 1013 warn("socket: %s", strerror(errno)); 1014 return; 1015 } 1016 len = strlen(msg) + 1; 1017 if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof (addr)) 1018 != len) 1019 warn("sendto biff: %s", strerror(errno)); 1020 } 1021 1022 static void 1023 usage() 1024 { 1025 eval = EX_USAGE; 1026 err("usage: mail.local [-l] [-f from] user ..."); 1027 } 1028 1029 static void 1030 /*VARARGS2*/ 1031 #ifdef __STDC__ 1032 mailerr(const char *hdr, const char *fmt, ...) 1033 #else 1034 mailerr(hdr, fmt, va_alist) 1035 const char *hdr; 1036 const char *fmt; 1037 va_dcl 1038 #endif 1039 { 1040 va_list ap; 1041 1042 #ifdef __STDC__ 1043 va_start(ap, fmt); 1044 #else 1045 va_start(ap); 1046 #endif 1047 if (lmtpmode) 1048 { 1049 if (hdr != NULL) 1050 printf("%s ", hdr); 1051 vprintf(fmt, ap); 1052 printf("\r\n"); 1053 } 1054 else 1055 { 1056 e_to_sys(errno); 1057 vwarn(fmt, ap); 1058 } 1059 } 1060 1061 static void 1062 /*VARARGS1*/ 1063 #ifdef __STDC__ 1064 err(const char *fmt, ...) 1065 #else 1066 err(fmt, va_alist) 1067 const char *fmt; 1068 va_dcl 1069 #endif 1070 { 1071 va_list ap; 1072 1073 #ifdef __STDC__ 1074 va_start(ap, fmt); 1075 #else 1076 va_start(ap); 1077 #endif 1078 vwarn(fmt, ap); 1079 va_end(ap); 1080 1081 exit(eval); 1082 } 1083 1084 static void 1085 /*VARARGS1*/ 1086 #ifdef __STDC__ 1087 warn(const char *fmt, ...) 1088 #else 1089 warn(fmt, va_alist) 1090 const char *fmt; 1091 va_dcl 1092 #endif 1093 { 1094 va_list ap; 1095 1096 #ifdef __STDC__ 1097 va_start(ap, fmt); 1098 #else 1099 va_start(ap); 1100 #endif 1101 vwarn(fmt, ap); 1102 va_end(ap); 1103 } 1104 1105 static void 1106 vwarn(fmt, ap) 1107 const char *fmt; 1108 va_list ap; 1109 { 1110 /* 1111 * Log the message to stderr. 1112 * 1113 * Don't use LOG_PERROR as an openlog() flag to do this, 1114 * it's not portable enough. 1115 */ 1116 if (eval != EX_USAGE) 1117 (void) fprintf(stderr, "mail.local: "); 1118 (void) vfprintf(stderr, fmt, ap); 1119 (void) fprintf(stderr, "\n"); 1120 1121 /* Log the message to syslog. */ 1122 vsyslog(LOG_ERR, fmt, ap); 1123 } 1124 1125 /* 1126 * e_to_sys -- 1127 * Guess which errno's are temporary. Gag me. 1128 */ 1129 static void 1130 e_to_sys(num) 1131 int num; 1132 { 1133 /* Temporary failures override hard errors. */ 1134 if (eval == EX_TEMPFAIL) 1135 return; 1136 1137 switch (num) /* Hopefully temporary errors. */ 1138 { 1139 #ifdef EDQUOT 1140 case EDQUOT: /* Disc quota exceeded */ 1141 if (bouncequota) 1142 { 1143 eval = EX_UNAVAILABLE; 1144 break; 1145 } 1146 #endif /* EDQUOT */ 1147 #ifdef EAGAIN 1148 /* FALLTHROUGH */ 1149 case EAGAIN: /* Resource temporarily unavailable */ 1150 #endif 1151 #ifdef EBUSY 1152 case EBUSY: /* Device busy */ 1153 #endif 1154 #ifdef EPROCLIM 1155 case EPROCLIM: /* Too many processes */ 1156 #endif 1157 #ifdef EUSERS 1158 case EUSERS: /* Too many users */ 1159 #endif 1160 #ifdef ECONNABORTED 1161 case ECONNABORTED: /* Software caused connection abort */ 1162 #endif 1163 #ifdef ECONNREFUSED 1164 case ECONNREFUSED: /* Connection refused */ 1165 #endif 1166 #ifdef ECONNRESET 1167 case ECONNRESET: /* Connection reset by peer */ 1168 #endif 1169 #ifdef EDEADLK 1170 case EDEADLK: /* Resource deadlock avoided */ 1171 #endif 1172 #ifdef EFBIG 1173 case EFBIG: /* File too large */ 1174 #endif 1175 #ifdef EHOSTDOWN 1176 case EHOSTDOWN: /* Host is down */ 1177 #endif 1178 #ifdef EHOSTUNREACH 1179 case EHOSTUNREACH: /* No route to host */ 1180 #endif 1181 #ifdef EMFILE 1182 case EMFILE: /* Too many open files */ 1183 #endif 1184 #ifdef ENETDOWN 1185 case ENETDOWN: /* Network is down */ 1186 #endif 1187 #ifdef ENETRESET 1188 case ENETRESET: /* Network dropped connection on reset */ 1189 #endif 1190 #ifdef ENETUNREACH 1191 case ENETUNREACH: /* Network is unreachable */ 1192 #endif 1193 #ifdef ENFILE 1194 case ENFILE: /* Too many open files in system */ 1195 #endif 1196 #ifdef ENOBUFS 1197 case ENOBUFS: /* No buffer space available */ 1198 #endif 1199 #ifdef ENOMEM 1200 case ENOMEM: /* Cannot allocate memory */ 1201 #endif 1202 #ifdef ENOSPC 1203 case ENOSPC: /* No space left on device */ 1204 #endif 1205 #ifdef EROFS 1206 case EROFS: /* Read-only file system */ 1207 #endif 1208 #ifdef ESTALE 1209 case ESTALE: /* Stale NFS file handle */ 1210 #endif 1211 #ifdef ETIMEDOUT 1212 case ETIMEDOUT: /* Connection timed out */ 1213 #endif 1214 #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) 1215 case EWOULDBLOCK: /* Operation would block. */ 1216 #endif 1217 eval = EX_TEMPFAIL; 1218 break; 1219 default: 1220 eval = EX_UNAVAILABLE; 1221 break; 1222 } 1223 } 1224