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 of 8 * the sendmail distribution. 9 * 10 */ 11 12 #ifndef lint 13 static char copyright[] = 14 "@(#) Copyright (c) 1990, 1993, 1994\n\ 15 The Regents of the University of California. All rights reserved.\n"; 16 #endif /* not lint */ 17 18 #ifndef lint 19 static char sccsid[] = "@(#)mail.local.c 8.83 (Berkeley) 12/17/1998"; 20 #endif /* not lint */ 21 22 /* 23 * This is not intended to work on System V derived systems 24 * such as Solaris or HP-UX, since they use a totally different 25 * approach to mailboxes (essentially, they have a setgid program 26 * rather than setuid, and they rely on the ability to "give away" 27 * files to do their work). IT IS NOT A BUG that this doesn't 28 * work on such architectures. 29 */ 30 31 #include <sys/param.h> 32 #include <sys/stat.h> 33 #include <sys/socket.h> 34 #include <sys/file.h> 35 36 #include <netinet/in.h> 37 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <netdb.h> 41 #include <pwd.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <syslog.h> 46 #include <time.h> 47 #include <unistd.h> 48 #ifdef EX_OK 49 # undef EX_OK /* unistd.h may have another use for this */ 50 #endif 51 #include <sysexits.h> 52 #include <ctype.h> 53 54 #ifdef __STDC__ 55 #include <stdarg.h> 56 #else 57 #include <varargs.h> 58 #endif 59 60 #if (defined(sun) && defined(__svr4__)) || defined(__SVR4) 61 # define USE_LOCKF 1 62 # define USE_SETEUID 1 63 # define _PATH_MAILDIR "/var/mail" 64 #endif 65 66 #if (defined(sun) && !defined(__svr4__)) && !defined(__SVR4) 67 # ifdef __dead 68 # undef __dead 69 # define __dead 70 # endif 71 #endif 72 73 #if defined(_AIX) 74 # define USE_LOCKF 1 75 # define USE_SETEUID 1 76 # define USE_VSYSLOG 0 77 #endif 78 79 #if defined(__hpux) 80 # define USE_LOCKF 1 81 # define USE_SETRESUID 1 82 # define USE_VSYSLOG 0 83 # ifdef __dead 84 # undef __dead 85 # define __dead 86 # endif 87 #endif 88 89 #if defined(_CRAY) 90 # if !defined(MAXPATHLEN) 91 # define MAXPATHLEN PATHSIZE 92 # endif 93 # define USE_VSYSLOG 0 94 # define _PATH_MAILDIR "/usr/spool/mail" 95 #endif 96 97 #if defined(ultrix) 98 # define USE_VSYSLOG 0 99 #endif 100 101 #if defined(__osf__) 102 # define USE_VSYSLOG 0 103 #endif 104 105 #if defined(NeXT) && !defined(__APPLE__) 106 # include <libc.h> 107 # define _PATH_MAILDIR "/usr/spool/mail" 108 # define __dead /* empty */ 109 # define S_IRUSR S_IREAD 110 # define S_IWUSR S_IWRITE 111 #endif 112 113 #if defined(IRIX64) || defined(IRIX5) || defined(IRIX6) 114 # include <paths.h> 115 # define HASSTRERROR 1 /* has strerror(3) */ 116 #endif 117 118 /* 119 * If you don't have flock, you could try using lockf instead. 120 */ 121 122 #ifdef USE_LOCKF 123 # define flock(a, b) lockf(a, b, 0) 124 # define LOCK_EX F_LOCK 125 #endif 126 127 #ifndef USE_VSYSLOG 128 # define USE_VSYSLOG 1 129 #endif 130 131 #ifndef LOCK_EX 132 # include <sys/file.h> 133 #endif 134 135 #if defined(BSD4_4) || defined(__GLIBC__) 136 # include "pathnames.h" 137 #endif 138 139 #ifndef __P 140 # ifdef __STDC__ 141 # define __P(protos) protos 142 # else 143 # define __P(protos) () 144 # define const 145 # endif 146 #endif 147 #ifndef __dead 148 # if defined(__GNUC__) && (__GNUC__ < 2 || __GNUC_MINOR__ < 5) && !defined(__STRICT_ANSI__) 149 # define __dead __volatile 150 # else 151 # define __dead 152 # endif 153 #endif 154 155 #ifdef BSD4_4 156 # define HAS_ST_GEN 1 157 #else 158 # ifndef _BSD_VA_LIST_ 159 # define _BSD_VA_LIST_ va_list 160 # endif 161 #endif 162 163 #if defined(BSD4_4) || defined(linux) 164 # define HASSNPRINTF 1 165 #else 166 # ifndef ultrix 167 extern FILE *fdopen __P((int, const char *)); 168 # endif 169 #endif 170 171 #if SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) 172 # define HASSNPRINTF 1 /* has snprintf starting in 2.6 */ 173 #endif 174 175 #if !HASSNPRINTF 176 extern int snprintf __P((char *, size_t, const char *, ...)); 177 # ifndef _CRAY 178 extern int vsnprintf __P((char *, size_t, const char *, ...)); 179 # endif 180 #endif 181 182 #if defined(BSD4_4) || defined(__osf__) || defined(__GNU_LIBRARY__) 183 # ifndef HASSTRERROR 184 # define HASSTRERROR 1 185 # endif 186 #endif 187 188 #if !HASSTRERROR 189 extern char *strerror __P((int)); 190 #endif 191 192 /* 193 * If you don't have setreuid, and you have saved uids, and you have 194 * a seteuid() call that doesn't try to emulate using setuid(), then 195 * you can try defining USE_SETEUID. 196 */ 197 #ifdef USE_SETEUID 198 # define setreuid(r, e) seteuid(e) 199 #endif 200 201 /* 202 * And of course on hpux you have setresuid() 203 */ 204 #ifdef USE_SETRESUID 205 # define setreuid(r, e) setresuid(-1, e, -1) 206 #endif 207 208 #ifndef _PATH_LOCTMP 209 # define _PATH_LOCTMP "/tmp/local.XXXXXX" 210 #endif 211 #ifndef _PATH_MAILDIR 212 # define _PATH_MAILDIR "/var/spool/mail" 213 #endif 214 215 #ifndef S_ISREG 216 # define S_ISREG(mode) (((mode) & _S_IFMT) == S_IFREG) 217 #endif 218 219 #ifndef MAILER_DAEMON 220 # define MAILER_DAEMON "MAILER-DAEMON" 221 #endif 222 223 int eval = EX_OK; /* sysexits.h error value. */ 224 int lmtpmode = 0; 225 u_char tTdvect[100]; 226 227 void deliver __P((int, char *, int, int)); 228 void e_to_sys __P((int)); 229 void notifybiff __P((char *)); 230 int store __P((char *, int)); 231 void usage __P((void)); 232 void vwarn __P((const char *, _BSD_VA_LIST_)); 233 void lockmbox __P((char *)); 234 void unlockmbox __P((void)); 235 void mailerr __P((const char *, const char *, ...)); 236 void dolmtp __P((int, int)); 237 238 int 239 main(argc, argv) 240 int argc; 241 char *argv[]; 242 { 243 struct passwd *pw; 244 int ch, fd, nobiff, nofsync; 245 uid_t uid; 246 char *from; 247 extern char *optarg; 248 extern int optind; 249 250 /* make sure we have some open file descriptors */ 251 for (fd = 10; fd < 30; fd++) 252 (void) close(fd); 253 254 /* use a reasonable umask */ 255 (void) umask(0077); 256 257 #ifdef LOG_MAIL 258 openlog("mail.local", 0, LOG_MAIL); 259 #else 260 openlog("mail.local", 0); 261 #endif 262 263 from = NULL; 264 nobiff = 0; 265 nofsync = 0; 266 while ((ch = getopt(argc, argv, "bdf:r:ls")) != -1) 267 switch(ch) { 268 case 'b': 269 nobiff++; 270 break; 271 case 'd': /* Backward compatible. */ 272 break; 273 case 'f': 274 case 'r': /* Backward compatible. */ 275 if (from != NULL) { 276 mailerr(NULL, "multiple -f options"); 277 usage(); 278 } 279 from = optarg; 280 break; 281 case 'l': 282 lmtpmode++; 283 break; 284 case 's': 285 nofsync++; 286 break; 287 case '?': 288 default: 289 usage(); 290 } 291 argc -= optind; 292 argv += optind; 293 294 if (lmtpmode) 295 dolmtp(nobiff, nofsync); 296 297 if (!*argv) 298 usage(); 299 300 /* 301 * If from not specified, use the name from getlogin() if the 302 * uid matches, otherwise, use the name from the password file 303 * corresponding to the uid. 304 */ 305 uid = getuid(); 306 if (!from && (!(from = getlogin()) || 307 !(pw = getpwnam(from)) || pw->pw_uid != uid)) 308 from = (pw = getpwuid(uid)) ? pw->pw_name : "???"; 309 310 /* 311 * There is no way to distinguish the error status of one delivery 312 * from the rest of the deliveries. So, if we failed hard on one 313 * or more deliveries, but had no failures on any of the others, we 314 * return a hard failure. If we failed temporarily on one or more 315 * deliveries, we return a temporary failure regardless of the other 316 * failures. This results in the delivery being reattempted later 317 * at the expense of repeated failures and multiple deliveries. 318 */ 319 for (fd = store(from, 0); *argv; ++argv) 320 deliver(fd, *argv, nobiff, nofsync); 321 exit(eval); 322 } 323 324 char * 325 parseaddr(s) 326 char *s; 327 { 328 char *p; 329 int len; 330 331 if (*s++ != '<') 332 return NULL; 333 334 p = s; 335 336 /* at-domain-list */ 337 while (*p == '@') { 338 p++; 339 if (*p == '[') { 340 p++; 341 while (isascii(*p) && 342 (isalnum(*p) || *p == '.' || 343 *p == '-' || *p == ':')) 344 p++; 345 if (*p++ != ']') 346 return NULL; 347 } else { 348 while ((isascii(*p) && isalnum(*p)) || 349 strchr(".-_", *p)) 350 p++; 351 } 352 if (*p == ',' && p[1] == '@') 353 p++; 354 else if (*p == ':' && p[1] != '@') 355 p++; 356 else 357 return NULL; 358 } 359 360 s = p; 361 362 /* local-part */ 363 if (*p == '\"') { 364 p++; 365 while (*p && *p != '\"') { 366 if (*p == '\\') { 367 if (!*++p) 368 return NULL; 369 } 370 p++; 371 } 372 if (!*p++) 373 return NULL; 374 } else { 375 while (*p && *p != '@' && *p != '>') { 376 if (*p == '\\') { 377 if (!*++p) 378 return NULL; 379 } else { 380 if (*p <= ' ' || (*p & 128) || 381 strchr("<>()[]\\,;:\"", *p)) 382 return NULL; 383 } 384 p++; 385 } 386 } 387 388 /* @domain */ 389 if (*p == '@') { 390 p++; 391 if (*p == '[') { 392 p++; 393 while (isascii(*p) && 394 (isalnum(*p) || *p == '.' || 395 *p == '-' || *p == ':')) 396 p++; 397 if (*p++ != ']') 398 return NULL; 399 } else { 400 while ((isascii(*p) && isalnum(*p)) || 401 strchr(".-_", *p)) 402 p++; 403 } 404 } 405 406 if (*p++ != '>') 407 return NULL; 408 if (*p && *p != ' ') 409 return NULL; 410 len = p - s - 1; 411 if (*s == '\0' || len <= 0) 412 { 413 s = MAILER_DAEMON; 414 len = strlen(s); 415 } 416 417 p = malloc(len + 1); 418 if (p == NULL) { 419 printf("421 4.3.0 memory exhausted\r\n"); 420 exit(EX_TEMPFAIL); 421 } 422 423 strncpy(p, s, len); 424 p[len] = '\0'; 425 return p; 426 } 427 428 char * 429 process_recipient(addr) 430 char *addr; 431 { 432 if (getpwnam(addr) == NULL) { 433 return "550 5.1.1 user unknown"; 434 } 435 436 return NULL; 437 } 438 439 440 #define RCPT_GROW 30 441 442 void 443 dolmtp(nobiff, nofsync) 444 int nobiff, nofsync; 445 { 446 char *return_path = NULL; 447 char **rcpt_addr = NULL; 448 int rcpt_num = 0; 449 int rcpt_alloc = 0; 450 char myhostname[1024]; 451 char buf[4096]; 452 char *err; 453 int msgfd; 454 char *p; 455 int i; 456 457 gethostname(myhostname, sizeof myhostname - 1); 458 459 printf("220 %s LMTP ready\r\n", myhostname); 460 for (;;) { 461 fflush(stdout); 462 if (fgets(buf, sizeof(buf)-1, stdin) == NULL) { 463 exit(EX_OK); 464 } 465 p = buf + strlen(buf) - 1; 466 if (p >= buf && *p == '\n') 467 *p-- = '\0'; 468 if (p >= buf && *p == '\r') 469 *p-- = '\0'; 470 471 switch (buf[0]) { 472 473 case 'd': 474 case 'D': 475 if (strcasecmp(buf, "data") == 0) { 476 if (rcpt_num == 0) { 477 printf("503 5.5.1 No recipients\r\n"); 478 continue; 479 } 480 msgfd = store(return_path, rcpt_num); 481 if (msgfd == -1) 482 continue; 483 484 for (i = 0; i < rcpt_num; i++) { 485 p = strchr(rcpt_addr[i], '+'); 486 if (p != NULL) 487 *p++ = '\0'; 488 deliver(msgfd, rcpt_addr[i], nobiff, 489 nofsync); 490 } 491 close(msgfd); 492 goto rset; 493 } 494 goto syntaxerr; 495 496 case 'l': 497 case 'L': 498 if (strncasecmp(buf, "lhlo ", 5) == 0) { 499 printf("250-%s\r\n250-8BITMIME\r\n250-ENHANCEDSTATUSCODES\r\n250 PIPELINING\r\n", 500 myhostname); 501 continue; 502 } 503 goto syntaxerr; 504 505 case 'm': 506 case 'M': 507 if (strncasecmp(buf, "mail ", 5) == 0) { 508 if (return_path != NULL) { 509 printf("503 5.5.1 Nested MAIL command\r\n"); 510 continue; 511 } 512 if (strncasecmp(buf+5, "from:", 5) != 0 || 513 ((return_path = parseaddr(buf+10)) == NULL)) { 514 printf("501 5.5.4 Syntax error in parameters\r\n"); 515 continue; 516 } 517 printf("250 2.5.0 ok\r\n"); 518 continue; 519 } 520 goto syntaxerr; 521 522 case 'n': 523 case 'N': 524 if (strcasecmp(buf, "noop") == 0) { 525 printf("250 2.0.0 ok\r\n"); 526 continue; 527 } 528 goto syntaxerr; 529 530 case 'q': 531 case 'Q': 532 if (strcasecmp(buf, "quit") == 0) { 533 printf("221 2.0.0 bye\r\n"); 534 exit(EX_OK); 535 } 536 goto syntaxerr; 537 538 case 'r': 539 case 'R': 540 if (strncasecmp(buf, "rcpt ", 5) == 0) { 541 if (return_path == NULL) { 542 printf("503 5.5.1 Need MAIL command\r\n"); 543 continue; 544 } 545 if (rcpt_num >= rcpt_alloc) { 546 rcpt_alloc += RCPT_GROW; 547 rcpt_addr = (char **) 548 realloc((char *)rcpt_addr, 549 rcpt_alloc * sizeof(char **)); 550 if (rcpt_addr == NULL) { 551 printf("421 4.3.0 memory exhausted\r\n"); 552 exit(EX_TEMPFAIL); 553 } 554 } 555 if (strncasecmp(buf+5, "to:", 3) != 0 || 556 ((rcpt_addr[rcpt_num] = parseaddr(buf+8)) == NULL)) { 557 printf("501 5.5.4 Syntax error in parameters\r\n"); 558 continue; 559 } 560 if ((err = process_recipient(rcpt_addr[rcpt_num])) != NULL) { 561 printf("%s\r\n", err); 562 continue; 563 } 564 rcpt_num++; 565 printf("250 2.1.5 ok\r\n"); 566 continue; 567 } 568 else if (strcasecmp(buf, "rset") == 0) { 569 printf("250 2.0.0 ok\r\n"); 570 571 rset: 572 while (rcpt_num) { 573 free(rcpt_addr[--rcpt_num]); 574 } 575 if (return_path != NULL) 576 free(return_path); 577 return_path = NULL; 578 continue; 579 } 580 goto syntaxerr; 581 582 case 'v': 583 case 'V': 584 if (strncasecmp(buf, "vrfy ", 5) == 0) { 585 printf("252 2.3.3 try RCPT to attempt delivery\r\n"); 586 continue; 587 } 588 goto syntaxerr; 589 590 default: 591 syntaxerr: 592 printf("500 5.5.2 Syntax error\r\n"); 593 continue; 594 } 595 } 596 } 597 598 int 599 store(from, lmtprcpts) 600 char *from; 601 int lmtprcpts; 602 { 603 FILE *fp = NULL; 604 time_t tval; 605 int fd, eline; 606 char line[2048]; 607 char tmpbuf[sizeof _PATH_LOCTMP + 1]; 608 609 strcpy(tmpbuf, _PATH_LOCTMP); 610 if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == NULL) { 611 if (lmtprcpts) { 612 printf("451 4.3.0 unable to open temporary file\r\n"); 613 return -1; 614 } else { 615 mailerr("451 4.3.0", "unable to open temporary file"); 616 exit(eval); 617 } 618 } 619 (void)unlink(tmpbuf); 620 621 if (lmtpmode) { 622 printf("354 go ahead\r\n"); 623 fflush(stdout); 624 } 625 626 (void)time(&tval); 627 (void)fprintf(fp, "From %s %s", from, ctime(&tval)); 628 629 line[0] = '\0'; 630 for (eline = 1; fgets(line, sizeof(line), stdin);) { 631 size_t line_len = strlen(line); 632 633 if (line_len >= 2 && 634 line[line_len - 2] == '\r' && 635 line[line_len - 1] == '\n') { 636 strcpy(line + line_len - 2, "\n"); 637 } 638 if (lmtprcpts && line[0] == '.') { 639 char *src = line + 1, *dest = line; 640 641 if (line[1] == '\n') 642 goto lmtpdot; 643 while (*src != '\0') 644 *dest++ = *src++; 645 *dest = '\0'; 646 } 647 if (line[0] == '\n') 648 eline = 1; 649 else { 650 if (eline && line[0] == 'F' && 651 !memcmp(line, "From ", 5)) 652 (void)putc('>', fp); 653 eline = 0; 654 } 655 (void)fprintf(fp, "%s", line); 656 if (ferror(fp)) { 657 if (lmtprcpts) { 658 while (lmtprcpts--) { 659 printf("451 4.3.0 temporary file write error\r\n"); 660 } 661 fclose(fp); 662 return -1; 663 } else { 664 mailerr("451 4.3.0", 665 "temporary file write error"); 666 fclose(fp); 667 exit(eval); 668 } 669 } 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') == NULL) 679 (void)putc('\n', fp); 680 681 lmtpdot: 682 683 /* Output a newline; note, empty messages are allowed. */ 684 (void)putc('\n', fp); 685 686 if (fflush(fp) == EOF || ferror(fp)) { 687 if (lmtprcpts) { 688 while (lmtprcpts--) { 689 printf("451 4.3.0 temporary file write error\r\n"); 690 } 691 fclose(fp); 692 return -1; 693 } else { 694 mailerr("451 4.3.0", "temporary file write error"); 695 fclose(fp); 696 exit(eval); 697 } 698 } 699 return (fd); 700 } 701 702 void 703 deliver(fd, name, nobiff, nofsync) 704 int fd; 705 char *name; 706 int nobiff, nofsync; 707 { 708 struct stat fsb, sb; 709 struct passwd *pw; 710 int mbfd, nr, nw, off; 711 char *p; 712 char biffmsg[100], buf[8*1024], path[MAXPATHLEN]; 713 off_t curoff; 714 extern char *quad_to_string(); 715 716 /* 717 * Disallow delivery to unknown names -- special mailboxes can be 718 * handled in the sendmail aliases file. 719 */ 720 if ((pw = getpwnam(name)) == NULL) { 721 if (eval != EX_TEMPFAIL) 722 eval = EX_UNAVAILABLE; 723 if (lmtpmode) { 724 if (eval == EX_TEMPFAIL) { 725 printf("451 4.3.0 cannot lookup name: %s\r\n", name); 726 } else { 727 printf("550 5.1.1 unknown name: %s\r\n", name); 728 } 729 } 730 else { 731 char *errcode = NULL; 732 733 if (eval == EX_TEMPFAIL) 734 errcode = "451 4.3.0"; 735 else 736 errcode = "550 5.1.1"; 737 mailerr(errcode, "unknown name: %s", name); 738 } 739 return; 740 } 741 endpwent(); 742 743 /* 744 * Keep name reasonably short to avoid buffer overruns. 745 * This isn't necessary on BSD because of the proper 746 * definition of snprintf(), but it can cause problems 747 * on other systems. 748 * Also, clear out any bogus characters. 749 */ 750 751 if (strlen(name) > 40) 752 name[40] = '\0'; 753 for (p = name; *p != '\0'; p++) 754 { 755 if (!isascii(*p)) 756 *p &= 0x7f; 757 else if (!isprint(*p)) 758 *p = '.'; 759 } 760 761 (void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name); 762 763 /* 764 * If the mailbox is linked or a symlink, fail. There's an obvious 765 * race here, that the file was replaced with a symbolic link after 766 * the lstat returned, but before the open. We attempt to detect 767 * this by comparing the original stat information and information 768 * returned by an fstat of the file descriptor returned by the open. 769 * 770 * NB: this is a symptom of a larger problem, that the mail spooling 771 * directory is writeable by the wrong users. If that directory is 772 * writeable, system security is compromised for other reasons, and 773 * it cannot be fixed here. 774 * 775 * If we created the mailbox, set the owner/group. If that fails, 776 * just return. Another process may have already opened it, so we 777 * can't unlink it. Historically, binmail set the owner/group at 778 * each mail delivery. We no longer do this, assuming that if the 779 * ownership or permissions were changed there was a reason. 780 * 781 * XXX 782 * open(2) should support flock'ing the file. 783 */ 784 tryagain: 785 lockmbox(path); 786 if (lstat(path, &sb) < 0) { 787 mbfd = open(path, 788 O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR); 789 if (lstat(path, &sb) < 0) 790 { 791 eval = EX_CANTCREAT; 792 mailerr("550 5.2.0", 793 "%s: lstat: file changed after open", path); 794 goto err1; 795 } 796 else 797 sb.st_uid = pw->pw_uid; 798 if (mbfd == -1) { 799 if (errno == EEXIST) 800 goto tryagain; 801 } else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) { 802 mailerr("451 4.3.0", "chown %u.%u: %s", 803 pw->pw_uid, pw->pw_gid, name); 804 goto err1; 805 } 806 } else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) { 807 mailerr("550 5.2.0", "%s: irregular file", path); 808 goto err0; 809 } else if (sb.st_uid != pw->pw_uid) { 810 eval = EX_CANTCREAT; 811 mailerr("550 5.2.0", "%s: wrong ownership (%d)", 812 path, sb.st_uid); 813 goto err0; 814 } else { 815 mbfd = open(path, O_APPEND|O_WRONLY, 0); 816 } 817 818 if (mbfd == -1) { 819 mailerr("450 4.2.0", "%s: %s", path, strerror(errno)); 820 goto err0; 821 } else if (fstat(mbfd, &fsb) < 0 || 822 fsb.st_nlink != 1 || 823 sb.st_nlink != 1 || 824 !S_ISREG(fsb.st_mode) || 825 sb.st_dev != fsb.st_dev || 826 sb.st_ino != fsb.st_ino || 827 #if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */ 828 sb.st_gen != fsb.st_gen || 829 #endif 830 sb.st_uid != fsb.st_uid) { 831 eval = EX_TEMPFAIL; 832 mailerr("550 5.2.0", "%s: fstat: file changed after open", 833 path); 834 goto err1; 835 } 836 837 /* Wait until we can get a lock on the file. */ 838 if (flock(mbfd, LOCK_EX)) { 839 mailerr("450 4.2.0", "%s: %s", path, strerror(errno)); 840 goto err1; 841 } 842 843 if (!nobiff) { 844 /* Get the starting offset of the new message for biff. */ 845 curoff = lseek(mbfd, (off_t)0, SEEK_END); 846 if (sizeof curoff > sizeof(long)) 847 (void)snprintf(biffmsg, sizeof(biffmsg), "%s@%s\n", 848 name, quad_to_string(curoff)); 849 else 850 (void)snprintf(biffmsg, sizeof(biffmsg), "%s@%ld\n", 851 name, curoff); 852 } 853 854 /* Copy the message into the file. */ 855 if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) { 856 mailerr("450 4.2.0", "temporary file: %s", 857 strerror(errno)); 858 goto err1; 859 } 860 if (setreuid(0, pw->pw_uid) < 0) { 861 mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)", 862 pw->pw_uid, strerror(errno), getuid(), geteuid()); 863 goto err1; 864 } 865 #ifdef DEBUG 866 printf("new euid = %d\n", geteuid()); 867 #endif 868 while ((nr = read(fd, buf, sizeof(buf))) > 0) 869 for (off = 0; off < nr; off += nw) 870 if ((nw = write(mbfd, buf + off, nr - off)) < 0) { 871 mailerr("450 4.2.0", "%s: %s", 872 path, strerror(errno)); 873 goto err3; 874 } 875 if (nr < 0) { 876 mailerr("450 4.2.0", "temporary file: %s", 877 strerror(errno)); 878 goto err3; 879 } 880 881 /* Flush to disk, don't wait for update. */ 882 if (!nofsync && fsync(mbfd)) { 883 mailerr("450 4.2.0", "%s: %s", path, strerror(errno)); 884 err3: 885 if (setreuid(0, 0) < 0) { 886 #if 0 887 /* already printed an error above for this recipient */ 888 e_to_sys(errno); 889 mailerr("450 4.2.0", "setreuid(0, 0): %s", 890 strerror(errno)); 891 #endif 892 } 893 #ifdef DEBUG 894 printf("reset euid = %d\n", geteuid()); 895 #endif 896 (void)ftruncate(mbfd, curoff); 897 err1: (void)close(mbfd); 898 err0: unlockmbox(); 899 return; 900 } 901 902 /* Close and check -- NFS doesn't write until the close. */ 903 if (close(mbfd)) { 904 mailerr("450 4.2.0", "%s: %s", path, strerror(errno)); 905 truncate(path, curoff); 906 } else if (!nobiff) 907 notifybiff(biffmsg); 908 909 if (setreuid(0, 0) < 0) { 910 mailerr("450 4.2.0", "setreuid(0, 0): %s", 911 strerror(errno)); 912 goto err0; 913 } 914 #ifdef DEBUG 915 printf("reset euid = %d\n", geteuid()); 916 #endif 917 unlockmbox(); 918 if (lmtpmode) { 919 printf("250 2.1.5 %s OK\r\n", name); 920 } 921 } 922 923 /* 924 * user.lock files are necessary for compatibility with other 925 * systems, e.g., when the mail spool file is NFS exported. 926 * Alas, mailbox locking is more than just a local matter. 927 * EPA 11/94. 928 */ 929 930 char lockname[MAXPATHLEN]; 931 int locked = 0; 932 933 void 934 lockmbox(path) 935 char *path; 936 { 937 int statfailed = 0; 938 939 if (locked) 940 return; 941 if (strlen(path) + 6 > sizeof lockname) 942 return; 943 snprintf(lockname, sizeof lockname, "%s.lock", path); 944 for (;; sleep(5)) { 945 int fd; 946 struct stat st; 947 time_t now; 948 949 fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0); 950 if (fd >= 0) { 951 /* defeat lock checking programs which test pid */ 952 write(fd, "0", 2); 953 locked = 1; 954 close(fd); 955 return; 956 } 957 if (stat(lockname, &st) < 0) { 958 if (statfailed++ > 5) 959 return; 960 continue; 961 } 962 statfailed = 0; 963 time(&now); 964 if (now < st.st_ctime + 300) 965 continue; 966 unlink(lockname); 967 } 968 } 969 970 void 971 unlockmbox() 972 { 973 if (!locked) 974 return; 975 unlink(lockname); 976 locked = 0; 977 } 978 979 void 980 notifybiff(msg) 981 char *msg; 982 { 983 static struct sockaddr_in addr; 984 static int f = -1; 985 struct hostent *hp; 986 struct servent *sp; 987 int len; 988 989 if (addr.sin_family == 0) { 990 /* Be silent if biff service not available. */ 991 if ((sp = getservbyname("biff", "udp")) == NULL) 992 return; 993 if ((hp = gethostbyname("localhost")) == NULL) { 994 return; 995 } 996 addr.sin_family = hp->h_addrtype; 997 memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); 998 addr.sin_port = sp->s_port; 999 } 1000 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 1001 return; 1002 } 1003 len = strlen(msg) + 1; 1004 (void) sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr)); 1005 } 1006 1007 void 1008 usage() 1009 { 1010 eval = EX_USAGE; 1011 mailerr(NULL, "usage: mail.local [-b] [-l] [-f from] [-s] user ..."); 1012 exit(eval); 1013 } 1014 1015 void 1016 #ifdef __STDC__ 1017 mailerr(const char *hdr, const char *fmt, ...) 1018 #else 1019 mailerr(hdr, fmt, va_alist) 1020 const char *hdr; 1021 const char *fmt; 1022 va_dcl 1023 #endif 1024 { 1025 va_list ap; 1026 1027 #ifdef __STDC__ 1028 va_start(ap, fmt); 1029 #else 1030 va_start(ap); 1031 #endif 1032 if (lmtpmode) 1033 { 1034 if (hdr != NULL) 1035 printf("%s ", hdr); 1036 vprintf(fmt, ap); 1037 printf("\r\n"); 1038 } 1039 else 1040 { 1041 e_to_sys(errno); 1042 vwarn(fmt, ap); 1043 } 1044 } 1045 1046 void 1047 vwarn(fmt, ap) 1048 const char *fmt; 1049 _BSD_VA_LIST_ ap; 1050 { 1051 /* 1052 * Log the message to stderr. 1053 * 1054 * Don't use LOG_PERROR as an openlog() flag to do this, 1055 * it's not portable enough. 1056 */ 1057 if (eval != EX_USAGE) 1058 (void)fprintf(stderr, "mail.local: "); 1059 (void)vfprintf(stderr, fmt, ap); 1060 (void)fprintf(stderr, "\n"); 1061 1062 #if USE_VSYSLOG 1063 /* Log the message to syslog. */ 1064 vsyslog(LOG_ERR, fmt, ap); 1065 #else 1066 { 1067 char fmtbuf[10240]; 1068 1069 (void) vsnprintf(fmtbuf, sizeof fmtbuf, fmt, ap); 1070 syslog(LOG_ERR, "%s", fmtbuf); 1071 } 1072 #endif 1073 } 1074 1075 /* 1076 * e_to_sys -- 1077 * Guess which errno's are temporary. Gag me. 1078 */ 1079 void 1080 e_to_sys(num) 1081 int num; 1082 { 1083 /* Temporary failures override hard errors. */ 1084 if (eval == EX_TEMPFAIL) 1085 return; 1086 1087 switch(num) { /* Hopefully temporary errors. */ 1088 #ifdef EAGAIN 1089 case EAGAIN: /* Resource temporarily unavailable */ 1090 #endif 1091 #ifdef EDQUOT 1092 case EDQUOT: /* Disc quota exceeded */ 1093 #endif 1094 #ifdef EBUSY 1095 case EBUSY: /* Device busy */ 1096 #endif 1097 #ifdef EPROCLIM 1098 case EPROCLIM: /* Too many processes */ 1099 #endif 1100 #ifdef EUSERS 1101 case EUSERS: /* Too many users */ 1102 #endif 1103 #ifdef ECONNABORTED 1104 case ECONNABORTED: /* Software caused connection abort */ 1105 #endif 1106 #ifdef ECONNREFUSED 1107 case ECONNREFUSED: /* Connection refused */ 1108 #endif 1109 #ifdef ECONNRESET 1110 case ECONNRESET: /* Connection reset by peer */ 1111 #endif 1112 #ifdef EDEADLK 1113 case EDEADLK: /* Resource deadlock avoided */ 1114 #endif 1115 #ifdef EFBIG 1116 case EFBIG: /* File too large */ 1117 #endif 1118 #ifdef EHOSTDOWN 1119 case EHOSTDOWN: /* Host is down */ 1120 #endif 1121 #ifdef EHOSTUNREACH 1122 case EHOSTUNREACH: /* No route to host */ 1123 #endif 1124 #ifdef EMFILE 1125 case EMFILE: /* Too many open files */ 1126 #endif 1127 #ifdef ENETDOWN 1128 case ENETDOWN: /* Network is down */ 1129 #endif 1130 #ifdef ENETRESET 1131 case ENETRESET: /* Network dropped connection on reset */ 1132 #endif 1133 #ifdef ENETUNREACH 1134 case ENETUNREACH: /* Network is unreachable */ 1135 #endif 1136 #ifdef ENFILE 1137 case ENFILE: /* Too many open files in system */ 1138 #endif 1139 #ifdef ENOBUFS 1140 case ENOBUFS: /* No buffer space available */ 1141 #endif 1142 #ifdef ENOMEM 1143 case ENOMEM: /* Cannot allocate memory */ 1144 #endif 1145 #ifdef ENOSPC 1146 case ENOSPC: /* No space left on device */ 1147 #endif 1148 #ifdef EROFS 1149 case EROFS: /* Read-only file system */ 1150 #endif 1151 #ifdef ESTALE 1152 case ESTALE: /* Stale NFS file handle */ 1153 #endif 1154 #ifdef ETIMEDOUT 1155 case ETIMEDOUT: /* Connection timed out */ 1156 #endif 1157 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK 1158 case EWOULDBLOCK: /* Operation would block. */ 1159 #endif 1160 eval = EX_TEMPFAIL; 1161 break; 1162 default: 1163 eval = EX_UNAVAILABLE; 1164 break; 1165 } 1166 } 1167 1168 #if !HASSTRERROR 1169 1170 char * 1171 strerror(eno) 1172 int eno; 1173 { 1174 extern int sys_nerr; 1175 extern char *sys_errlist[]; 1176 static char ebuf[60]; 1177 1178 if (eno >= 0 && eno < sys_nerr) 1179 return sys_errlist[eno]; 1180 (void) sprintf(ebuf, "Error %d", eno); 1181 return ebuf; 1182 } 1183 1184 #endif /* !HASSTRERROR */ 1185 1186 #if defined(ultrix) || defined(_CRAY) 1187 1188 /* 1189 * Copyright (c) 1987, 1993 1190 * The Regents of the University of California. All rights reserved. 1191 * 1192 * Redistribution and use in source and binary forms, with or without 1193 * modification, are permitted provided that the following conditions 1194 * are met: 1195 * 1. Redistributions of source code must retain the above copyright 1196 * notice, this list of conditions and the following disclaimer. 1197 * 2. Redistributions in binary form must reproduce the above copyright 1198 * notice, this list of conditions and the following disclaimer in the 1199 * documentation and/or other materials provided with the distribution. 1200 * 3. All advertising materials mentioning features or use of this software 1201 * must display the following acknowledgement: 1202 * This product includes software developed by the University of 1203 * California, Berkeley and its contributors. 1204 * 4. Neither the name of the University nor the names of its contributors 1205 * may be used to endorse or promote products derived from this software 1206 * without specific prior written permission. 1207 * 1208 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1209 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1210 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1211 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 1212 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1213 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1214 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 1215 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 1216 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 1217 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1218 * SUCH DAMAGE. 1219 */ 1220 1221 #if defined(LIBC_SCCS) && !defined(lint) 1222 static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; 1223 #endif /* LIBC_SCCS and not lint */ 1224 1225 #include <sys/types.h> 1226 #include <sys/stat.h> 1227 #include <fcntl.h> 1228 #include <errno.h> 1229 #include <stdio.h> 1230 #include <ctype.h> 1231 1232 static int _gettemp(); 1233 1234 mkstemp(path) 1235 char *path; 1236 { 1237 int fd; 1238 1239 return (_gettemp(path, &fd) ? fd : -1); 1240 } 1241 1242 /* 1243 char * 1244 mktemp(path) 1245 char *path; 1246 { 1247 return(_gettemp(path, (int *)NULL) ? path : (char *)NULL); 1248 } 1249 */ 1250 1251 static 1252 _gettemp(path, doopen) 1253 char *path; 1254 register int *doopen; 1255 { 1256 extern int errno; 1257 register char *start, *trv; 1258 struct stat sbuf; 1259 u_int pid; 1260 1261 pid = getpid(); 1262 for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ 1263 while (*--trv == 'X') { 1264 *trv = (pid % 10) + '0'; 1265 pid /= 10; 1266 } 1267 1268 /* 1269 * check the target directory; if you have six X's and it 1270 * doesn't exist this runs for a *very* long time. 1271 */ 1272 for (start = trv + 1;; --trv) { 1273 if (trv <= path) 1274 break; 1275 if (*trv == '/') { 1276 *trv = '\0'; 1277 if (stat(path, &sbuf) < 0) 1278 return(0); 1279 if (!S_ISDIR(sbuf.st_mode)) { 1280 errno = ENOTDIR; 1281 return(0); 1282 } 1283 *trv = '/'; 1284 break; 1285 } 1286 } 1287 1288 for (;;) { 1289 if (doopen) { 1290 if ((*doopen = 1291 open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0) 1292 return(1); 1293 if (errno != EEXIST) 1294 return(0); 1295 } 1296 else if (stat(path, &sbuf) < 0) 1297 return(errno == ENOENT ? 1 : 0); 1298 1299 /* tricky little algorithm for backward compatibility */ 1300 for (trv = start;;) { 1301 if (!*trv) 1302 return(0); 1303 if (*trv == 'z') 1304 *trv++ = 'a'; 1305 else { 1306 if (isascii(*trv) && isdigit(*trv)) 1307 *trv = 'a'; 1308 else 1309 ++*trv; 1310 break; 1311 } 1312 } 1313 } 1314 /*NOTREACHED*/ 1315 } 1316 1317 #endif /* ultrix */ 1318