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