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-2002 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 deliver(hfd, bfd, name, bouncequota) 711 int hfd; 712 int bfd; 713 char *name; 714 bool bouncequota; 715 { 716 struct stat fsb, sb; 717 int mbfd = -1, nr, nw = 0, off; 718 char biffmsg[100], buf[8*1024], path[MAXPATHLEN]; 719 off_t curoff, cursize; 720 int len; 721 struct passwd *pw = NULL; 722 723 /* 724 * Disallow delivery to unknown names -- special mailboxes 725 * can be handled in the sendmail aliases file. 726 */ 727 if ((pw = getpwnam(name)) == NULL) { 728 eval = EX_TEMPFAIL; 729 mailerr("451 4.3.0", "cannot lookup name: %s", name); 730 return; 731 } 732 endpwent(); 733 734 if (sigterm_caught) { 735 mailerr("451 4.3.0", "shutting down"); 736 return; 737 } 738 739 /* mailbox may be NFS mounted, seteuid to user */ 740 targ_uid = pw->pw_uid; 741 (void) seteuid(targ_uid); 742 743 if ((saved_uid != 0) && (src_uid != targ_uid)) { 744 /* 745 * If saved_uid == 0 (root), anything is OK; this is 746 * as it should be. But to prevent a random user from 747 * calling "mail.local foo" in an attempt to hijack 748 * foo's mail-box, make sure src_uid == targ_uid o/w. 749 */ 750 warn("%s: wrong owner (is %d, should be %d)", 751 name, src_uid, targ_uid); 752 eval = EX_CANTCREAT; 753 return; 754 } 755 756 path[0] = '\0'; 757 (void) snprintf(path, sizeof (path), "%s/%s", _PATH_MAILDIR, name); 758 759 /* 760 * If the mailbox is linked or a symlink, fail. There's an obvious 761 * race here, that the file was replaced with a symbolic link after 762 * the lstat returned, but before the open. We attempt to detect 763 * this by comparing the original stat information and information 764 * returned by an fstat of the file descriptor returned by the open. 765 * 766 * NB: this is a symptom of a larger problem, that the mail spooling 767 * directory is writeable by the wrong users. If that directory is 768 * writeable, system security is compromised for other reasons, and 769 * it cannot be fixed here. 770 * 771 * If we created the mailbox, set the owner/group. If that fails, 772 * just return. Another process may have already opened it, so we 773 * can't unlink it. Historically, binmail set the owner/group at 774 * each mail delivery. We no longer do this, assuming that if the 775 * ownership or permissions were changed there was a reason. 776 * 777 * XXX 778 * open(2) should support flock'ing the file. 779 */ 780 tryagain: 781 /* should check lock status, but... maillock return no value */ 782 maillock(name, 10); 783 784 if (sigterm_caught) { 785 mailerr("451 4.3.0", "shutting down"); 786 goto err0; 787 } 788 789 if (lstat(path, &sb)) { 790 mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY, 791 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); 792 if (mbfd != -1) 793 (void) fchmod(mbfd, 0660); 794 795 796 if (mbfd == -1) { 797 if (errno == EEXIST) { 798 mailunlock(); 799 goto tryagain; 800 } 801 } 802 } else if (sb.st_nlink != 1) { 803 mailerr("550 5.2.0", "%s: too many links", path); 804 goto err0; 805 } else if (!S_ISREG(sb.st_mode)) { 806 mailerr("550 5.2.0", "%s: irregular file", path); 807 goto err0; 808 } else { 809 mbfd = open(path, O_APPEND|O_WRONLY, 0); 810 if (mbfd != -1 && 811 (fstat(mbfd, &fsb) || fsb.st_nlink != 1 || 812 S_ISLNK(fsb.st_mode) || sb.st_dev != fsb.st_dev || 813 sb.st_ino != fsb.st_ino)) { 814 eval = EX_TEMPFAIL; 815 mailerr("550 5.2.0", 816 "%s: fstat: file changed after open", path); 817 goto err1; 818 } 819 } 820 821 if (mbfd == -1) { 822 mailerr("450 4.2.0", "%s: %s", path, strerror(errno)); 823 goto err0; 824 } 825 826 if (sigterm_caught) { 827 mailerr("451 4.3.0", "shutting down"); 828 goto err0; 829 } 830 831 /* Get the starting offset of the new message for biff. */ 832 curoff = lseek(mbfd, (off_t)0, SEEK_END); 833 (void) snprintf(biffmsg, sizeof (biffmsg), "%s@%ld\n", name, curoff); 834 835 /* Copy the message into the file. */ 836 if (lseek(hfd, (off_t)0, SEEK_SET) == (off_t)-1) { 837 mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 838 goto err1; 839 } 840 /* Copy the message into the file. */ 841 if (lseek(bfd, (off_t)0, SEEK_SET) == (off_t)-1) { 842 mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 843 goto err1; 844 } 845 if ((write(mbfd, unix_from_line, ulen)) != ulen) { 846 mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 847 goto err2; 848 } 849 850 if (sigterm_caught) { 851 mailerr("451 4.3.0", "shutting down"); 852 goto err2; 853 } 854 855 while ((nr = read(hfd, buf, sizeof (buf))) > 0) 856 for (off = 0; off < nr; nr -= nw, off += nw) 857 if ((nw = write(mbfd, buf + off, nr)) < 0) 858 { 859 #ifdef EDQUOT 860 if (errno == EDQUOT && bouncequota) 861 mailerr("552 5.2.2", "%s: %s", 862 path, sm_errstring(errno)); 863 #endif /* EDQUOT */ 864 mailerr("450 4.2.0", "%s: %s", 865 path, sm_errstring(errno)); 866 goto err2; 867 } 868 if (nr < 0) { 869 mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 870 goto err2; 871 } 872 873 if (sigterm_caught) { 874 mailerr("451 4.3.0", "shutting down"); 875 goto err2; 876 } 877 878 (void) snprintf(buf, sizeof (buf), "Content-Length: %d\n\n", 879 content_length); 880 len = strlen(buf); 881 if (write(mbfd, buf, len) != len) { 882 mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 883 goto err2; 884 } 885 886 if (sigterm_caught) { 887 mailerr("451 4.3.0", "shutting down"); 888 goto err2; 889 } 890 891 while ((nr = read(bfd, buf, sizeof (buf))) > 0) { 892 for (off = 0; off < nr; nr -= nw, off += nw) 893 if ((nw = write(mbfd, buf + off, nr)) < 0) { 894 mailerr("450 4.2.0", "temporary file: %s", 895 strerror(errno)); 896 goto err2; 897 } 898 if (sigterm_caught) { 899 mailerr("451 4.3.0", "shutting down"); 900 goto err2; 901 } 902 } 903 if (nr < 0) { 904 mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 905 goto err2; 906 } 907 908 /* Flush to disk, don't wait for update. */ 909 if (fsync(mbfd)) { 910 mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 911 err2: if (mbfd >= 0) 912 (void)ftruncate(mbfd, curoff); 913 err1: (void)close(mbfd); 914 err0: mailunlock(); 915 (void)seteuid(saved_uid); 916 return; 917 } 918 919 /* 920 ** Save the current size so if the close() fails below 921 ** we can make sure no other process has changed the mailbox 922 ** between the failed close and the re-open()/re-lock(). 923 ** If something else has changed the size, we shouldn't 924 ** try to truncate it as we may do more harm then good 925 ** (e.g., truncate a later message delivery). 926 */ 927 928 if (fstat(mbfd, &sb) < 0) 929 cursize = 0; 930 else 931 cursize = sb.st_size; 932 933 /* Close and check -- NFS doesn't write until the close. */ 934 if (close(mbfd)) 935 { 936 #ifdef EDQUOT 937 if (errno == EDQUOT && bouncequota) 938 mailerr("552 5.2.2", "%s: %s", path, 939 sm_errstring(errno)); 940 else 941 #endif /* EDQUOT */ 942 mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 943 mbfd = open(path, O_WRONLY, 0); 944 if (mbfd < 0 || 945 cursize == 0 946 || flock(mbfd, LOCK_EX) < 0 || 947 fstat(mbfd, &sb) < 0 || 948 sb.st_size != cursize || 949 sb.st_nlink != 1 || 950 !S_ISREG(sb.st_mode) || 951 sb.st_dev != fsb.st_dev || 952 sb.st_ino != fsb.st_ino || 953 sb.st_uid != fsb.st_uid) 954 { 955 /* Don't use a bogus file */ 956 if (mbfd >= 0) 957 { 958 (void) close(mbfd); 959 mbfd = -1; 960 } 961 } 962 963 /* Attempt to truncate back to pre-write size */ 964 goto err2; 965 } else 966 notifybiff(biffmsg); 967 968 mailunlock(); 969 970 (void)seteuid(saved_uid); 971 972 if (lmtpmode) { 973 printf("250 2.1.5 %s OK\r\n", name); 974 } 975 } 976 977 static void 978 notifybiff(msg) 979 char *msg; 980 { 981 static struct sockaddr_in addr; 982 static int f = -1; 983 struct hostent *hp; 984 struct servent *sp; 985 int len; 986 987 if (msg == NULL) { 988 /* Be silent if biff service not available. */ 989 if ((sp = getservbyname("biff", "udp")) == NULL) 990 return; 991 if ((hp = gethostbyname("localhost")) == NULL) { 992 warn("localhost: %s", strerror(errno)); 993 return; 994 } 995 addr.sin_family = hp->h_addrtype; 996 (void) memmove(&addr.sin_addr, hp->h_addr, hp->h_length); 997 addr.sin_port = sp->s_port; 998 return; 999 } 1000 1001 if (addr.sin_family == 0) 1002 return; /* did not initialize */ 1003 1004 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 1005 warn("socket: %s", strerror(errno)); 1006 return; 1007 } 1008 len = strlen(msg) + 1; 1009 if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof (addr)) 1010 != len) 1011 warn("sendto biff: %s", strerror(errno)); 1012 } 1013 1014 static void 1015 usage() 1016 { 1017 eval = EX_USAGE; 1018 err("usage: mail.local [-l] [-f from] user ..."); 1019 } 1020 1021 static void 1022 /*VARARGS2*/ 1023 #ifdef __STDC__ 1024 mailerr(const char *hdr, const char *fmt, ...) 1025 #else 1026 mailerr(hdr, fmt, va_alist) 1027 const char *hdr; 1028 const char *fmt; 1029 va_dcl 1030 #endif 1031 { 1032 va_list ap; 1033 1034 #ifdef __STDC__ 1035 va_start(ap, fmt); 1036 #else 1037 va_start(ap); 1038 #endif 1039 if (lmtpmode) 1040 { 1041 if (hdr != NULL) 1042 printf("%s ", hdr); 1043 vprintf(fmt, ap); 1044 printf("\r\n"); 1045 } 1046 else 1047 { 1048 e_to_sys(errno); 1049 vwarn(fmt, ap); 1050 } 1051 } 1052 1053 static void 1054 /*VARARGS1*/ 1055 #ifdef __STDC__ 1056 err(const char *fmt, ...) 1057 #else 1058 err(fmt, va_alist) 1059 const char *fmt; 1060 va_dcl 1061 #endif 1062 { 1063 va_list ap; 1064 1065 #ifdef __STDC__ 1066 va_start(ap, fmt); 1067 #else 1068 va_start(ap); 1069 #endif 1070 vwarn(fmt, ap); 1071 va_end(ap); 1072 1073 exit(eval); 1074 } 1075 1076 static void 1077 /*VARARGS1*/ 1078 #ifdef __STDC__ 1079 warn(const char *fmt, ...) 1080 #else 1081 warn(fmt, va_alist) 1082 const char *fmt; 1083 va_dcl 1084 #endif 1085 { 1086 va_list ap; 1087 1088 #ifdef __STDC__ 1089 va_start(ap, fmt); 1090 #else 1091 va_start(ap); 1092 #endif 1093 vwarn(fmt, ap); 1094 va_end(ap); 1095 } 1096 1097 static void 1098 vwarn(fmt, ap) 1099 const char *fmt; 1100 va_list ap; 1101 { 1102 /* 1103 * Log the message to stderr. 1104 * 1105 * Don't use LOG_PERROR as an openlog() flag to do this, 1106 * it's not portable enough. 1107 */ 1108 if (eval != EX_USAGE) 1109 (void) fprintf(stderr, "mail.local: "); 1110 (void) vfprintf(stderr, fmt, ap); 1111 (void) fprintf(stderr, "\n"); 1112 1113 /* Log the message to syslog. */ 1114 vsyslog(LOG_ERR, fmt, ap); 1115 } 1116 1117 /* 1118 * e_to_sys -- 1119 * Guess which errno's are temporary. Gag me. 1120 */ 1121 static void 1122 e_to_sys(num) 1123 int num; 1124 { 1125 /* Temporary failures override hard errors. */ 1126 if (eval == EX_TEMPFAIL) 1127 return; 1128 1129 switch (num) /* Hopefully temporary errors. */ 1130 { 1131 #ifdef EDQUOT 1132 case EDQUOT: /* Disc quota exceeded */ 1133 if (bouncequota) 1134 { 1135 eval = EX_UNAVAILABLE; 1136 break; 1137 } 1138 /* FALLTHROUGH */ 1139 #endif /* EDQUOT */ 1140 #ifdef EAGAIN 1141 case EAGAIN: /* Resource temporarily unavailable */ 1142 #endif 1143 #ifdef EBUSY 1144 case EBUSY: /* Device busy */ 1145 #endif 1146 #ifdef EPROCLIM 1147 case EPROCLIM: /* Too many processes */ 1148 #endif 1149 #ifdef EUSERS 1150 case EUSERS: /* Too many users */ 1151 #endif 1152 #ifdef ECONNABORTED 1153 case ECONNABORTED: /* Software caused connection abort */ 1154 #endif 1155 #ifdef ECONNREFUSED 1156 case ECONNREFUSED: /* Connection refused */ 1157 #endif 1158 #ifdef ECONNRESET 1159 case ECONNRESET: /* Connection reset by peer */ 1160 #endif 1161 #ifdef EDEADLK 1162 case EDEADLK: /* Resource deadlock avoided */ 1163 #endif 1164 #ifdef EFBIG 1165 case EFBIG: /* File too large */ 1166 #endif 1167 #ifdef EHOSTDOWN 1168 case EHOSTDOWN: /* Host is down */ 1169 #endif 1170 #ifdef EHOSTUNREACH 1171 case EHOSTUNREACH: /* No route to host */ 1172 #endif 1173 #ifdef EMFILE 1174 case EMFILE: /* Too many open files */ 1175 #endif 1176 #ifdef ENETDOWN 1177 case ENETDOWN: /* Network is down */ 1178 #endif 1179 #ifdef ENETRESET 1180 case ENETRESET: /* Network dropped connection on reset */ 1181 #endif 1182 #ifdef ENETUNREACH 1183 case ENETUNREACH: /* Network is unreachable */ 1184 #endif 1185 #ifdef ENFILE 1186 case ENFILE: /* Too many open files in system */ 1187 #endif 1188 #ifdef ENOBUFS 1189 case ENOBUFS: /* No buffer space available */ 1190 #endif 1191 #ifdef ENOMEM 1192 case ENOMEM: /* Cannot allocate memory */ 1193 #endif 1194 #ifdef ENOSPC 1195 case ENOSPC: /* No space left on device */ 1196 #endif 1197 #ifdef EROFS 1198 case EROFS: /* Read-only file system */ 1199 #endif 1200 #ifdef ESTALE 1201 case ESTALE: /* Stale NFS file handle */ 1202 #endif 1203 #ifdef ETIMEDOUT 1204 case ETIMEDOUT: /* Connection timed out */ 1205 #endif 1206 #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) 1207 case EWOULDBLOCK: /* Operation would block. */ 1208 #endif 1209 eval = EX_TEMPFAIL; 1210 break; 1211 default: 1212 eval = EX_UNAVAILABLE; 1213 break; 1214 } 1215 } 1216