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