1 /* 2 * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1990, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * By using this file, you agree to the terms and conditions set 8 * forth in the LICENSE file which can be found at the top level of 9 * the sendmail distribution. 10 * 11 * $FreeBSD$ 12 * 13 */ 14 15 #include <sm/gen.h> 16 17 SM_IDSTR(copyright, 18 "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\ 19 All rights reserved.\n\ 20 Copyright (c) 1990, 1993, 1994\n\ 21 The Regents of the University of California. All rights reserved.\n") 22 23 SM_IDSTR(id, "@(#)$Id: mail.local.c,v 8.239.2.11 2003/09/01 01:49:46 gshapiro Exp $") 24 25 #include <stdlib.h> 26 #include <sm/errstring.h> 27 #include <sm/io.h> 28 #include <sm/limits.h> 29 # include <unistd.h> 30 # ifdef EX_OK 31 # undef EX_OK /* unistd.h may have another use for this */ 32 # endif /* EX_OK */ 33 # define LOCKFILE_PMODE 0 34 #include <sm/mbdb.h> 35 #include <sm/sysexits.h> 36 37 /* 38 ** This is not intended to work on System V derived systems 39 ** such as Solaris or HP-UX, since they use a totally different 40 ** approach to mailboxes (essentially, they have a set-group-ID program 41 ** rather than set-user-ID, and they rely on the ability to "give away" 42 ** files to do their work). IT IS NOT A BUG that this doesn't 43 ** work on such architectures. 44 */ 45 46 47 #include <stdio.h> 48 #include <errno.h> 49 #include <fcntl.h> 50 #include <sys/types.h> 51 #include <sys/stat.h> 52 #include <time.h> 53 #include <stdlib.h> 54 # include <sys/socket.h> 55 # include <sys/file.h> 56 # include <netinet/in.h> 57 # include <arpa/nameser.h> 58 # include <netdb.h> 59 # include <pwd.h> 60 61 #include <sm/string.h> 62 #include <syslog.h> 63 #include <ctype.h> 64 65 #include <sm/conf.h> 66 #include <sendmail/pathnames.h> 67 68 69 #ifndef LOCKTO_RM 70 # define LOCKTO_RM 300 /* timeout for stale lockfile removal */ 71 #endif /* ! LOCKTO_RM */ 72 #ifndef LOCKTO_GLOB 73 # define LOCKTO_GLOB 400 /* global timeout for lockfile creation */ 74 #endif /* ! LOCKTO_GLOB */ 75 76 /* define a realloc() which works for NULL pointers */ 77 #define REALLOC(ptr, size) (((ptr) == NULL) ? malloc(size) : realloc(ptr, size)) 78 79 /* 80 ** If you don't have flock, you could try using lockf instead. 81 */ 82 83 #ifdef LDA_USE_LOCKF 84 # define flock(a, b) lockf(a, b, 0) 85 # ifdef LOCK_EX 86 # undef LOCK_EX 87 # endif /* LOCK_EX */ 88 # define LOCK_EX F_LOCK 89 #endif /* LDA_USE_LOCKF */ 90 91 #ifndef LOCK_EX 92 # include <sys/file.h> 93 #endif /* ! LOCK_EX */ 94 95 /* 96 ** If you don't have setreuid, and you have saved uids, and you have 97 ** a seteuid() call that doesn't try to emulate using setuid(), then 98 ** you can try defining LDA_USE_SETEUID. 99 */ 100 101 #ifdef LDA_USE_SETEUID 102 # define setreuid(r, e) seteuid(e) 103 #endif /* LDA_USE_SETEUID */ 104 105 #ifdef LDA_CONTENTLENGTH 106 # define CONTENTLENGTH 1 107 #endif /* LDA_CONTENTLENGTH */ 108 109 #ifndef INADDRSZ 110 # define INADDRSZ 4 /* size of an IPv4 address in bytes */ 111 #endif /* ! INADDRSZ */ 112 113 #ifdef MAILLOCK 114 # include <maillock.h> 115 #endif /* MAILLOCK */ 116 117 #ifndef MAILER_DAEMON 118 # define MAILER_DAEMON "MAILER-DAEMON" 119 #endif /* ! MAILER_DAEMON */ 120 121 #ifdef CONTENTLENGTH 122 char ContentHdr[40] = "Content-Length: "; 123 off_t HeaderLength; 124 off_t BodyLength; 125 #endif /* CONTENTLENGTH */ 126 127 bool EightBitMime = true; /* advertise 8BITMIME in LMTP */ 128 char ErrBuf[10240]; /* error buffer */ 129 int ExitVal = EX_OK; /* sysexits.h error value. */ 130 bool nobiff = false; 131 bool nofsync = false; 132 bool HoldErrs = false; /* Hold errors in ErrBuf */ 133 bool LMTPMode = false; 134 bool BounceQuota = false; /* permanent error when over quota */ 135 char *HomeMailFile = NULL; /* store mail in homedir */ 136 137 void deliver __P((int, char *)); 138 int e_to_sys __P((int)); 139 void notifybiff __P((char *)); 140 int store __P((char *, bool *)); 141 void usage __P((void)); 142 int lockmbox __P((char *)); 143 void unlockmbox __P((void)); 144 void mailerr __P((const char *, const char *, ...)); 145 void flush_error __P((void)); 146 147 148 int 149 main(argc, argv) 150 int argc; 151 char *argv[]; 152 { 153 struct passwd *pw; 154 int ch, fd; 155 uid_t uid; 156 char *from; 157 char *mbdbname = "pw"; 158 int err; 159 extern char *optarg; 160 extern int optind; 161 162 163 /* make sure we have some open file descriptors */ 164 for (fd = 10; fd < 30; fd++) 165 (void) close(fd); 166 167 /* use a reasonable umask */ 168 (void) umask(0077); 169 170 # ifdef LOG_MAIL 171 openlog("mail.local", 0, LOG_MAIL); 172 # else /* LOG_MAIL */ 173 openlog("mail.local", 0); 174 # endif /* LOG_MAIL */ 175 176 from = NULL; 177 while ((ch = getopt(argc, argv, "7BbdD:f:h:r:ls")) != -1) 178 { 179 switch(ch) 180 { 181 case '7': /* Do not advertise 8BITMIME */ 182 EightBitMime = false; 183 break; 184 185 case 'B': 186 nobiff = true; 187 break; 188 189 case 'b': /* bounce mail when over quota. */ 190 BounceQuota = true; 191 break; 192 193 case 'd': /* Backward compatible. */ 194 break; 195 196 case 'D': /* mailbox database type */ 197 mbdbname = optarg; 198 break; 199 200 case 'f': 201 case 'r': /* Backward compatible. */ 202 if (from != NULL) 203 { 204 mailerr(NULL, "Multiple -f options"); 205 usage(); 206 } 207 from = optarg; 208 break; 209 210 case 'h': 211 if (optarg != NULL || *optarg != '\0') 212 HomeMailFile = optarg; 213 else 214 { 215 mailerr(NULL, "-h: missing filename"); 216 usage(); 217 } 218 break; 219 220 case 'l': 221 LMTPMode = true; 222 break; 223 224 case 's': 225 nofsync++; 226 break; 227 228 case '?': 229 default: 230 usage(); 231 } 232 } 233 argc -= optind; 234 argv += optind; 235 236 /* initialize biff structures */ 237 if (!nobiff) 238 notifybiff(NULL); 239 240 err = sm_mbdb_initialize(mbdbname); 241 if (err != EX_OK) 242 { 243 char *errcode = "521"; 244 245 if (err == EX_TEMPFAIL) 246 errcode = "421"; 247 248 mailerr(errcode, "Can not open mailbox database %s: %s", 249 mbdbname, sm_strexit(err)); 250 exit(err); 251 } 252 253 if (LMTPMode) 254 { 255 extern void dolmtp __P((void)); 256 257 if (argc > 0) 258 { 259 mailerr("421", "Users should not be specified in command line if LMTP required"); 260 exit(EX_TEMPFAIL); 261 } 262 263 dolmtp(); 264 /* NOTREACHED */ 265 exit(EX_OK); 266 } 267 268 /* Non-LMTP from here on out */ 269 if (*argv == '\0') 270 usage(); 271 272 /* 273 ** If from not specified, use the name from getlogin() if the 274 ** uid matches, otherwise, use the name from the password file 275 ** corresponding to the uid. 276 */ 277 278 uid = getuid(); 279 if (from == NULL && ((from = getlogin()) == NULL || 280 (pw = getpwnam(from)) == NULL || 281 pw->pw_uid != uid)) 282 from = (pw = getpwuid(uid)) != NULL ? pw->pw_name : "???"; 283 284 /* 285 ** There is no way to distinguish the error status of one delivery 286 ** from the rest of the deliveries. So, if we failed hard on one 287 ** or more deliveries, but had no failures on any of the others, we 288 ** return a hard failure. If we failed temporarily on one or more 289 ** deliveries, we return a temporary failure regardless of the other 290 ** failures. This results in the delivery being reattempted later 291 ** at the expense of repeated failures and multiple deliveries. 292 */ 293 294 HoldErrs = true; 295 fd = store(from, NULL); 296 HoldErrs = false; 297 if (fd < 0) 298 { 299 flush_error(); 300 exit(ExitVal); 301 } 302 for (; *argv != NULL; ++argv) 303 deliver(fd, *argv); 304 exit(ExitVal); 305 /* NOTREACHED */ 306 return ExitVal; 307 } 308 309 char * 310 parseaddr(s, rcpt) 311 char *s; 312 bool rcpt; 313 { 314 char *p; 315 int l; 316 317 if (*s++ != '<') 318 return NULL; 319 320 p = s; 321 322 /* at-domain-list */ 323 while (*p == '@') 324 { 325 p++; 326 while (*p != ',' && *p != ':' && *p != '\0') 327 p++; 328 if (*p == '\0') 329 return NULL; 330 331 /* Skip over , or : */ 332 p++; 333 } 334 335 s = p; 336 337 /* local-part */ 338 while (*p != '\0' && *p != '@' && *p != '>') 339 { 340 if (*p == '\\') 341 { 342 if (*++p == '\0') 343 return NULL; 344 } 345 else if (*p == '\"') 346 { 347 p++; 348 while (*p != '\0' && *p != '\"') 349 { 350 if (*p == '\\') 351 { 352 if (*++p == '\0') 353 return NULL; 354 } 355 p++; 356 } 357 if (*p == '\0' || *(p + 1) == '\0') 358 return NULL; 359 } 360 /* +detail ? */ 361 if (*p == '+' && rcpt) 362 *p = '\0'; 363 p++; 364 } 365 366 /* @domain */ 367 if (*p == '@') 368 { 369 if (rcpt) 370 *p++ = '\0'; 371 while (*p != '\0' && *p != '>') 372 p++; 373 } 374 375 if (*p != '>') 376 return NULL; 377 else 378 *p = '\0'; 379 p++; 380 381 if (*p != '\0' && *p != ' ') 382 return NULL; 383 384 if (*s == '\0') 385 s = MAILER_DAEMON; 386 387 l = strlen(s) + 1; 388 if (l < 0) 389 return NULL; 390 p = malloc(l); 391 if (p == NULL) 392 { 393 mailerr("421 4.3.0", "Memory exhausted"); 394 exit(EX_TEMPFAIL); 395 } 396 397 (void) sm_strlcpy(p, s, l); 398 return p; 399 } 400 401 char * 402 process_recipient(addr) 403 char *addr; 404 { 405 SM_MBDB_T user; 406 407 switch (sm_mbdb_lookup(addr, &user)) 408 { 409 case EX_OK: 410 return NULL; 411 412 case EX_NOUSER: 413 return "550 5.1.1 User unknown"; 414 415 case EX_TEMPFAIL: 416 return "451 4.3.0 User database failure; retry later"; 417 418 default: 419 return "550 5.3.0 User database failure"; 420 } 421 } 422 423 #define RCPT_GROW 30 424 425 void 426 dolmtp() 427 { 428 char *return_path = NULL; 429 char **rcpt_addr = NULL; 430 int rcpt_num = 0; 431 int rcpt_alloc = 0; 432 bool gotlhlo = false; 433 char *err; 434 int msgfd; 435 char *p; 436 int i; 437 char myhostname[1024]; 438 char buf[4096]; 439 440 memset(myhostname, '\0', sizeof myhostname); 441 (void) gethostname(myhostname, sizeof myhostname - 1); 442 if (myhostname[0] == '\0') 443 sm_strlcpy(myhostname, "localhost", sizeof myhostname); 444 445 printf("220 %s LMTP ready\r\n", myhostname); 446 for (;;) 447 { 448 (void) fflush(stdout); 449 if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) 450 exit(EX_OK); 451 p = buf + strlen(buf) - 1; 452 if (p >= buf && *p == '\n') 453 *p-- = '\0'; 454 if (p >= buf && *p == '\r') 455 *p-- = '\0'; 456 457 switch (buf[0]) 458 { 459 case 'd': 460 case 'D': 461 if (sm_strcasecmp(buf, "data") == 0) 462 { 463 bool inbody = false; 464 465 if (rcpt_num == 0) 466 { 467 mailerr("503 5.5.1", "No recipients"); 468 continue; 469 } 470 HoldErrs = true; 471 msgfd = store(return_path, &inbody); 472 HoldErrs = false; 473 if (msgfd < 0 && !inbody) 474 { 475 flush_error(); 476 continue; 477 } 478 479 for (i = 0; i < rcpt_num; i++) 480 { 481 if (msgfd < 0) 482 { 483 /* print error for rcpt */ 484 flush_error(); 485 continue; 486 } 487 p = strchr(rcpt_addr[i], '+'); 488 if (p != NULL) 489 *p = '\0'; 490 deliver(msgfd, rcpt_addr[i]); 491 } 492 if (msgfd >= 0) 493 (void) close(msgfd); 494 goto rset; 495 } 496 goto syntaxerr; 497 /* NOTREACHED */ 498 break; 499 500 case 'l': 501 case 'L': 502 if (sm_strncasecmp(buf, "lhlo ", 5) == 0) 503 { 504 /* check for duplicate per RFC 1651 4.2 */ 505 if (gotlhlo) 506 { 507 mailerr("503", "%s Duplicate LHLO", 508 myhostname); 509 continue; 510 } 511 gotlhlo = true; 512 printf("250-%s\r\n", myhostname); 513 if (EightBitMime) 514 printf("250-8BITMIME\r\n"); 515 printf("250-ENHANCEDSTATUSCODES\r\n"); 516 printf("250 PIPELINING\r\n"); 517 continue; 518 } 519 goto syntaxerr; 520 /* NOTREACHED */ 521 break; 522 523 case 'm': 524 case 'M': 525 if (sm_strncasecmp(buf, "mail ", 5) == 0) 526 { 527 if (return_path != NULL) 528 { 529 mailerr("503 5.5.1", 530 "Nested MAIL command"); 531 continue; 532 } 533 if (sm_strncasecmp(buf + 5, "from:", 5) != 0 || 534 ((return_path = parseaddr(buf + 10, 535 false)) == NULL)) 536 { 537 mailerr("501 5.5.4", 538 "Syntax error in parameters"); 539 continue; 540 } 541 printf("250 2.5.0 Ok\r\n"); 542 continue; 543 } 544 goto syntaxerr; 545 /* NOTREACHED */ 546 break; 547 548 case 'n': 549 case 'N': 550 if (sm_strcasecmp(buf, "noop") == 0) 551 { 552 printf("250 2.0.0 Ok\r\n"); 553 continue; 554 } 555 goto syntaxerr; 556 /* NOTREACHED */ 557 break; 558 559 case 'q': 560 case 'Q': 561 if (sm_strcasecmp(buf, "quit") == 0) 562 { 563 printf("221 2.0.0 Bye\r\n"); 564 exit(EX_OK); 565 } 566 goto syntaxerr; 567 /* NOTREACHED */ 568 break; 569 570 case 'r': 571 case 'R': 572 if (sm_strncasecmp(buf, "rcpt ", 5) == 0) 573 { 574 if (return_path == NULL) 575 { 576 mailerr("503 5.5.1", 577 "Need MAIL command"); 578 continue; 579 } 580 if (rcpt_num >= rcpt_alloc) 581 { 582 rcpt_alloc += RCPT_GROW; 583 rcpt_addr = (char **) 584 REALLOC((char *) rcpt_addr, 585 rcpt_alloc * 586 sizeof(char **)); 587 if (rcpt_addr == NULL) 588 { 589 mailerr("421 4.3.0", 590 "Memory exhausted"); 591 exit(EX_TEMPFAIL); 592 } 593 } 594 if (sm_strncasecmp(buf + 5, "to:", 3) != 0 || 595 ((rcpt_addr[rcpt_num] = parseaddr(buf + 8, 596 true)) == NULL)) 597 { 598 mailerr("501 5.5.4", 599 "Syntax error in parameters"); 600 continue; 601 } 602 err = process_recipient(rcpt_addr[rcpt_num]); 603 if (err != NULL) 604 { 605 mailerr(NULL, "%s", err); 606 continue; 607 } 608 rcpt_num++; 609 printf("250 2.1.5 Ok\r\n"); 610 continue; 611 } 612 else if (sm_strcasecmp(buf, "rset") == 0) 613 { 614 printf("250 2.0.0 Ok\r\n"); 615 616 rset: 617 while (rcpt_num > 0) 618 free(rcpt_addr[--rcpt_num]); 619 if (return_path != NULL) 620 free(return_path); 621 return_path = NULL; 622 continue; 623 } 624 goto syntaxerr; 625 /* NOTREACHED */ 626 break; 627 628 case 'v': 629 case 'V': 630 if (sm_strncasecmp(buf, "vrfy ", 5) == 0) 631 { 632 printf("252 2.3.3 Try RCPT to attempt delivery\r\n"); 633 continue; 634 } 635 goto syntaxerr; 636 /* NOTREACHED */ 637 break; 638 639 default: 640 syntaxerr: 641 mailerr("500 5.5.2", "Syntax error"); 642 continue; 643 /* NOTREACHED */ 644 break; 645 } 646 } 647 } 648 649 int 650 store(from, inbody) 651 char *from; 652 bool *inbody; 653 { 654 FILE *fp = NULL; 655 time_t tval; 656 bool eline; /* previous line was empty */ 657 bool fullline = true; /* current line is terminated */ 658 bool prevfl; /* previous line was terminated */ 659 char line[2048]; 660 int fd; 661 char tmpbuf[sizeof _PATH_LOCTMP + 1]; 662 663 if (inbody != NULL) 664 *inbody = false; 665 666 (void) umask(0077); 667 (void) sm_strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf); 668 if ((fd = mkstemp(tmpbuf)) < 0 || (fp = fdopen(fd, "w+")) == NULL) 669 { 670 if (fd >= 0) 671 (void) close(fd); 672 mailerr("451 4.3.0", "Unable to open temporary file"); 673 return -1; 674 } 675 (void) unlink(tmpbuf); 676 677 if (LMTPMode) 678 { 679 printf("354 Go ahead\r\n"); 680 (void) fflush(stdout); 681 } 682 if (inbody != NULL) 683 *inbody = true; 684 685 (void) time(&tval); 686 (void) fprintf(fp, "From %s %s", from, ctime(&tval)); 687 688 #ifdef CONTENTLENGTH 689 HeaderLength = 0; 690 BodyLength = -1; 691 #endif /* CONTENTLENGTH */ 692 693 line[0] = '\0'; 694 eline = true; 695 while (fgets(line, sizeof(line), stdin) != (char *) NULL) 696 { 697 size_t line_len = 0; 698 int peek; 699 700 prevfl = fullline; /* preserve state of previous line */ 701 while (line[line_len] != '\n' && line_len < sizeof(line) - 2) 702 line_len++; 703 line_len++; 704 705 /* Check for dot-stuffing */ 706 if (prevfl && LMTPMode && line[0] == '.') 707 { 708 if (line[1] == '\n' || 709 (line[1] == '\r' && line[2] == '\n')) 710 goto lmtpdot; 711 memcpy(line, line + 1, line_len); 712 line_len--; 713 } 714 715 /* Check to see if we have the full line from fgets() */ 716 fullline = false; 717 if (line_len > 0) 718 { 719 if (line[line_len - 1] == '\n') 720 { 721 if (line_len >= 2 && 722 line[line_len - 2] == '\r') 723 { 724 line[line_len - 2] = '\n'; 725 line[line_len - 1] = '\0'; 726 line_len--; 727 } 728 fullline = true; 729 } 730 else if (line[line_len - 1] == '\r') 731 { 732 /* Did we just miss the CRLF? */ 733 peek = fgetc(stdin); 734 if (peek == '\n') 735 { 736 line[line_len - 1] = '\n'; 737 fullline = true; 738 } 739 else 740 (void) ungetc(peek, stdin); 741 } 742 } 743 else 744 fullline = true; 745 746 #ifdef CONTENTLENGTH 747 if (prevfl && line[0] == '\n' && HeaderLength == 0) 748 { 749 eline = false; 750 if (fp != NULL) 751 HeaderLength = ftell(fp); 752 if (HeaderLength <= 0) 753 { 754 /* 755 ** shouldn't happen, unless ftell() is 756 ** badly broken 757 */ 758 759 HeaderLength = -1; 760 } 761 } 762 #else /* CONTENTLENGTH */ 763 if (prevfl && line[0] == '\n') 764 eline = true; 765 #endif /* CONTENTLENGTH */ 766 else 767 { 768 if (eline && line[0] == 'F' && 769 fp != NULL && 770 !memcmp(line, "From ", 5)) 771 (void) putc('>', fp); 772 eline = false; 773 #ifdef CONTENTLENGTH 774 /* discard existing "Content-Length:" headers */ 775 if (prevfl && HeaderLength == 0 && 776 (line[0] == 'C' || line[0] == 'c') && 777 sm_strncasecmp(line, ContentHdr, 15) == 0) 778 { 779 /* 780 ** be paranoid: clear the line 781 ** so no "wrong matches" may occur later 782 */ 783 line[0] = '\0'; 784 continue; 785 } 786 #endif /* CONTENTLENGTH */ 787 788 } 789 if (fp != NULL) 790 { 791 (void) fwrite(line, sizeof(char), line_len, fp); 792 if (ferror(fp)) 793 { 794 mailerr("451 4.3.0", 795 "Temporary file write error"); 796 (void) fclose(fp); 797 fp = NULL; 798 continue; 799 } 800 } 801 } 802 803 /* check if an error occurred */ 804 if (fp == NULL) 805 return -1; 806 807 if (LMTPMode) 808 { 809 /* Got a premature EOF -- toss message and exit */ 810 exit(EX_OK); 811 } 812 813 /* If message not newline terminated, need an extra. */ 814 if (fp != NULL && strchr(line, '\n') == NULL) 815 (void) putc('\n', fp); 816 817 lmtpdot: 818 819 #ifdef CONTENTLENGTH 820 if (fp != NULL) 821 BodyLength = ftell(fp); 822 if (HeaderLength == 0 && BodyLength > 0) /* empty body */ 823 { 824 HeaderLength = BodyLength; 825 BodyLength = 0; 826 } 827 else 828 BodyLength = BodyLength - HeaderLength - 1 ; 829 830 if (HeaderLength > 0 && BodyLength >= 0) 831 { 832 (void) sm_snprintf(line, sizeof line, "%lld\n", 833 (LONGLONG_T) BodyLength); 834 (void) sm_strlcpy(&ContentHdr[16], line, 835 sizeof(ContentHdr) - 16); 836 } 837 else 838 BodyLength = -1; /* Something is wrong here */ 839 #endif /* CONTENTLENGTH */ 840 841 /* Output a newline; note, empty messages are allowed. */ 842 if (fp != NULL) 843 (void) putc('\n', fp); 844 845 if (fp == NULL || fflush(fp) == EOF || ferror(fp) != 0) 846 { 847 mailerr("451 4.3.0", "Temporary file write error"); 848 if (fp != NULL) 849 (void) fclose(fp); 850 return -1; 851 } 852 return fd; 853 } 854 855 void 856 deliver(fd, name) 857 int fd; 858 char *name; 859 { 860 struct stat fsb; 861 struct stat sb; 862 char path[MAXPATHLEN]; 863 int mbfd = -1, nr = 0, nw, off; 864 int exitval; 865 char *p; 866 char *errcode; 867 off_t curoff, cursize; 868 #ifdef CONTENTLENGTH 869 off_t headerbytes; 870 int readamount; 871 #endif /* CONTENTLENGTH */ 872 char biffmsg[100], buf[8 * 1024]; 873 SM_MBDB_T user; 874 875 /* 876 ** Disallow delivery to unknown names -- special mailboxes can be 877 ** handled in the sendmail aliases file. 878 */ 879 880 exitval = sm_mbdb_lookup(name, &user); 881 switch (exitval) 882 { 883 case EX_OK: 884 break; 885 886 case EX_NOUSER: 887 exitval = EX_UNAVAILABLE; 888 mailerr("550 5.1.1", "%s: User unknown", name); 889 break; 890 891 case EX_TEMPFAIL: 892 mailerr("451 4.3.0", "%s: User database failure; retry later", 893 name); 894 break; 895 896 default: 897 exitval = EX_UNAVAILABLE; 898 mailerr("550 5.3.0", "%s: User database failure", name); 899 break; 900 } 901 902 if (exitval != EX_OK) 903 { 904 if (ExitVal != EX_TEMPFAIL) 905 ExitVal = exitval; 906 return; 907 } 908 909 endpwent(); 910 911 /* 912 ** Keep name reasonably short to avoid buffer overruns. 913 ** This isn't necessary on BSD because of the proper 914 ** definition of snprintf(), but it can cause problems 915 ** on other systems. 916 ** Also, clear out any bogus characters. 917 */ 918 919 if (strlen(name) > 40) 920 name[40] = '\0'; 921 for (p = name; *p != '\0'; p++) 922 { 923 if (!isascii(*p)) 924 *p &= 0x7f; 925 else if (!isprint(*p)) 926 *p = '.'; 927 } 928 929 930 if (HomeMailFile == NULL) 931 { 932 if (sm_snprintf(path, sizeof(path), "%s/%s", 933 _PATH_MAILDIR, name) >= sizeof(path)) 934 { 935 exitval = EX_UNAVAILABLE; 936 mailerr("550 5.1.1", "%s: Invalid mailbox path", name); 937 return; 938 } 939 } 940 else if (*user.mbdb_homedir == '\0') 941 { 942 exitval = EX_UNAVAILABLE; 943 mailerr("550 5.1.1", "%s: User missing home directory", name); 944 return; 945 } 946 else if (sm_snprintf(path, sizeof(path), "%s/%s", 947 user.mbdb_homedir, HomeMailFile) >= sizeof(path)) 948 { 949 exitval = EX_UNAVAILABLE; 950 mailerr("550 5.1.1", "%s: Invalid mailbox path", name); 951 return; 952 } 953 954 955 /* 956 ** If the mailbox is linked or a symlink, fail. There's an obvious 957 ** race here, that the file was replaced with a symbolic link after 958 ** the lstat returned, but before the open. We attempt to detect 959 ** this by comparing the original stat information and information 960 ** returned by an fstat of the file descriptor returned by the open. 961 ** 962 ** NB: this is a symptom of a larger problem, that the mail spooling 963 ** directory is writeable by the wrong users. If that directory is 964 ** writeable, system security is compromised for other reasons, and 965 ** it cannot be fixed here. 966 ** 967 ** If we created the mailbox, set the owner/group. If that fails, 968 ** just return. Another process may have already opened it, so we 969 ** can't unlink it. Historically, binmail set the owner/group at 970 ** each mail delivery. We no longer do this, assuming that if the 971 ** ownership or permissions were changed there was a reason. 972 ** 973 ** XXX 974 ** open(2) should support flock'ing the file. 975 */ 976 977 tryagain: 978 #ifdef MAILLOCK 979 p = name; 980 #else /* MAILLOCK */ 981 p = path; 982 #endif /* MAILLOCK */ 983 if ((off = lockmbox(p)) != 0) 984 { 985 if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL) 986 { 987 ExitVal = EX_TEMPFAIL; 988 errcode = "451 4.3.0"; 989 } 990 else 991 errcode = "551 5.3.0"; 992 993 mailerr(errcode, "lockmailbox %s failed; error code %d %s", 994 p, off, errno > 0 ? sm_errstring(errno) : ""); 995 return; 996 } 997 998 if (lstat(path, &sb) < 0) 999 { 1000 int save_errno; 1001 int mode = S_IRUSR|S_IWUSR; 1002 gid_t gid = user.mbdb_gid; 1003 1004 #ifdef MAILGID 1005 (void) umask(0007); 1006 gid = MAILGID; 1007 mode |= S_IRGRP|S_IWGRP; 1008 #endif /* MAILGID */ 1009 1010 mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY, 1011 mode); 1012 save_errno = errno; 1013 1014 if (lstat(path, &sb) < 0) 1015 { 1016 ExitVal = EX_CANTCREAT; 1017 mailerr("550 5.2.0", 1018 "%s: lstat: file changed after open", path); 1019 goto err1; 1020 } 1021 if (mbfd < 0) 1022 { 1023 if (save_errno == EEXIST) 1024 goto tryagain; 1025 1026 /* open failed, don't try again */ 1027 mailerr("450 4.2.0", "%s: %s", path, 1028 sm_errstring(save_errno)); 1029 goto err0; 1030 } 1031 else if (fchown(mbfd, user.mbdb_uid, gid) < 0) 1032 { 1033 mailerr("451 4.3.0", "chown %u.%u: %s", 1034 user.mbdb_uid, gid, name); 1035 goto err1; 1036 } 1037 else 1038 { 1039 /* 1040 ** open() was successful, now close it so can 1041 ** be opened as the right owner again. 1042 ** Paranoia: reset mbdf since the file descriptor 1043 ** is no longer valid; better safe than sorry. 1044 */ 1045 1046 sb.st_uid = user.mbdb_uid; 1047 (void) close(mbfd); 1048 mbfd = -1; 1049 } 1050 } 1051 else if (sb.st_nlink != 1) 1052 { 1053 mailerr("550 5.2.0", "%s: too many links", path); 1054 goto err0; 1055 } 1056 else if (!S_ISREG(sb.st_mode)) 1057 { 1058 mailerr("550 5.2.0", "%s: irregular file", path); 1059 goto err0; 1060 } 1061 else if (sb.st_uid != user.mbdb_uid) 1062 { 1063 ExitVal = EX_CANTCREAT; 1064 mailerr("550 5.2.0", "%s: wrong ownership (%d)", 1065 path, (int) sb.st_uid); 1066 goto err0; 1067 } 1068 1069 /* change UID for quota checks */ 1070 if (setreuid(0, user.mbdb_uid) < 0) 1071 { 1072 mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)", 1073 (int) user.mbdb_uid, sm_errstring(errno), 1074 (int) getuid(), (int) geteuid()); 1075 goto err1; 1076 } 1077 #ifdef DEBUG 1078 fprintf(stderr, "new euid = %d\n", (int) geteuid()); 1079 #endif /* DEBUG */ 1080 mbfd = open(path, O_APPEND|O_WRONLY, 0); 1081 if (mbfd < 0) 1082 { 1083 mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 1084 goto err0; 1085 } 1086 else if (fstat(mbfd, &fsb) < 0 || 1087 fsb.st_nlink != 1 || 1088 sb.st_nlink != 1 || 1089 !S_ISREG(fsb.st_mode) || 1090 sb.st_dev != fsb.st_dev || 1091 sb.st_ino != fsb.st_ino || 1092 # if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */ 1093 sb.st_gen != fsb.st_gen || 1094 # endif /* HAS_ST_GEN && 0 */ 1095 sb.st_uid != fsb.st_uid) 1096 { 1097 ExitVal = EX_TEMPFAIL; 1098 mailerr("550 5.2.0", "%s: fstat: file changed after open", 1099 path); 1100 goto err1; 1101 } 1102 1103 #if 0 1104 /* 1105 ** This code could be reused if we decide to add a 1106 ** per-user quota field to the sm_mbdb interface. 1107 */ 1108 1109 /* 1110 ** Fail if the user has a quota specified, and delivery of this 1111 ** message would exceed that quota. We bounce such failures using 1112 ** EX_UNAVAILABLE, unless there were internal problems, since 1113 ** storing immense messages for later retries can cause queueing 1114 ** issues. 1115 */ 1116 1117 if (ui.quota > 0) 1118 { 1119 struct stat dsb; 1120 1121 if (fstat(fd, &dsb) < 0) 1122 { 1123 ExitVal = EX_TEMPFAIL; 1124 mailerr("451 4.3.0", 1125 "%s: fstat: can't stat temporary storage: %s", 1126 ui.mailspool, sm_errstring(errno)); 1127 goto err1; 1128 } 1129 1130 if (dsb.st_size + sb.st_size + 1 > ui.quota) 1131 { 1132 ExitVal = EX_UNAVAILABLE; 1133 mailerr("551 5.2.2", 1134 "%s: Mailbox full or quota exceeded", 1135 ui.mailspool); 1136 goto err1; 1137 } 1138 } 1139 #endif /* 0 */ 1140 1141 /* Wait until we can get a lock on the file. */ 1142 if (flock(mbfd, LOCK_EX) < 0) 1143 { 1144 mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 1145 goto err1; 1146 } 1147 1148 /* Get the starting offset of the new message */ 1149 curoff = lseek(mbfd, (off_t) 0, SEEK_END); 1150 1151 if (!nobiff) 1152 { 1153 (void) sm_snprintf(biffmsg, sizeof(biffmsg), "%s@%lld\n", 1154 name, (LONGLONG_T) curoff); 1155 } 1156 1157 /* Copy the message into the file. */ 1158 if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1) 1159 { 1160 mailerr("450 4.2.0", "Temporary file: %s", 1161 sm_errstring(errno)); 1162 goto err1; 1163 } 1164 #ifdef DEBUG 1165 fprintf(stderr, "before writing: euid = %d\n", (int) geteuid()); 1166 #endif /* DEBUG */ 1167 #ifdef CONTENTLENGTH 1168 headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ; 1169 for (;;) 1170 { 1171 if (headerbytes == 0) 1172 { 1173 (void) sm_snprintf(buf, sizeof buf, "%s", ContentHdr); 1174 nr = strlen(buf); 1175 headerbytes = -1; 1176 readamount = 0; 1177 } 1178 else if (headerbytes > sizeof(buf) || headerbytes < 0) 1179 readamount = sizeof(buf); 1180 else 1181 readamount = headerbytes; 1182 if (readamount != 0) 1183 nr = read(fd, buf, readamount); 1184 if (nr <= 0) 1185 break; 1186 if (headerbytes > 0) 1187 headerbytes -= nr ; 1188 1189 #else /* CONTENTLENGTH */ 1190 while ((nr = read(fd, buf, sizeof(buf))) > 0) 1191 { 1192 #endif /* CONTENTLENGTH */ 1193 for (off = 0; off < nr; off += nw) 1194 { 1195 if ((nw = write(mbfd, buf + off, nr - off)) < 0) 1196 { 1197 errcode = "450 4.2.0"; 1198 #ifdef EDQUOT 1199 if (errno == EDQUOT && BounceQuota) 1200 errcode = "552 5.2.2"; 1201 #endif /* EDQUOT */ 1202 mailerr(errcode, "%s: %s", 1203 path, sm_errstring(errno)); 1204 goto err3; 1205 } 1206 } 1207 } 1208 if (nr < 0) 1209 { 1210 mailerr("450 4.2.0", "Temporary file: %s", 1211 sm_errstring(errno)); 1212 goto err3; 1213 } 1214 1215 /* Flush to disk, don't wait for update. */ 1216 if (!nofsync && fsync(mbfd) < 0) 1217 { 1218 mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 1219 err3: 1220 #ifdef DEBUG 1221 fprintf(stderr, "reset euid = %d\n", (int) geteuid()); 1222 #endif /* DEBUG */ 1223 if (mbfd >= 0) 1224 (void) ftruncate(mbfd, curoff); 1225 err1: if (mbfd >= 0) 1226 (void) close(mbfd); 1227 err0: (void) setreuid(0, 0); 1228 unlockmbox(); 1229 return; 1230 } 1231 1232 /* 1233 ** Save the current size so if the close() fails below 1234 ** we can make sure no other process has changed the mailbox 1235 ** between the failed close and the re-open()/re-lock(). 1236 ** If something else has changed the size, we shouldn't 1237 ** try to truncate it as we may do more harm then good 1238 ** (e.g., truncate a later message delivery). 1239 */ 1240 1241 if (fstat(mbfd, &sb) < 0) 1242 cursize = 0; 1243 else 1244 cursize = sb.st_size; 1245 1246 1247 /* Close and check -- NFS doesn't write until the close. */ 1248 if (close(mbfd)) 1249 { 1250 errcode = "450 4.2.0"; 1251 #ifdef EDQUOT 1252 if (errno == EDQUOT && BounceQuota) 1253 errcode = "552 5.2.2"; 1254 #endif /* EDQUOT */ 1255 mailerr(errcode, "%s: %s", path, sm_errstring(errno)); 1256 mbfd = open(path, O_WRONLY, 0); 1257 if (mbfd < 0 || 1258 cursize == 0 1259 || flock(mbfd, LOCK_EX) < 0 || 1260 fstat(mbfd, &sb) < 0 || 1261 sb.st_size != cursize || 1262 sb.st_nlink != 1 || 1263 !S_ISREG(sb.st_mode) || 1264 sb.st_dev != fsb.st_dev || 1265 sb.st_ino != fsb.st_ino || 1266 # if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */ 1267 sb.st_gen != fsb.st_gen || 1268 # endif /* HAS_ST_GEN && 0 */ 1269 sb.st_uid != fsb.st_uid 1270 ) 1271 { 1272 /* Don't use a bogus file */ 1273 if (mbfd >= 0) 1274 { 1275 (void) close(mbfd); 1276 mbfd = -1; 1277 } 1278 } 1279 1280 /* Attempt to truncate back to pre-write size */ 1281 goto err3; 1282 } 1283 else if (!nobiff) 1284 notifybiff(biffmsg); 1285 1286 if (setreuid(0, 0) < 0) 1287 { 1288 mailerr("450 4.2.0", "setreuid(0, 0): %s", 1289 sm_errstring(errno)); 1290 goto err0; 1291 } 1292 #ifdef DEBUG 1293 fprintf(stderr, "reset euid = %d\n", (int) geteuid()); 1294 #endif /* DEBUG */ 1295 unlockmbox(); 1296 if (LMTPMode) 1297 printf("250 2.1.5 %s Ok\r\n", name); 1298 } 1299 1300 /* 1301 ** user.lock files are necessary for compatibility with other 1302 ** systems, e.g., when the mail spool file is NFS exported. 1303 ** Alas, mailbox locking is more than just a local matter. 1304 ** EPA 11/94. 1305 */ 1306 1307 bool Locked = false; 1308 1309 #ifdef MAILLOCK 1310 int 1311 lockmbox(name) 1312 char *name; 1313 { 1314 int r = 0; 1315 1316 if (Locked) 1317 return 0; 1318 if ((r = maillock(name, 15)) == L_SUCCESS) 1319 { 1320 Locked = true; 1321 return 0; 1322 } 1323 switch (r) 1324 { 1325 case L_TMPLOCK: /* Can't create tmp file */ 1326 case L_TMPWRITE: /* Can't write pid into lockfile */ 1327 case L_MAXTRYS: /* Failed after retrycnt attempts */ 1328 errno = 0; 1329 r = EX_TEMPFAIL; 1330 break; 1331 case L_ERROR: /* Check errno for reason */ 1332 r = errno; 1333 break; 1334 default: /* other permanent errors */ 1335 errno = 0; 1336 r = EX_UNAVAILABLE; 1337 break; 1338 } 1339 return r; 1340 } 1341 1342 void 1343 unlockmbox() 1344 { 1345 if (Locked) 1346 mailunlock(); 1347 Locked = false; 1348 } 1349 #else /* MAILLOCK */ 1350 1351 char LockName[MAXPATHLEN]; 1352 1353 int 1354 lockmbox(path) 1355 char *path; 1356 { 1357 int statfailed = 0; 1358 time_t start; 1359 1360 if (Locked) 1361 return 0; 1362 if (strlen(path) + 6 > sizeof LockName) 1363 return EX_SOFTWARE; 1364 (void) sm_snprintf(LockName, sizeof LockName, "%s.lock", path); 1365 (void) time(&start); 1366 for (; ; sleep(5)) 1367 { 1368 int fd; 1369 struct stat st; 1370 time_t now; 1371 1372 /* global timeout */ 1373 (void) time(&now); 1374 if (now > start + LOCKTO_GLOB) 1375 { 1376 errno = 0; 1377 return EX_TEMPFAIL; 1378 } 1379 fd = open(LockName, O_WRONLY|O_EXCL|O_CREAT, LOCKFILE_PMODE); 1380 if (fd >= 0) 1381 { 1382 /* defeat lock checking programs which test pid */ 1383 (void) write(fd, "0", 2); 1384 Locked = true; 1385 (void) close(fd); 1386 return 0; 1387 } 1388 if (stat(LockName, &st) < 0) 1389 { 1390 if (statfailed++ > 5) 1391 { 1392 errno = 0; 1393 return EX_TEMPFAIL; 1394 } 1395 continue; 1396 } 1397 statfailed = 0; 1398 (void) time(&now); 1399 if (now < st.st_ctime + LOCKTO_RM) 1400 continue; 1401 1402 /* try to remove stale lockfile */ 1403 if (unlink(LockName) < 0) 1404 return errno; 1405 } 1406 } 1407 1408 void 1409 unlockmbox() 1410 { 1411 if (!Locked) 1412 return; 1413 (void) unlink(LockName); 1414 Locked = false; 1415 } 1416 #endif /* MAILLOCK */ 1417 1418 void 1419 notifybiff(msg) 1420 char *msg; 1421 { 1422 static bool initialized = false; 1423 static int f = -1; 1424 struct hostent *hp; 1425 struct servent *sp; 1426 int len; 1427 static struct sockaddr_in addr; 1428 1429 if (!initialized) 1430 { 1431 initialized = true; 1432 1433 /* Be silent if biff service not available. */ 1434 if ((sp = getservbyname("biff", "udp")) == NULL || 1435 (hp = gethostbyname("localhost")) == NULL || 1436 hp->h_length != INADDRSZ) 1437 return; 1438 1439 addr.sin_family = hp->h_addrtype; 1440 memcpy(&addr.sin_addr, hp->h_addr, INADDRSZ); 1441 addr.sin_port = sp->s_port; 1442 } 1443 1444 /* No message, just return */ 1445 if (msg == NULL) 1446 return; 1447 1448 /* Couldn't initialize addr struct */ 1449 if (addr.sin_family == AF_UNSPEC) 1450 return; 1451 1452 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 1453 return; 1454 len = strlen(msg) + 1; 1455 (void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr)); 1456 } 1457 1458 void 1459 usage() 1460 { 1461 ExitVal = EX_USAGE; 1462 mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] user ..."); 1463 exit(ExitVal); 1464 } 1465 1466 void 1467 /*VARARGS2*/ 1468 #ifdef __STDC__ 1469 mailerr(const char *hdr, const char *fmt, ...) 1470 #else /* __STDC__ */ 1471 mailerr(hdr, fmt, va_alist) 1472 const char *hdr; 1473 const char *fmt; 1474 va_dcl 1475 #endif /* __STDC__ */ 1476 { 1477 size_t len = 0; 1478 SM_VA_LOCAL_DECL 1479 1480 (void) e_to_sys(errno); 1481 1482 SM_VA_START(ap, fmt); 1483 1484 if (LMTPMode && hdr != NULL) 1485 { 1486 sm_snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr); 1487 len = strlen(ErrBuf); 1488 } 1489 (void) sm_vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap); 1490 SM_VA_END(ap); 1491 1492 if (!HoldErrs) 1493 flush_error(); 1494 1495 /* Log the message to syslog. */ 1496 if (!LMTPMode) 1497 syslog(LOG_ERR, "%s", ErrBuf); 1498 } 1499 1500 void 1501 flush_error() 1502 { 1503 if (LMTPMode) 1504 printf("%s\r\n", ErrBuf); 1505 else 1506 { 1507 if (ExitVal != EX_USAGE) 1508 (void) fprintf(stderr, "mail.local: "); 1509 fprintf(stderr, "%s\n", ErrBuf); 1510 } 1511 } 1512 1513 /* 1514 * e_to_sys -- 1515 * Guess which errno's are temporary. Gag me. 1516 */ 1517 1518 int 1519 e_to_sys(num) 1520 int num; 1521 { 1522 /* Temporary failures override hard errors. */ 1523 if (ExitVal == EX_TEMPFAIL) 1524 return ExitVal; 1525 1526 switch (num) /* Hopefully temporary errors. */ 1527 { 1528 #ifdef EDQUOT 1529 case EDQUOT: /* Disc quota exceeded */ 1530 if (BounceQuota) 1531 { 1532 ExitVal = EX_UNAVAILABLE; 1533 break; 1534 } 1535 /* FALLTHROUGH */ 1536 #endif /* EDQUOT */ 1537 #ifdef EAGAIN 1538 case EAGAIN: /* Resource temporarily unavailable */ 1539 #endif /* EAGAIN */ 1540 #ifdef EBUSY 1541 case EBUSY: /* Device busy */ 1542 #endif /* EBUSY */ 1543 #ifdef EPROCLIM 1544 case EPROCLIM: /* Too many processes */ 1545 #endif /* EPROCLIM */ 1546 #ifdef EUSERS 1547 case EUSERS: /* Too many users */ 1548 #endif /* EUSERS */ 1549 #ifdef ECONNABORTED 1550 case ECONNABORTED: /* Software caused connection abort */ 1551 #endif /* ECONNABORTED */ 1552 #ifdef ECONNREFUSED 1553 case ECONNREFUSED: /* Connection refused */ 1554 #endif /* ECONNREFUSED */ 1555 #ifdef ECONNRESET 1556 case ECONNRESET: /* Connection reset by peer */ 1557 #endif /* ECONNRESET */ 1558 #ifdef EDEADLK 1559 case EDEADLK: /* Resource deadlock avoided */ 1560 #endif /* EDEADLK */ 1561 #ifdef EFBIG 1562 case EFBIG: /* File too large */ 1563 #endif /* EFBIG */ 1564 #ifdef EHOSTDOWN 1565 case EHOSTDOWN: /* Host is down */ 1566 #endif /* EHOSTDOWN */ 1567 #ifdef EHOSTUNREACH 1568 case EHOSTUNREACH: /* No route to host */ 1569 #endif /* EHOSTUNREACH */ 1570 #ifdef EMFILE 1571 case EMFILE: /* Too many open files */ 1572 #endif /* EMFILE */ 1573 #ifdef ENETDOWN 1574 case ENETDOWN: /* Network is down */ 1575 #endif /* ENETDOWN */ 1576 #ifdef ENETRESET 1577 case ENETRESET: /* Network dropped connection on reset */ 1578 #endif /* ENETRESET */ 1579 #ifdef ENETUNREACH 1580 case ENETUNREACH: /* Network is unreachable */ 1581 #endif /* ENETUNREACH */ 1582 #ifdef ENFILE 1583 case ENFILE: /* Too many open files in system */ 1584 #endif /* ENFILE */ 1585 #ifdef ENOBUFS 1586 case ENOBUFS: /* No buffer space available */ 1587 #endif /* ENOBUFS */ 1588 #ifdef ENOMEM 1589 case ENOMEM: /* Cannot allocate memory */ 1590 #endif /* ENOMEM */ 1591 #ifdef ENOSPC 1592 case ENOSPC: /* No space left on device */ 1593 #endif /* ENOSPC */ 1594 #ifdef EROFS 1595 case EROFS: /* Read-only file system */ 1596 #endif /* EROFS */ 1597 #ifdef ESTALE 1598 case ESTALE: /* Stale NFS file handle */ 1599 #endif /* ESTALE */ 1600 #ifdef ETIMEDOUT 1601 case ETIMEDOUT: /* Connection timed out */ 1602 #endif /* ETIMEDOUT */ 1603 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK 1604 case EWOULDBLOCK: /* Operation would block. */ 1605 #endif /* defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK */ 1606 ExitVal = EX_TEMPFAIL; 1607 break; 1608 1609 default: 1610 ExitVal = EX_UNAVAILABLE; 1611 break; 1612 } 1613 return ExitVal; 1614 } 1615 1616 #if defined(ultrix) || defined(_CRAY) 1617 /* 1618 * Copyright (c) 1987, 1993 1619 * The Regents of the University of California. All rights reserved. 1620 * 1621 * Redistribution and use in source and binary forms, with or without 1622 * modification, are permitted provided that the following conditions 1623 * are met: 1624 * 1. Redistributions of source code must retain the above copyright 1625 * notice, this list of conditions and the following disclaimer. 1626 * 2. Redistributions in binary form must reproduce the above copyright 1627 * notice, this list of conditions and the following disclaimer in the 1628 * documentation and/or other materials provided with the distribution. 1629 * 3. All advertising materials mentioning features or use of this software 1630 * must display the following acknowledgement: 1631 * This product includes software developed by the University of 1632 * California, Berkeley and its contributors. 1633 * 4. Neither the name of the University nor the names of its contributors 1634 * may be used to endorse or promote products derived from this software 1635 * without specific prior written permission. 1636 * 1637 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1638 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1639 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1640 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 1641 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1642 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1643 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 1644 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 1645 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 1646 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1647 * SUCH DAMAGE. 1648 */ 1649 1650 # if defined(LIBC_SCCS) && !defined(lint) 1651 static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; 1652 # endif /* defined(LIBC_SCCS) && !defined(lint) */ 1653 1654 # include <sys/types.h> 1655 # include <sys/stat.h> 1656 # include <fcntl.h> 1657 # include <errno.h> 1658 # include <stdio.h> 1659 # include <ctype.h> 1660 1661 static int _gettemp(); 1662 1663 mkstemp(path) 1664 char *path; 1665 { 1666 int fd; 1667 1668 return (_gettemp(path, &fd) ? fd : -1); 1669 } 1670 1671 static 1672 _gettemp(path, doopen) 1673 char *path; 1674 register int *doopen; 1675 { 1676 extern int errno; 1677 register char *start, *trv; 1678 struct stat sbuf; 1679 unsigned int pid; 1680 1681 pid = getpid(); 1682 for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ 1683 while (*--trv == 'X') 1684 { 1685 *trv = (pid % 10) + '0'; 1686 pid /= 10; 1687 } 1688 1689 /* 1690 * check the target directory; if you have six X's and it 1691 * doesn't exist this runs for a *very* long time. 1692 */ 1693 for (start = trv + 1;; --trv) 1694 { 1695 if (trv <= path) 1696 break; 1697 if (*trv == '/') 1698 { 1699 *trv = '\0'; 1700 if (stat(path, &sbuf) < 0) 1701 return(0); 1702 if (!S_ISDIR(sbuf.st_mode)) 1703 { 1704 errno = ENOTDIR; 1705 return(0); 1706 } 1707 *trv = '/'; 1708 break; 1709 } 1710 } 1711 1712 for (;;) 1713 { 1714 if (doopen) 1715 { 1716 if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR, 1717 0600)) >= 0) 1718 return(1); 1719 if (errno != EEXIST) 1720 return(0); 1721 } 1722 else if (stat(path, &sbuf) < 0) 1723 return(errno == ENOENT ? 1 : 0); 1724 1725 /* tricky little algorithm for backward compatibility */ 1726 for (trv = start;;) 1727 { 1728 if (!*trv) 1729 return(0); 1730 if (*trv == 'z') 1731 *trv++ = 'a'; 1732 else 1733 { 1734 if (isascii(*trv) && isdigit(*trv)) 1735 *trv = 'a'; 1736 else 1737 ++*trv; 1738 break; 1739 } 1740 } 1741 } 1742 /* NOTREACHED */ 1743 } 1744 #endif /* defined(ultrix) || defined(_CRAY) */ 1745