1 /* 2 * Copyright (c) 1998-2001 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.235 2001/12/30 04:59:39 gshapiro 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 /* $FreeBSD$ */ 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 mailerr("451 4.3.0", "Unable to open temporary file"); 674 return -1; 675 } 676 (void) unlink(tmpbuf); 677 678 if (LMTPMode) 679 { 680 printf("354 Go ahead\r\n"); 681 (void) fflush(stdout); 682 } 683 if (inbody != NULL) 684 *inbody = true; 685 686 (void) time(&tval); 687 (void) fprintf(fp, "From %s %s", from, ctime(&tval)); 688 689 #ifdef CONTENTLENGTH 690 HeaderLength = 0; 691 BodyLength = -1; 692 #endif /* CONTENTLENGTH */ 693 694 line[0] = '\0'; 695 eline = true; 696 while (fgets(line, sizeof(line), stdin) != (char *) NULL) 697 { 698 size_t line_len = 0; 699 int peek; 700 701 prevfl = fullline; /* preserve state of previous line */ 702 while (line[line_len] != '\n' && line_len < sizeof(line) - 2) 703 line_len++; 704 line_len++; 705 706 /* Check for dot-stuffing */ 707 if (prevfl && LMTPMode && line[0] == '.') 708 { 709 if (line[1] == '\n' || 710 (line[1] == '\r' && line[2] == '\n')) 711 goto lmtpdot; 712 memcpy(line, line + 1, line_len); 713 line_len--; 714 } 715 716 /* Check to see if we have the full line from fgets() */ 717 fullline = false; 718 if (line_len > 0) 719 { 720 if (line[line_len - 1] == '\n') 721 { 722 if (line_len >= 2 && 723 line[line_len - 2] == '\r') 724 { 725 line[line_len - 2] = '\n'; 726 line[line_len - 1] = '\0'; 727 line_len--; 728 } 729 fullline = true; 730 } 731 else if (line[line_len - 1] == '\r') 732 { 733 /* Did we just miss the CRLF? */ 734 peek = fgetc(stdin); 735 if (peek == '\n') 736 { 737 line[line_len - 1] = '\n'; 738 fullline = true; 739 } 740 else 741 (void) ungetc(peek, stdin); 742 } 743 } 744 else 745 fullline = true; 746 747 #ifdef CONTENTLENGTH 748 if (prevfl && line[0] == '\n' && HeaderLength == 0) 749 { 750 eline = false; 751 if (fp != NULL) 752 HeaderLength = ftell(fp); 753 if (HeaderLength <= 0) 754 { 755 /* 756 ** shouldn't happen, unless ftell() is 757 ** badly broken 758 */ 759 760 HeaderLength = -1; 761 } 762 } 763 #else /* CONTENTLENGTH */ 764 if (prevfl && line[0] == '\n') 765 eline = true; 766 #endif /* CONTENTLENGTH */ 767 else 768 { 769 if (eline && line[0] == 'F' && 770 fp != NULL && 771 !memcmp(line, "From ", 5)) 772 (void) putc('>', fp); 773 eline = false; 774 #ifdef CONTENTLENGTH 775 /* discard existing "Content-Length:" headers */ 776 if (prevfl && HeaderLength == 0 && 777 (line[0] == 'C' || line[0] == 'c') && 778 sm_strncasecmp(line, ContentHdr, 15) == 0) 779 { 780 /* 781 ** be paranoid: clear the line 782 ** so no "wrong matches" may occur later 783 */ 784 line[0] = '\0'; 785 continue; 786 } 787 #endif /* CONTENTLENGTH */ 788 789 } 790 if (fp != NULL) 791 { 792 (void) fwrite(line, sizeof(char), line_len, fp); 793 if (ferror(fp)) 794 { 795 mailerr("451 4.3.0", 796 "Temporary file write error"); 797 (void) fclose(fp); 798 fp = NULL; 799 continue; 800 } 801 } 802 } 803 804 /* check if an error occurred */ 805 if (fp == NULL) 806 return -1; 807 808 if (LMTPMode) 809 { 810 /* Got a premature EOF -- toss message and exit */ 811 exit(EX_OK); 812 } 813 814 /* If message not newline terminated, need an extra. */ 815 if (fp != NULL && strchr(line, '\n') == NULL) 816 (void) putc('\n', fp); 817 818 lmtpdot: 819 820 #ifdef CONTENTLENGTH 821 if (fp != NULL) 822 BodyLength = ftell(fp); 823 if (HeaderLength == 0 && BodyLength > 0) /* empty body */ 824 { 825 HeaderLength = BodyLength; 826 BodyLength = 0; 827 } 828 else 829 BodyLength = BodyLength - HeaderLength - 1 ; 830 831 if (HeaderLength > 0 && BodyLength >= 0) 832 { 833 (void) sm_snprintf(line, sizeof line, "%lld\n", 834 (LONGLONG_T) BodyLength); 835 (void) sm_strlcpy(&ContentHdr[16], line, 836 sizeof(ContentHdr) - 16); 837 } 838 else 839 BodyLength = -1; /* Something is wrong here */ 840 #endif /* CONTENTLENGTH */ 841 842 /* Output a newline; note, empty messages are allowed. */ 843 if (fp != NULL) 844 (void) putc('\n', fp); 845 846 if (fp == NULL || fflush(fp) == EOF || ferror(fp) != 0) 847 { 848 mailerr("451 4.3.0", "Temporary file write error"); 849 if (fp != NULL) 850 (void) fclose(fp); 851 return -1; 852 } 853 return fd; 854 } 855 856 void 857 deliver(fd, name) 858 int fd; 859 char *name; 860 { 861 struct stat fsb; 862 struct stat sb; 863 char path[MAXPATHLEN]; 864 int mbfd = -1, nr = 0, nw, off; 865 int exitval; 866 char *p; 867 char *errcode; 868 off_t curoff; 869 #ifdef CONTENTLENGTH 870 off_t headerbytes; 871 int readamount; 872 #endif /* CONTENTLENGTH */ 873 char biffmsg[100], buf[8*1024]; 874 SM_MBDB_T user; 875 876 /* 877 ** Disallow delivery to unknown names -- special mailboxes can be 878 ** handled in the sendmail aliases file. 879 */ 880 881 exitval = sm_mbdb_lookup(name, &user); 882 switch (exitval) 883 { 884 case EX_OK: 885 break; 886 887 case EX_NOUSER: 888 exitval = EX_UNAVAILABLE; 889 mailerr("550 5.1.1", "%s: User unknown", name); 890 break; 891 892 case EX_TEMPFAIL: 893 mailerr("451 4.3.0", "%s: User database failure; retry later", 894 name); 895 break; 896 897 default: 898 exitval = EX_UNAVAILABLE; 899 mailerr("550 5.3.0", "%s: User database failure", name); 900 break; 901 } 902 903 if (exitval != EX_OK) 904 { 905 if (ExitVal != EX_TEMPFAIL) 906 ExitVal = exitval; 907 return; 908 } 909 910 endpwent(); 911 912 /* 913 ** Keep name reasonably short to avoid buffer overruns. 914 ** This isn't necessary on BSD because of the proper 915 ** definition of snprintf(), but it can cause problems 916 ** on other systems. 917 ** Also, clear out any bogus characters. 918 */ 919 920 if (strlen(name) > 40) 921 name[40] = '\0'; 922 for (p = name; *p != '\0'; p++) 923 { 924 if (!isascii(*p)) 925 *p &= 0x7f; 926 else if (!isprint(*p)) 927 *p = '.'; 928 } 929 930 931 if (HomeMailFile == NULL) 932 { 933 if (sm_snprintf(path, sizeof(path), "%s/%s", 934 _PATH_MAILDIR, name) >= sizeof(path)) 935 { 936 exitval = EX_UNAVAILABLE; 937 mailerr("550 5.1.1", "%s: Invalid mailbox path", name); 938 return; 939 } 940 } 941 else if (*user.mbdb_homedir == '\0') 942 { 943 exitval = EX_UNAVAILABLE; 944 mailerr("550 5.1.1", "%s: User missing home directory", name); 945 return; 946 } 947 else if (sm_snprintf(path, sizeof(path), "%s/%s", 948 user.mbdb_homedir, HomeMailFile) >= sizeof(path)) 949 { 950 exitval = EX_UNAVAILABLE; 951 mailerr("550 5.1.1", "%s: Invalid mailbox path", name); 952 return; 953 } 954 955 956 /* 957 ** If the mailbox is linked or a symlink, fail. There's an obvious 958 ** race here, that the file was replaced with a symbolic link after 959 ** the lstat returned, but before the open. We attempt to detect 960 ** this by comparing the original stat information and information 961 ** returned by an fstat of the file descriptor returned by the open. 962 ** 963 ** NB: this is a symptom of a larger problem, that the mail spooling 964 ** directory is writeable by the wrong users. If that directory is 965 ** writeable, system security is compromised for other reasons, and 966 ** it cannot be fixed here. 967 ** 968 ** If we created the mailbox, set the owner/group. If that fails, 969 ** just return. Another process may have already opened it, so we 970 ** can't unlink it. Historically, binmail set the owner/group at 971 ** each mail delivery. We no longer do this, assuming that if the 972 ** ownership or permissions were changed there was a reason. 973 ** 974 ** XXX 975 ** open(2) should support flock'ing the file. 976 */ 977 978 tryagain: 979 #ifdef MAILLOCK 980 p = name; 981 #else /* MAILLOCK */ 982 p = path; 983 #endif /* MAILLOCK */ 984 if ((off = lockmbox(p)) != 0) 985 { 986 if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL) 987 { 988 ExitVal = EX_TEMPFAIL; 989 errcode = "451 4.3.0"; 990 } 991 else 992 errcode = "551 5.3.0"; 993 994 mailerr(errcode, "lockmailbox %s failed; error code %d %s", 995 p, off, errno > 0 ? sm_errstring(errno) : ""); 996 return; 997 } 998 999 if (lstat(path, &sb) < 0) 1000 { 1001 int save_errno; 1002 int mode = S_IRUSR|S_IWUSR; 1003 gid_t gid = user.mbdb_gid; 1004 1005 #ifdef MAILGID 1006 (void) umask(0007); 1007 gid = MAILGID; 1008 mode |= S_IRGRP|S_IWGRP; 1009 #endif /* MAILGID */ 1010 1011 mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY|EXTRA_MODE, 1012 mode); 1013 save_errno = errno; 1014 1015 if (lstat(path, &sb) < 0) 1016 { 1017 ExitVal = EX_CANTCREAT; 1018 mailerr("550 5.2.0", 1019 "%s: lstat: file changed after open", path); 1020 goto err1; 1021 } 1022 if (mbfd < 0) 1023 { 1024 if (save_errno == EEXIST) 1025 goto tryagain; 1026 1027 /* open failed, don't try again */ 1028 mailerr("450 4.2.0", "%s: %s", path, 1029 sm_errstring(save_errno)); 1030 goto err0; 1031 } 1032 else if (fchown(mbfd, user.mbdb_uid, gid) < 0) 1033 { 1034 mailerr("451 4.3.0", "chown %u.%u: %s", 1035 user.mbdb_uid, gid, name); 1036 goto err1; 1037 } 1038 else 1039 { 1040 /* 1041 ** open() was successful, now close it so can 1042 ** be opened as the right owner again. 1043 ** Paranoia: reset mbdf since the file descriptor 1044 ** is no longer valid; better safe than sorry. 1045 */ 1046 1047 sb.st_uid = user.mbdb_uid; 1048 (void) close(mbfd); 1049 mbfd = -1; 1050 } 1051 } 1052 else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) 1053 { 1054 mailerr("550 5.2.0", "%s: irregular file", path); 1055 goto err0; 1056 } 1057 else if (sb.st_uid != user.mbdb_uid) 1058 { 1059 ExitVal = EX_CANTCREAT; 1060 mailerr("550 5.2.0", "%s: wrong ownership (%d)", 1061 path, (int) sb.st_uid); 1062 goto err0; 1063 } 1064 1065 /* change UID for quota checks */ 1066 if (setreuid(0, user.mbdb_uid) < 0) 1067 { 1068 mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)", 1069 (int) user.mbdb_uid, sm_errstring(errno), 1070 (int) getuid(), (int) geteuid()); 1071 goto err1; 1072 } 1073 #ifdef DEBUG 1074 fprintf(stderr, "new euid = %d\n", (int) geteuid()); 1075 #endif /* DEBUG */ 1076 mbfd = open(path, O_APPEND|O_WRONLY|EXTRA_MODE, 0); 1077 if (mbfd < 0) 1078 { 1079 mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 1080 goto err0; 1081 } 1082 else if (fstat(mbfd, &fsb) < 0 || 1083 fsb.st_nlink != 1 || 1084 sb.st_nlink != 1 || 1085 !S_ISREG(fsb.st_mode) || 1086 sb.st_dev != fsb.st_dev || 1087 sb.st_ino != fsb.st_ino || 1088 # if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */ 1089 sb.st_gen != fsb.st_gen || 1090 # endif /* HAS_ST_GEN && 0 */ 1091 sb.st_uid != fsb.st_uid) 1092 { 1093 ExitVal = EX_TEMPFAIL; 1094 mailerr("550 5.2.0", "%s: fstat: file changed after open", 1095 path); 1096 goto err1; 1097 } 1098 1099 #if 0 1100 /* 1101 ** This code could be reused if we decide to add a 1102 ** per-user quota field to the sm_mbdb interface. 1103 */ 1104 1105 /* 1106 ** Fail if the user has a quota specified, and delivery of this 1107 ** message would exceed that quota. We bounce such failures using 1108 ** EX_UNAVAILABLE, unless there were internal problems, since 1109 ** storing immense messages for later retries can cause queueing 1110 ** issues. 1111 */ 1112 1113 if (ui.quota > 0) 1114 { 1115 struct stat dsb; 1116 1117 if (fstat(fd, &dsb) < 0) 1118 { 1119 ExitVal = EX_TEMPFAIL; 1120 mailerr("451 4.3.0", 1121 "%s: fstat: can't stat temporary storage: %s", 1122 ui.mailspool, sm_errstring(errno)); 1123 goto err1; 1124 } 1125 1126 if (dsb.st_size + sb.st_size + 1 > ui.quota) 1127 { 1128 ExitVal = EX_UNAVAILABLE; 1129 mailerr("551 5.2.2", 1130 "%s: Mailbox full or quota exceeded", 1131 ui.mailspool); 1132 goto err1; 1133 } 1134 } 1135 #endif /* 0 */ 1136 1137 /* Wait until we can get a lock on the file. */ 1138 if (flock(mbfd, LOCK_EX) < 0) 1139 { 1140 mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 1141 goto err1; 1142 } 1143 1144 if (!nobiff) 1145 { 1146 /* Get the starting offset of the new message for biff. */ 1147 curoff = lseek(mbfd, (off_t) 0, SEEK_END); 1148 (void) sm_snprintf(biffmsg, sizeof(biffmsg), "%s@%lld\n", 1149 name, (LONGLONG_T) curoff); 1150 } 1151 1152 /* Copy the message into the file. */ 1153 if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1) 1154 { 1155 mailerr("450 4.2.0", "Temporary file: %s", 1156 sm_errstring(errno)); 1157 goto err1; 1158 } 1159 #ifdef DEBUG 1160 fprintf(stderr, "before writing: euid = %d\n", (int) geteuid()); 1161 #endif /* DEBUG */ 1162 #ifdef CONTENTLENGTH 1163 headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ; 1164 for (;;) 1165 { 1166 if (headerbytes == 0) 1167 { 1168 (void) sm_snprintf(buf, sizeof buf, "%s", ContentHdr); 1169 nr = strlen(buf); 1170 headerbytes = -1; 1171 readamount = 0; 1172 } 1173 else if (headerbytes > sizeof(buf) || headerbytes < 0) 1174 readamount = sizeof(buf); 1175 else 1176 readamount = headerbytes; 1177 if (readamount != 0) 1178 nr = read(fd, buf, readamount); 1179 if (nr <= 0) 1180 break; 1181 if (headerbytes > 0) 1182 headerbytes -= nr ; 1183 1184 #else /* CONTENTLENGTH */ 1185 while ((nr = read(fd, buf, sizeof(buf))) > 0) 1186 { 1187 #endif /* CONTENTLENGTH */ 1188 for (off = 0; off < nr; off += nw) 1189 { 1190 if ((nw = write(mbfd, buf + off, nr - off)) < 0) 1191 { 1192 errcode = "450 4.2.0"; 1193 #ifdef EDQUOT 1194 if (errno == EDQUOT && BounceQuota) 1195 errcode = "552 5.2.2"; 1196 #endif /* EDQUOT */ 1197 mailerr(errcode, "%s: %s", 1198 path, sm_errstring(errno)); 1199 goto err3; 1200 } 1201 } 1202 } 1203 if (nr < 0) 1204 { 1205 mailerr("450 4.2.0", "Temporary file: %s", 1206 sm_errstring(errno)); 1207 goto err3; 1208 } 1209 1210 /* Flush to disk, don't wait for update. */ 1211 if (!nofsync && fsync(mbfd) < 0) 1212 { 1213 mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 1214 err3: 1215 (void) setreuid(0, 0); 1216 #ifdef DEBUG 1217 fprintf(stderr, "reset euid = %d\n", (int) geteuid()); 1218 #endif /* DEBUG */ 1219 (void) ftruncate(mbfd, curoff); 1220 err1: if (mbfd >= 0) 1221 (void) close(mbfd); 1222 err0: unlockmbox(); 1223 return; 1224 } 1225 1226 /* Close and check -- NFS doesn't write until the close. */ 1227 if (close(mbfd)) 1228 { 1229 errcode = "450 4.2.0"; 1230 #ifdef EDQUOT 1231 if (errno == EDQUOT && BounceQuota) 1232 errcode = "552 5.2.2"; 1233 #endif /* EDQUOT */ 1234 mailerr(errcode, "%s: %s", path, sm_errstring(errno)); 1235 (void) truncate(path, curoff); 1236 } 1237 else if (!nobiff) 1238 notifybiff(biffmsg); 1239 1240 if (setreuid(0, 0) < 0) 1241 { 1242 mailerr("450 4.2.0", "setreuid(0, 0): %s", 1243 sm_errstring(errno)); 1244 goto err0; 1245 } 1246 #ifdef DEBUG 1247 fprintf(stderr, "reset euid = %d\n", (int) geteuid()); 1248 #endif /* DEBUG */ 1249 unlockmbox(); 1250 if (LMTPMode) 1251 printf("250 2.1.5 %s Ok\r\n", name); 1252 } 1253 1254 /* 1255 ** user.lock files are necessary for compatibility with other 1256 ** systems, e.g., when the mail spool file is NFS exported. 1257 ** Alas, mailbox locking is more than just a local matter. 1258 ** EPA 11/94. 1259 */ 1260 1261 bool Locked = false; 1262 1263 #ifdef MAILLOCK 1264 int 1265 lockmbox(name) 1266 char *name; 1267 { 1268 int r = 0; 1269 1270 if (Locked) 1271 return 0; 1272 if ((r = maillock(name, 15)) == L_SUCCESS) 1273 { 1274 Locked = true; 1275 return 0; 1276 } 1277 switch (r) 1278 { 1279 case L_TMPLOCK: /* Can't create tmp file */ 1280 case L_TMPWRITE: /* Can't write pid into lockfile */ 1281 case L_MAXTRYS: /* Failed after retrycnt attempts */ 1282 errno = 0; 1283 r = EX_TEMPFAIL; 1284 break; 1285 case L_ERROR: /* Check errno for reason */ 1286 r = errno; 1287 break; 1288 default: /* other permanent errors */ 1289 errno = 0; 1290 r = EX_UNAVAILABLE; 1291 break; 1292 } 1293 return r; 1294 } 1295 1296 void 1297 unlockmbox() 1298 { 1299 if (Locked) 1300 mailunlock(); 1301 Locked = false; 1302 } 1303 #else /* MAILLOCK */ 1304 1305 char LockName[MAXPATHLEN]; 1306 1307 int 1308 lockmbox(path) 1309 char *path; 1310 { 1311 int statfailed = 0; 1312 time_t start; 1313 1314 if (Locked) 1315 return 0; 1316 if (strlen(path) + 6 > sizeof LockName) 1317 return EX_SOFTWARE; 1318 (void) sm_snprintf(LockName, sizeof LockName, "%s.lock", path); 1319 (void) time(&start); 1320 for (; ; sleep(5)) 1321 { 1322 int fd; 1323 struct stat st; 1324 time_t now; 1325 1326 /* global timeout */ 1327 (void) time(&now); 1328 if (now > start + LOCKTO_GLOB) 1329 { 1330 errno = 0; 1331 return EX_TEMPFAIL; 1332 } 1333 fd = open(LockName, O_WRONLY|O_EXCL|O_CREAT, 0); 1334 if (fd >= 0) 1335 { 1336 /* defeat lock checking programs which test pid */ 1337 (void) write(fd, "0", 2); 1338 Locked = true; 1339 (void) close(fd); 1340 return 0; 1341 } 1342 if (stat(LockName, &st) < 0) 1343 { 1344 if (statfailed++ > 5) 1345 { 1346 errno = 0; 1347 return EX_TEMPFAIL; 1348 } 1349 continue; 1350 } 1351 statfailed = 0; 1352 (void) time(&now); 1353 if (now < st.st_ctime + LOCKTO_RM) 1354 continue; 1355 1356 /* try to remove stale lockfile */ 1357 if (unlink(LockName) < 0) 1358 return errno; 1359 } 1360 } 1361 1362 void 1363 unlockmbox() 1364 { 1365 if (!Locked) 1366 return; 1367 (void) unlink(LockName); 1368 Locked = false; 1369 } 1370 #endif /* MAILLOCK */ 1371 1372 void 1373 notifybiff(msg) 1374 char *msg; 1375 { 1376 static bool initialized = false; 1377 static int f = -1; 1378 struct hostent *hp; 1379 struct servent *sp; 1380 int len; 1381 static struct sockaddr_in addr; 1382 1383 if (!initialized) 1384 { 1385 initialized = true; 1386 1387 /* Be silent if biff service not available. */ 1388 if ((sp = getservbyname("biff", "udp")) == NULL || 1389 (hp = gethostbyname("localhost")) == NULL || 1390 hp->h_length != INADDRSZ) 1391 return; 1392 1393 addr.sin_family = hp->h_addrtype; 1394 memcpy(&addr.sin_addr, hp->h_addr, INADDRSZ); 1395 addr.sin_port = sp->s_port; 1396 } 1397 1398 /* No message, just return */ 1399 if (msg == NULL) 1400 return; 1401 1402 /* Couldn't initialize addr struct */ 1403 if (addr.sin_family == AF_UNSPEC) 1404 return; 1405 1406 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 1407 return; 1408 len = strlen(msg) + 1; 1409 (void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr)); 1410 } 1411 1412 void 1413 usage() 1414 { 1415 ExitVal = EX_USAGE; 1416 mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] user ..."); 1417 exit(ExitVal); 1418 } 1419 1420 void 1421 /*VARARGS2*/ 1422 #ifdef __STDC__ 1423 mailerr(const char *hdr, const char *fmt, ...) 1424 #else /* __STDC__ */ 1425 mailerr(hdr, fmt, va_alist) 1426 const char *hdr; 1427 const char *fmt; 1428 va_dcl 1429 #endif /* __STDC__ */ 1430 { 1431 size_t len = 0; 1432 SM_VA_LOCAL_DECL 1433 1434 (void) e_to_sys(errno); 1435 1436 SM_VA_START(ap, fmt); 1437 1438 if (LMTPMode && hdr != NULL) 1439 { 1440 sm_snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr); 1441 len = strlen(ErrBuf); 1442 } 1443 (void) sm_vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap); 1444 SM_VA_END(ap); 1445 1446 if (!HoldErrs) 1447 flush_error(); 1448 1449 /* Log the message to syslog. */ 1450 if (!LMTPMode) 1451 syslog(LOG_ERR, "%s", ErrBuf); 1452 } 1453 1454 void 1455 flush_error() 1456 { 1457 if (LMTPMode) 1458 printf("%s\r\n", ErrBuf); 1459 else 1460 { 1461 if (ExitVal != EX_USAGE) 1462 (void) fprintf(stderr, "mail.local: "); 1463 fprintf(stderr, "%s\n", ErrBuf); 1464 } 1465 } 1466 1467 /* 1468 * e_to_sys -- 1469 * Guess which errno's are temporary. Gag me. 1470 */ 1471 1472 int 1473 e_to_sys(num) 1474 int num; 1475 { 1476 /* Temporary failures override hard errors. */ 1477 if (ExitVal == EX_TEMPFAIL) 1478 return ExitVal; 1479 1480 switch (num) /* Hopefully temporary errors. */ 1481 { 1482 #ifdef EDQUOT 1483 case EDQUOT: /* Disc quota exceeded */ 1484 if (BounceQuota) 1485 { 1486 ExitVal = EX_UNAVAILABLE; 1487 break; 1488 } 1489 /* FALLTHROUGH */ 1490 #endif /* EDQUOT */ 1491 #ifdef EAGAIN 1492 case EAGAIN: /* Resource temporarily unavailable */ 1493 #endif /* EAGAIN */ 1494 #ifdef EBUSY 1495 case EBUSY: /* Device busy */ 1496 #endif /* EBUSY */ 1497 #ifdef EPROCLIM 1498 case EPROCLIM: /* Too many processes */ 1499 #endif /* EPROCLIM */ 1500 #ifdef EUSERS 1501 case EUSERS: /* Too many users */ 1502 #endif /* EUSERS */ 1503 #ifdef ECONNABORTED 1504 case ECONNABORTED: /* Software caused connection abort */ 1505 #endif /* ECONNABORTED */ 1506 #ifdef ECONNREFUSED 1507 case ECONNREFUSED: /* Connection refused */ 1508 #endif /* ECONNREFUSED */ 1509 #ifdef ECONNRESET 1510 case ECONNRESET: /* Connection reset by peer */ 1511 #endif /* ECONNRESET */ 1512 #ifdef EDEADLK 1513 case EDEADLK: /* Resource deadlock avoided */ 1514 #endif /* EDEADLK */ 1515 #ifdef EFBIG 1516 case EFBIG: /* File too large */ 1517 #endif /* EFBIG */ 1518 #ifdef EHOSTDOWN 1519 case EHOSTDOWN: /* Host is down */ 1520 #endif /* EHOSTDOWN */ 1521 #ifdef EHOSTUNREACH 1522 case EHOSTUNREACH: /* No route to host */ 1523 #endif /* EHOSTUNREACH */ 1524 #ifdef EMFILE 1525 case EMFILE: /* Too many open files */ 1526 #endif /* EMFILE */ 1527 #ifdef ENETDOWN 1528 case ENETDOWN: /* Network is down */ 1529 #endif /* ENETDOWN */ 1530 #ifdef ENETRESET 1531 case ENETRESET: /* Network dropped connection on reset */ 1532 #endif /* ENETRESET */ 1533 #ifdef ENETUNREACH 1534 case ENETUNREACH: /* Network is unreachable */ 1535 #endif /* ENETUNREACH */ 1536 #ifdef ENFILE 1537 case ENFILE: /* Too many open files in system */ 1538 #endif /* ENFILE */ 1539 #ifdef ENOBUFS 1540 case ENOBUFS: /* No buffer space available */ 1541 #endif /* ENOBUFS */ 1542 #ifdef ENOMEM 1543 case ENOMEM: /* Cannot allocate memory */ 1544 #endif /* ENOMEM */ 1545 #ifdef ENOSPC 1546 case ENOSPC: /* No space left on device */ 1547 #endif /* ENOSPC */ 1548 #ifdef EROFS 1549 case EROFS: /* Read-only file system */ 1550 #endif /* EROFS */ 1551 #ifdef ESTALE 1552 case ESTALE: /* Stale NFS file handle */ 1553 #endif /* ESTALE */ 1554 #ifdef ETIMEDOUT 1555 case ETIMEDOUT: /* Connection timed out */ 1556 #endif /* ETIMEDOUT */ 1557 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK 1558 case EWOULDBLOCK: /* Operation would block. */ 1559 #endif /* defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK */ 1560 ExitVal = EX_TEMPFAIL; 1561 break; 1562 1563 default: 1564 ExitVal = EX_UNAVAILABLE; 1565 break; 1566 } 1567 return ExitVal; 1568 } 1569 1570 #if defined(ultrix) || defined(_CRAY) 1571 /* 1572 * Copyright (c) 1987, 1993 1573 * The Regents of the University of California. All rights reserved. 1574 * 1575 * Redistribution and use in source and binary forms, with or without 1576 * modification, are permitted provided that the following conditions 1577 * are met: 1578 * 1. Redistributions of source code must retain the above copyright 1579 * notice, this list of conditions and the following disclaimer. 1580 * 2. Redistributions in binary form must reproduce the above copyright 1581 * notice, this list of conditions and the following disclaimer in the 1582 * documentation and/or other materials provided with the distribution. 1583 * 3. All advertising materials mentioning features or use of this software 1584 * must display the following acknowledgement: 1585 * This product includes software developed by the University of 1586 * California, Berkeley and its contributors. 1587 * 4. Neither the name of the University nor the names of its contributors 1588 * may be used to endorse or promote products derived from this software 1589 * without specific prior written permission. 1590 * 1591 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1592 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1593 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1594 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 1595 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1596 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1597 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 1598 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 1599 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 1600 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1601 * SUCH DAMAGE. 1602 */ 1603 1604 # if defined(LIBC_SCCS) && !defined(lint) 1605 static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; 1606 # endif /* defined(LIBC_SCCS) && !defined(lint) */ 1607 1608 # include <sys/types.h> 1609 # include <sys/stat.h> 1610 # include <fcntl.h> 1611 # include <errno.h> 1612 # include <stdio.h> 1613 # include <ctype.h> 1614 1615 static int _gettemp(); 1616 1617 mkstemp(path) 1618 char *path; 1619 { 1620 int fd; 1621 1622 return (_gettemp(path, &fd) ? fd : -1); 1623 } 1624 1625 static 1626 _gettemp(path, doopen) 1627 char *path; 1628 register int *doopen; 1629 { 1630 extern int errno; 1631 register char *start, *trv; 1632 struct stat sbuf; 1633 unsigned int pid; 1634 1635 pid = getpid(); 1636 for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ 1637 while (*--trv == 'X') 1638 { 1639 *trv = (pid % 10) + '0'; 1640 pid /= 10; 1641 } 1642 1643 /* 1644 * check the target directory; if you have six X's and it 1645 * doesn't exist this runs for a *very* long time. 1646 */ 1647 for (start = trv + 1;; --trv) 1648 { 1649 if (trv <= path) 1650 break; 1651 if (*trv == '/') 1652 { 1653 *trv = '\0'; 1654 if (stat(path, &sbuf) < 0) 1655 return(0); 1656 if (!S_ISDIR(sbuf.st_mode)) 1657 { 1658 errno = ENOTDIR; 1659 return(0); 1660 } 1661 *trv = '/'; 1662 break; 1663 } 1664 } 1665 1666 for (;;) 1667 { 1668 if (doopen) 1669 { 1670 if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR, 1671 0600)) >= 0) 1672 return(1); 1673 if (errno != EEXIST) 1674 return(0); 1675 } 1676 else if (stat(path, &sbuf) < 0) 1677 return(errno == ENOENT ? 1 : 0); 1678 1679 /* tricky little algorithm for backward compatibility */ 1680 for (trv = start;;) 1681 { 1682 if (!*trv) 1683 return(0); 1684 if (*trv == 'z') 1685 *trv++ = 'a'; 1686 else 1687 { 1688 if (isascii(*trv) && isdigit(*trv)) 1689 *trv = 'a'; 1690 else 1691 ++*trv; 1692 break; 1693 } 1694 } 1695 } 1696 /* NOTREACHED */ 1697 } 1698 #endif /* defined(ultrix) || defined(_CRAY) */ 1699