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-2006 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 535 bfd = -1; 536 hfd = -1; 537 btn = strdup(_PATH_LOCTMP); 538 if ((bfd = mkstemp(btn)) == -1 || (bfp = fdopen(bfd, "w+")) == NULL) { 539 if (bfd != -1) 540 (void) close(bfd); 541 if (lmtprcpts) { 542 printf("451 4.3.0 unable to open temporary file\r\n"); 543 return; 544 } else { 545 mailerr("451 4.3.0", "unable to open temporary file"); 546 exit(eval); 547 } 548 } 549 (void) unlink(btn); 550 free(btn); 551 552 if (lmtpmode) { 553 printf("354 go ahead\r\n"); 554 fflush(stdout); 555 } 556 557 htn = strdup(_PATH_LOCHTMP); 558 if ((hfd = mkstemp(htn)) == -1 || (hfp = fdopen(hfd, "w+")) == NULL) { 559 if (hfd != -1) 560 (void) close(hfd); 561 e_to_sys(errno); 562 err("unable to open temporary file"); 563 } 564 (void) unlink(htn); 565 free(htn); 566 567 in_header_section = TRUE; 568 content_length = 0; 569 fp = hfp; 570 571 line[0] = '\0'; 572 while (fgets(line, sizeof(line), stdin) != (char *)NULL) 573 { 574 size_t line_len = 0; 575 int peek; 576 577 prevfl = fullline; /* preserve state of previous line */ 578 while (line[line_len] != '\n' && line_len < sizeof(line) - 2) 579 line_len++; 580 line_len++; 581 582 /* Check for dot-stuffing */ 583 if (prevfl && lmtprcpts && line[0] == '.') 584 { 585 if (line[1] == '\n' || 586 (line[1] == '\r' && line[2] == '\n')) 587 goto lmtpdot; 588 memcpy(line, line + 1, line_len); 589 line_len--; 590 } 591 592 /* Check to see if we have the full line from fgets() */ 593 fullline = FALSE; 594 if (line_len > 0) 595 { 596 if (line[line_len - 1] == '\n') 597 { 598 if (line_len >= 2 && 599 line[line_len - 2] == '\r') 600 { 601 line[line_len - 2] = '\n'; 602 line[line_len - 1] = '\0'; 603 line_len--; 604 } 605 fullline = TRUE; 606 } 607 else if (line[line_len - 1] == '\r') 608 { 609 /* Did we just miss the CRLF? */ 610 peek = fgetc(stdin); 611 if (peek == '\n') 612 { 613 line[line_len - 1] = '\n'; 614 fullline = TRUE; 615 } 616 else 617 (void) ungetc(peek, stdin); 618 } 619 } 620 else 621 fullline = TRUE; 622 623 if (prevfl && line[0] == '\n' && in_header_section) { 624 in_header_section = FALSE; 625 if (fflush(fp) == EOF || ferror(fp)) { 626 if (lmtprcpts) { 627 while (lmtprcpts--) 628 printf("451 4.3.0 temporary file write error\r\n"); 629 fclose(fp); 630 return; 631 } else { 632 mailerr("451 4.3.0", 633 "temporary file write error"); 634 fclose(fp); 635 exit(eval); 636 } 637 } 638 fp = bfp; 639 continue; 640 } 641 642 if (in_header_section) { 643 if (strncasecmp("Content-Length:", line, 15) == 0) { 644 continue; /* skip this header */ 645 } 646 } else 647 content_length += strlen(line); 648 (void) fwrite(line, sizeof(char), line_len, fp); 649 if (ferror(fp)) { 650 if (lmtprcpts) { 651 while (lmtprcpts--) 652 printf("451 4.3.0 temporary file write error\r\n"); 653 fclose(fp); 654 return; 655 } else { 656 mailerr("451 4.3.0", 657 "temporary file write error"); 658 fclose(fp); 659 exit(eval); 660 } 661 } 662 } 663 if (sigterm_caught) { 664 if (lmtprcpts) 665 while (lmtprcpts--) 666 printf("451 4.3.0 shutting down\r\n"); 667 else 668 mailerr("451 4.3.0", "shutting down"); 669 fclose(fp); 670 exit(eval); 671 } 672 673 if (lmtprcpts) { 674 /* Got a premature EOF -- toss message and exit */ 675 exit(EX_OK); 676 } 677 678 /* If message not newline terminated, need an extra. */ 679 if (!strchr(line, '\n')) { 680 (void) putc('\n', fp); 681 content_length++; 682 } 683 684 lmtpdot: 685 686 /* Output a newline; note, empty messages are allowed. */ 687 (void) putc('\n', fp); 688 689 if (fflush(fp) == EOF || ferror(fp)) { 690 if (lmtprcpts) { 691 while (lmtprcpts--) { 692 printf("451 4.3.0 temporary file write error\r\n"); 693 } 694 fclose(fp); 695 return; 696 } else { 697 mailerr("451 4.3.0", "temporary file write error"); 698 fclose(fp); 699 exit(eval); 700 } 701 } 702 703 (void) time(&tval); 704 (void) snprintf(unix_from_line, sizeof (unix_from_line), "From %s %s", 705 from, ctime(&tval)); 706 ulen = strlen(unix_from_line); 707 } 708 709 static void 710 handle_error(err_num, bouncequota, path) 711 int err_num; 712 bool bouncequota; 713 char *path; 714 { 715 #ifdef EDQUOT 716 if (err_num == EDQUOT && bouncequota) { 717 mailerr("552 5.2.2", "%s: %s", path, sm_errstring(err_num)); 718 } else 719 #endif /* EDQUOT */ 720 mailerr("450 4.2.0", "%s: %s", path, sm_errstring(err_num)); 721 } 722 723 static void 724 deliver(hfd, bfd, name, bouncequota) 725 int hfd; 726 int bfd; 727 char *name; 728 bool bouncequota; 729 { 730 struct stat fsb, sb; 731 int mbfd = -1, nr, nw = 0, off; 732 char biffmsg[100], buf[8*1024], path[MAXPATHLEN]; 733 off_t curoff, cursize; 734 int len; 735 struct passwd *pw = NULL; 736 737 /* 738 * Disallow delivery to unknown names -- special mailboxes 739 * can be handled in the sendmail aliases file. 740 */ 741 if ((pw = getpwnam(name)) == NULL) { 742 eval = EX_TEMPFAIL; 743 mailerr("451 4.3.0", "cannot lookup name: %s", name); 744 return; 745 } 746 endpwent(); 747 748 if (sigterm_caught) { 749 mailerr("451 4.3.0", "shutting down"); 750 return; 751 } 752 753 /* mailbox may be NFS mounted, seteuid to user */ 754 targ_uid = pw->pw_uid; 755 (void) seteuid(targ_uid); 756 757 if ((saved_uid != 0) && (src_uid != targ_uid)) { 758 /* 759 * If saved_uid == 0 (root), anything is OK; this is 760 * as it should be. But to prevent a random user from 761 * calling "mail.local foo" in an attempt to hijack 762 * foo's mail-box, make sure src_uid == targ_uid o/w. 763 */ 764 warn("%s: wrong owner (is %d, should be %d)", 765 name, src_uid, targ_uid); 766 eval = EX_CANTCREAT; 767 return; 768 } 769 770 path[0] = '\0'; 771 (void) snprintf(path, sizeof (path), "%s/%s", _PATH_MAILDIR, name); 772 773 /* 774 * If the mailbox is linked or a symlink, fail. There's an obvious 775 * race here, that the file was replaced with a symbolic link after 776 * the lstat returned, but before the open. We attempt to detect 777 * this by comparing the original stat information and information 778 * returned by an fstat of the file descriptor returned by the open. 779 * 780 * NB: this is a symptom of a larger problem, that the mail spooling 781 * directory is writeable by the wrong users. If that directory is 782 * writeable, system security is compromised for other reasons, and 783 * it cannot be fixed here. 784 * 785 * If we created the mailbox, set the owner/group. If that fails, 786 * just return. Another process may have already opened it, so we 787 * can't unlink it. Historically, binmail set the owner/group at 788 * each mail delivery. We no longer do this, assuming that if the 789 * ownership or permissions were changed there was a reason. 790 * 791 * XXX 792 * open(2) should support flock'ing the file. 793 */ 794 tryagain: 795 /* should check lock status, but... maillock return no value */ 796 maillock(name, 10); 797 798 if (sigterm_caught) { 799 mailerr("451 4.3.0", "shutting down"); 800 goto err0; 801 } 802 803 if (lstat(path, &sb)) { 804 mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY, 805 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); 806 if (mbfd != -1) 807 (void) fchmod(mbfd, 0660); 808 809 810 if (mbfd == -1) { 811 if (errno == EEXIST) { 812 mailunlock(); 813 goto tryagain; 814 } 815 } 816 } else if (sb.st_nlink != 1) { 817 mailerr("550 5.2.0", "%s: too many links", path); 818 goto err0; 819 } else if (!S_ISREG(sb.st_mode)) { 820 mailerr("550 5.2.0", "%s: irregular file", path); 821 goto err0; 822 } else { 823 mbfd = open(path, O_APPEND|O_WRONLY, 0); 824 if (mbfd != -1 && 825 (fstat(mbfd, &fsb) || fsb.st_nlink != 1 || 826 S_ISLNK(fsb.st_mode) || sb.st_dev != fsb.st_dev || 827 sb.st_ino != fsb.st_ino)) { 828 eval = EX_TEMPFAIL; 829 mailerr("550 5.2.0", 830 "%s: fstat: file changed after open", path); 831 goto err1; 832 } 833 } 834 835 if (mbfd == -1) { 836 mailerr("450 4.2.0", "%s: %s", path, strerror(errno)); 837 goto err0; 838 } 839 840 if (sigterm_caught) { 841 mailerr("451 4.3.0", "shutting down"); 842 goto err0; 843 } 844 845 /* Get the starting offset of the new message for biff. */ 846 curoff = lseek(mbfd, (off_t)0, SEEK_END); 847 (void) snprintf(biffmsg, sizeof (biffmsg), "%s@%ld\n", name, curoff); 848 849 /* Copy the message into the file. */ 850 if (lseek(hfd, (off_t)0, SEEK_SET) == (off_t)-1) { 851 mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 852 goto err1; 853 } 854 /* Copy the message into the file. */ 855 if (lseek(bfd, (off_t)0, SEEK_SET) == (off_t)-1) { 856 mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 857 goto err1; 858 } 859 if ((write(mbfd, unix_from_line, ulen)) != ulen) { 860 handle_error(errno, bouncequota, path); 861 goto err2; 862 } 863 864 if (sigterm_caught) { 865 mailerr("451 4.3.0", "shutting down"); 866 goto err2; 867 } 868 869 while ((nr = read(hfd, buf, sizeof (buf))) > 0) 870 for (off = 0; off < nr; nr -= nw, off += nw) 871 if ((nw = write(mbfd, buf + off, nr)) < 0) 872 { 873 handle_error(errno, bouncequota, path); 874 goto err2; 875 } 876 if (nr < 0) { 877 handle_error(errno, bouncequota, path); 878 goto err2; 879 } 880 881 if (sigterm_caught) { 882 mailerr("451 4.3.0", "shutting down"); 883 goto err2; 884 } 885 886 (void) snprintf(buf, sizeof (buf), "Content-Length: %d\n\n", 887 content_length); 888 len = strlen(buf); 889 if (write(mbfd, buf, len) != len) { 890 handle_error(errno, bouncequota, path); 891 goto err2; 892 } 893 894 if (sigterm_caught) { 895 mailerr("451 4.3.0", "shutting down"); 896 goto err2; 897 } 898 899 while ((nr = read(bfd, buf, sizeof (buf))) > 0) { 900 for (off = 0; off < nr; nr -= nw, off += nw) 901 if ((nw = write(mbfd, buf + off, nr)) < 0) { 902 handle_error(errno, bouncequota, path); 903 goto err2; 904 } 905 if (sigterm_caught) { 906 mailerr("451 4.3.0", "shutting down"); 907 goto err2; 908 } 909 } 910 if (nr < 0) { 911 handle_error(errno, bouncequota, path); 912 goto err2; 913 } 914 915 /* Flush to disk, don't wait for update. */ 916 if (fsync(mbfd)) { 917 handle_error(errno, bouncequota, path); 918 err2: if (mbfd >= 0) 919 (void)ftruncate(mbfd, curoff); 920 err1: (void)close(mbfd); 921 err0: mailunlock(); 922 (void)seteuid(saved_uid); 923 return; 924 } 925 926 /* 927 ** Save the current size so if the close() fails below 928 ** we can make sure no other process has changed the mailbox 929 ** between the failed close and the re-open()/re-lock(). 930 ** If something else has changed the size, we shouldn't 931 ** try to truncate it as we may do more harm then good 932 ** (e.g., truncate a later message delivery). 933 */ 934 935 if (fstat(mbfd, &sb) < 0) 936 cursize = 0; 937 else 938 cursize = sb.st_size; 939 940 /* Close and check -- NFS doesn't write until the close. */ 941 if (close(mbfd)) 942 { 943 handle_error(errno, bouncequota, path); 944 mbfd = open(path, O_WRONLY, 0); 945 if (mbfd < 0 || 946 cursize == 0 947 || flock(mbfd, LOCK_EX) < 0 || 948 fstat(mbfd, &sb) < 0 || 949 sb.st_size != cursize || 950 sb.st_nlink != 1 || 951 !S_ISREG(sb.st_mode) || 952 sb.st_dev != fsb.st_dev || 953 sb.st_ino != fsb.st_ino || 954 sb.st_uid != fsb.st_uid) 955 { 956 /* Don't use a bogus file */ 957 if (mbfd >= 0) 958 { 959 (void) close(mbfd); 960 mbfd = -1; 961 } 962 } 963 964 /* Attempt to truncate back to pre-write size */ 965 goto err2; 966 } else 967 notifybiff(biffmsg); 968 969 mailunlock(); 970 971 (void)seteuid(saved_uid); 972 973 if (lmtpmode) { 974 printf("250 2.1.5 %s OK\r\n", name); 975 } 976 } 977 978 static void 979 notifybiff(msg) 980 char *msg; 981 { 982 static struct sockaddr_in addr; 983 static int f = -1; 984 struct hostent *hp; 985 struct servent *sp; 986 int len; 987 988 if (msg == NULL) { 989 /* Be silent if biff service not available. */ 990 if ((sp = getservbyname("biff", "udp")) == NULL) 991 return; 992 if ((hp = gethostbyname("localhost")) == NULL) { 993 warn("localhost: %s", strerror(errno)); 994 return; 995 } 996 addr.sin_family = hp->h_addrtype; 997 (void) memmove(&addr.sin_addr, hp->h_addr, hp->h_length); 998 addr.sin_port = sp->s_port; 999 return; 1000 } 1001 1002 if (addr.sin_family == 0) 1003 return; /* did not initialize */ 1004 1005 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 1006 warn("socket: %s", strerror(errno)); 1007 return; 1008 } 1009 len = strlen(msg) + 1; 1010 if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof (addr)) 1011 != len) 1012 warn("sendto biff: %s", strerror(errno)); 1013 } 1014 1015 static void 1016 usage() 1017 { 1018 eval = EX_USAGE; 1019 err("usage: mail.local [-l] [-f from] user ..."); 1020 } 1021 1022 static void 1023 /*VARARGS2*/ 1024 #ifdef __STDC__ 1025 mailerr(const char *hdr, const char *fmt, ...) 1026 #else 1027 mailerr(hdr, fmt, va_alist) 1028 const char *hdr; 1029 const char *fmt; 1030 va_dcl 1031 #endif 1032 { 1033 va_list ap; 1034 1035 #ifdef __STDC__ 1036 va_start(ap, fmt); 1037 #else 1038 va_start(ap); 1039 #endif 1040 if (lmtpmode) 1041 { 1042 if (hdr != NULL) 1043 printf("%s ", hdr); 1044 vprintf(fmt, ap); 1045 printf("\r\n"); 1046 } 1047 else 1048 { 1049 e_to_sys(errno); 1050 vwarn(fmt, ap); 1051 } 1052 } 1053 1054 static void 1055 /*VARARGS1*/ 1056 #ifdef __STDC__ 1057 err(const char *fmt, ...) 1058 #else 1059 err(fmt, va_alist) 1060 const char *fmt; 1061 va_dcl 1062 #endif 1063 { 1064 va_list ap; 1065 1066 #ifdef __STDC__ 1067 va_start(ap, fmt); 1068 #else 1069 va_start(ap); 1070 #endif 1071 vwarn(fmt, ap); 1072 va_end(ap); 1073 1074 exit(eval); 1075 } 1076 1077 static void 1078 /*VARARGS1*/ 1079 #ifdef __STDC__ 1080 warn(const char *fmt, ...) 1081 #else 1082 warn(fmt, va_alist) 1083 const char *fmt; 1084 va_dcl 1085 #endif 1086 { 1087 va_list ap; 1088 1089 #ifdef __STDC__ 1090 va_start(ap, fmt); 1091 #else 1092 va_start(ap); 1093 #endif 1094 vwarn(fmt, ap); 1095 va_end(ap); 1096 } 1097 1098 static void 1099 vwarn(fmt, ap) 1100 const char *fmt; 1101 va_list ap; 1102 { 1103 /* 1104 * Log the message to stderr. 1105 * 1106 * Don't use LOG_PERROR as an openlog() flag to do this, 1107 * it's not portable enough. 1108 */ 1109 if (eval != EX_USAGE) 1110 (void) fprintf(stderr, "mail.local: "); 1111 (void) vfprintf(stderr, fmt, ap); 1112 (void) fprintf(stderr, "\n"); 1113 1114 /* Log the message to syslog. */ 1115 vsyslog(LOG_ERR, fmt, ap); 1116 } 1117 1118 /* 1119 * e_to_sys -- 1120 * Guess which errno's are temporary. Gag me. 1121 */ 1122 static void 1123 e_to_sys(num) 1124 int num; 1125 { 1126 /* Temporary failures override hard errors. */ 1127 if (eval == EX_TEMPFAIL) 1128 return; 1129 1130 switch (num) /* Hopefully temporary errors. */ 1131 { 1132 #ifdef EDQUOT 1133 case EDQUOT: /* Disc quota exceeded */ 1134 if (bouncequota) 1135 { 1136 eval = EX_UNAVAILABLE; 1137 break; 1138 } 1139 /* FALLTHROUGH */ 1140 #endif /* EDQUOT */ 1141 #ifdef EAGAIN 1142 case EAGAIN: /* Resource temporarily unavailable */ 1143 #endif 1144 #ifdef EBUSY 1145 case EBUSY: /* Device busy */ 1146 #endif 1147 #ifdef EPROCLIM 1148 case EPROCLIM: /* Too many processes */ 1149 #endif 1150 #ifdef EUSERS 1151 case EUSERS: /* Too many users */ 1152 #endif 1153 #ifdef ECONNABORTED 1154 case ECONNABORTED: /* Software caused connection abort */ 1155 #endif 1156 #ifdef ECONNREFUSED 1157 case ECONNREFUSED: /* Connection refused */ 1158 #endif 1159 #ifdef ECONNRESET 1160 case ECONNRESET: /* Connection reset by peer */ 1161 #endif 1162 #ifdef EDEADLK 1163 case EDEADLK: /* Resource deadlock avoided */ 1164 #endif 1165 #ifdef EFBIG 1166 case EFBIG: /* File too large */ 1167 #endif 1168 #ifdef EHOSTDOWN 1169 case EHOSTDOWN: /* Host is down */ 1170 #endif 1171 #ifdef EHOSTUNREACH 1172 case EHOSTUNREACH: /* No route to host */ 1173 #endif 1174 #ifdef EMFILE 1175 case EMFILE: /* Too many open files */ 1176 #endif 1177 #ifdef ENETDOWN 1178 case ENETDOWN: /* Network is down */ 1179 #endif 1180 #ifdef ENETRESET 1181 case ENETRESET: /* Network dropped connection on reset */ 1182 #endif 1183 #ifdef ENETUNREACH 1184 case ENETUNREACH: /* Network is unreachable */ 1185 #endif 1186 #ifdef ENFILE 1187 case ENFILE: /* Too many open files in system */ 1188 #endif 1189 #ifdef ENOBUFS 1190 case ENOBUFS: /* No buffer space available */ 1191 #endif 1192 #ifdef ENOMEM 1193 case ENOMEM: /* Cannot allocate memory */ 1194 #endif 1195 #ifdef ENOSPC 1196 case ENOSPC: /* No space left on device */ 1197 #endif 1198 #ifdef EROFS 1199 case EROFS: /* Read-only file system */ 1200 #endif 1201 #ifdef ESTALE 1202 case ESTALE: /* Stale NFS file handle */ 1203 #endif 1204 #ifdef ETIMEDOUT 1205 case ETIMEDOUT: /* Connection timed out */ 1206 #endif 1207 #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) 1208 case EWOULDBLOCK: /* Operation would block. */ 1209 #endif 1210 eval = EX_TEMPFAIL; 1211 break; 1212 default: 1213 eval = EX_UNAVAILABLE; 1214 break; 1215 } 1216 } 1217