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