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