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