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 * $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.2 2002/09/24 02:09:09 ca 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 || !S_ISREG(sb.st_mode)) 1055 { 1056 mailerr("550 5.2.0", "%s: irregular file", path); 1057 goto err0; 1058 } 1059 else if (sb.st_uid != user.mbdb_uid) 1060 { 1061 ExitVal = EX_CANTCREAT; 1062 mailerr("550 5.2.0", "%s: wrong ownership (%d)", 1063 path, (int) sb.st_uid); 1064 goto err0; 1065 } 1066 1067 /* change UID for quota checks */ 1068 if (setreuid(0, user.mbdb_uid) < 0) 1069 { 1070 mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)", 1071 (int) user.mbdb_uid, sm_errstring(errno), 1072 (int) getuid(), (int) geteuid()); 1073 goto err1; 1074 } 1075 #ifdef DEBUG 1076 fprintf(stderr, "new euid = %d\n", (int) geteuid()); 1077 #endif /* DEBUG */ 1078 mbfd = open(path, O_APPEND|O_WRONLY|EXTRA_MODE, 0); 1079 if (mbfd < 0) 1080 { 1081 mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 1082 goto err0; 1083 } 1084 else if (fstat(mbfd, &fsb) < 0 || 1085 fsb.st_nlink != 1 || 1086 sb.st_nlink != 1 || 1087 !S_ISREG(fsb.st_mode) || 1088 sb.st_dev != fsb.st_dev || 1089 sb.st_ino != fsb.st_ino || 1090 # if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */ 1091 sb.st_gen != fsb.st_gen || 1092 # endif /* HAS_ST_GEN && 0 */ 1093 sb.st_uid != fsb.st_uid) 1094 { 1095 ExitVal = EX_TEMPFAIL; 1096 mailerr("550 5.2.0", "%s: fstat: file changed after open", 1097 path); 1098 goto err1; 1099 } 1100 1101 #if 0 1102 /* 1103 ** This code could be reused if we decide to add a 1104 ** per-user quota field to the sm_mbdb interface. 1105 */ 1106 1107 /* 1108 ** Fail if the user has a quota specified, and delivery of this 1109 ** message would exceed that quota. We bounce such failures using 1110 ** EX_UNAVAILABLE, unless there were internal problems, since 1111 ** storing immense messages for later retries can cause queueing 1112 ** issues. 1113 */ 1114 1115 if (ui.quota > 0) 1116 { 1117 struct stat dsb; 1118 1119 if (fstat(fd, &dsb) < 0) 1120 { 1121 ExitVal = EX_TEMPFAIL; 1122 mailerr("451 4.3.0", 1123 "%s: fstat: can't stat temporary storage: %s", 1124 ui.mailspool, sm_errstring(errno)); 1125 goto err1; 1126 } 1127 1128 if (dsb.st_size + sb.st_size + 1 > ui.quota) 1129 { 1130 ExitVal = EX_UNAVAILABLE; 1131 mailerr("551 5.2.2", 1132 "%s: Mailbox full or quota exceeded", 1133 ui.mailspool); 1134 goto err1; 1135 } 1136 } 1137 #endif /* 0 */ 1138 1139 /* Wait until we can get a lock on the file. */ 1140 if (flock(mbfd, LOCK_EX) < 0) 1141 { 1142 mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 1143 goto err1; 1144 } 1145 1146 /* Get the starting offset of the new message for biff. */ 1147 curoff = lseek(mbfd, (off_t) 0, SEEK_END); 1148 1149 if (!nobiff) 1150 { 1151 (void) sm_snprintf(biffmsg, sizeof(biffmsg), "%s@%lld\n", 1152 name, (LONGLONG_T) curoff); 1153 } 1154 1155 /* Copy the message into the file. */ 1156 if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1) 1157 { 1158 mailerr("450 4.2.0", "Temporary file: %s", 1159 sm_errstring(errno)); 1160 goto err1; 1161 } 1162 #ifdef DEBUG 1163 fprintf(stderr, "before writing: euid = %d\n", (int) geteuid()); 1164 #endif /* DEBUG */ 1165 #ifdef CONTENTLENGTH 1166 headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ; 1167 for (;;) 1168 { 1169 if (headerbytes == 0) 1170 { 1171 (void) sm_snprintf(buf, sizeof buf, "%s", ContentHdr); 1172 nr = strlen(buf); 1173 headerbytes = -1; 1174 readamount = 0; 1175 } 1176 else if (headerbytes > sizeof(buf) || headerbytes < 0) 1177 readamount = sizeof(buf); 1178 else 1179 readamount = headerbytes; 1180 if (readamount != 0) 1181 nr = read(fd, buf, readamount); 1182 if (nr <= 0) 1183 break; 1184 if (headerbytes > 0) 1185 headerbytes -= nr ; 1186 1187 #else /* CONTENTLENGTH */ 1188 while ((nr = read(fd, buf, sizeof(buf))) > 0) 1189 { 1190 #endif /* CONTENTLENGTH */ 1191 for (off = 0; off < nr; off += nw) 1192 { 1193 if ((nw = write(mbfd, buf + off, nr - off)) < 0) 1194 { 1195 errcode = "450 4.2.0"; 1196 #ifdef EDQUOT 1197 if (errno == EDQUOT && BounceQuota) 1198 errcode = "552 5.2.2"; 1199 #endif /* EDQUOT */ 1200 mailerr(errcode, "%s: %s", 1201 path, sm_errstring(errno)); 1202 goto err3; 1203 } 1204 } 1205 } 1206 if (nr < 0) 1207 { 1208 mailerr("450 4.2.0", "Temporary file: %s", 1209 sm_errstring(errno)); 1210 goto err3; 1211 } 1212 1213 /* Flush to disk, don't wait for update. */ 1214 if (!nofsync && fsync(mbfd) < 0) 1215 { 1216 mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 1217 err3: 1218 (void) setreuid(0, 0); 1219 #ifdef DEBUG 1220 fprintf(stderr, "reset euid = %d\n", (int) geteuid()); 1221 #endif /* DEBUG */ 1222 if (mbfd >= 0) 1223 (void) ftruncate(mbfd, curoff); 1224 err1: if (mbfd >= 0) 1225 (void) close(mbfd); 1226 err0: unlockmbox(); 1227 return; 1228 } 1229 1230 /* Close and check -- NFS doesn't write until the close. */ 1231 if (close(mbfd)) 1232 { 1233 errcode = "450 4.2.0"; 1234 #ifdef EDQUOT 1235 if (errno == EDQUOT && BounceQuota) 1236 errcode = "552 5.2.2"; 1237 #endif /* EDQUOT */ 1238 mailerr(errcode, "%s: %s", path, sm_errstring(errno)); 1239 mbfd = open(path, O_WRONLY|EXTRA_MODE, 0); 1240 if (mbfd < 0 1241 || fstat(mbfd, &sb) < 0 || 1242 sb.st_nlink != 1 || 1243 !S_ISREG(sb.st_mode) || 1244 sb.st_dev != fsb.st_dev || 1245 sb.st_ino != fsb.st_ino || 1246 # if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */ 1247 sb.st_gen != fsb.st_gen || 1248 # endif /* HAS_ST_GEN && 0 */ 1249 sb.st_uid != fsb.st_uid 1250 ) 1251 { 1252 /* Don't use a bogus file */ 1253 if (mbfd >= 0) 1254 { 1255 (void) close(mbfd); 1256 mbfd = -1; 1257 } 1258 } 1259 1260 /* Attempt to truncate back to pre-write size */ 1261 goto err3; 1262 } 1263 else if (!nobiff) 1264 notifybiff(biffmsg); 1265 1266 if (setreuid(0, 0) < 0) 1267 { 1268 mailerr("450 4.2.0", "setreuid(0, 0): %s", 1269 sm_errstring(errno)); 1270 goto err0; 1271 } 1272 #ifdef DEBUG 1273 fprintf(stderr, "reset euid = %d\n", (int) geteuid()); 1274 #endif /* DEBUG */ 1275 unlockmbox(); 1276 if (LMTPMode) 1277 printf("250 2.1.5 %s Ok\r\n", name); 1278 } 1279 1280 /* 1281 ** user.lock files are necessary for compatibility with other 1282 ** systems, e.g., when the mail spool file is NFS exported. 1283 ** Alas, mailbox locking is more than just a local matter. 1284 ** EPA 11/94. 1285 */ 1286 1287 bool Locked = false; 1288 1289 #ifdef MAILLOCK 1290 int 1291 lockmbox(name) 1292 char *name; 1293 { 1294 int r = 0; 1295 1296 if (Locked) 1297 return 0; 1298 if ((r = maillock(name, 15)) == L_SUCCESS) 1299 { 1300 Locked = true; 1301 return 0; 1302 } 1303 switch (r) 1304 { 1305 case L_TMPLOCK: /* Can't create tmp file */ 1306 case L_TMPWRITE: /* Can't write pid into lockfile */ 1307 case L_MAXTRYS: /* Failed after retrycnt attempts */ 1308 errno = 0; 1309 r = EX_TEMPFAIL; 1310 break; 1311 case L_ERROR: /* Check errno for reason */ 1312 r = errno; 1313 break; 1314 default: /* other permanent errors */ 1315 errno = 0; 1316 r = EX_UNAVAILABLE; 1317 break; 1318 } 1319 return r; 1320 } 1321 1322 void 1323 unlockmbox() 1324 { 1325 if (Locked) 1326 mailunlock(); 1327 Locked = false; 1328 } 1329 #else /* MAILLOCK */ 1330 1331 char LockName[MAXPATHLEN]; 1332 1333 int 1334 lockmbox(path) 1335 char *path; 1336 { 1337 int statfailed = 0; 1338 time_t start; 1339 1340 if (Locked) 1341 return 0; 1342 if (strlen(path) + 6 > sizeof LockName) 1343 return EX_SOFTWARE; 1344 (void) sm_snprintf(LockName, sizeof LockName, "%s.lock", path); 1345 (void) time(&start); 1346 for (; ; sleep(5)) 1347 { 1348 int fd; 1349 struct stat st; 1350 time_t now; 1351 1352 /* global timeout */ 1353 (void) time(&now); 1354 if (now > start + LOCKTO_GLOB) 1355 { 1356 errno = 0; 1357 return EX_TEMPFAIL; 1358 } 1359 fd = open(LockName, O_WRONLY|O_EXCL|O_CREAT, 0); 1360 if (fd >= 0) 1361 { 1362 /* defeat lock checking programs which test pid */ 1363 (void) write(fd, "0", 2); 1364 Locked = true; 1365 (void) close(fd); 1366 return 0; 1367 } 1368 if (stat(LockName, &st) < 0) 1369 { 1370 if (statfailed++ > 5) 1371 { 1372 errno = 0; 1373 return EX_TEMPFAIL; 1374 } 1375 continue; 1376 } 1377 statfailed = 0; 1378 (void) time(&now); 1379 if (now < st.st_ctime + LOCKTO_RM) 1380 continue; 1381 1382 /* try to remove stale lockfile */ 1383 if (unlink(LockName) < 0) 1384 return errno; 1385 } 1386 } 1387 1388 void 1389 unlockmbox() 1390 { 1391 if (!Locked) 1392 return; 1393 (void) unlink(LockName); 1394 Locked = false; 1395 } 1396 #endif /* MAILLOCK */ 1397 1398 void 1399 notifybiff(msg) 1400 char *msg; 1401 { 1402 static bool initialized = false; 1403 static int f = -1; 1404 struct hostent *hp; 1405 struct servent *sp; 1406 int len; 1407 static struct sockaddr_in addr; 1408 1409 if (!initialized) 1410 { 1411 initialized = true; 1412 1413 /* Be silent if biff service not available. */ 1414 if ((sp = getservbyname("biff", "udp")) == NULL || 1415 (hp = gethostbyname("localhost")) == NULL || 1416 hp->h_length != INADDRSZ) 1417 return; 1418 1419 addr.sin_family = hp->h_addrtype; 1420 memcpy(&addr.sin_addr, hp->h_addr, INADDRSZ); 1421 addr.sin_port = sp->s_port; 1422 } 1423 1424 /* No message, just return */ 1425 if (msg == NULL) 1426 return; 1427 1428 /* Couldn't initialize addr struct */ 1429 if (addr.sin_family == AF_UNSPEC) 1430 return; 1431 1432 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 1433 return; 1434 len = strlen(msg) + 1; 1435 (void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr)); 1436 } 1437 1438 void 1439 usage() 1440 { 1441 ExitVal = EX_USAGE; 1442 mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] user ..."); 1443 exit(ExitVal); 1444 } 1445 1446 void 1447 /*VARARGS2*/ 1448 #ifdef __STDC__ 1449 mailerr(const char *hdr, const char *fmt, ...) 1450 #else /* __STDC__ */ 1451 mailerr(hdr, fmt, va_alist) 1452 const char *hdr; 1453 const char *fmt; 1454 va_dcl 1455 #endif /* __STDC__ */ 1456 { 1457 size_t len = 0; 1458 SM_VA_LOCAL_DECL 1459 1460 (void) e_to_sys(errno); 1461 1462 SM_VA_START(ap, fmt); 1463 1464 if (LMTPMode && hdr != NULL) 1465 { 1466 sm_snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr); 1467 len = strlen(ErrBuf); 1468 } 1469 (void) sm_vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap); 1470 SM_VA_END(ap); 1471 1472 if (!HoldErrs) 1473 flush_error(); 1474 1475 /* Log the message to syslog. */ 1476 if (!LMTPMode) 1477 syslog(LOG_ERR, "%s", ErrBuf); 1478 } 1479 1480 void 1481 flush_error() 1482 { 1483 if (LMTPMode) 1484 printf("%s\r\n", ErrBuf); 1485 else 1486 { 1487 if (ExitVal != EX_USAGE) 1488 (void) fprintf(stderr, "mail.local: "); 1489 fprintf(stderr, "%s\n", ErrBuf); 1490 } 1491 } 1492 1493 /* 1494 * e_to_sys -- 1495 * Guess which errno's are temporary. Gag me. 1496 */ 1497 1498 int 1499 e_to_sys(num) 1500 int num; 1501 { 1502 /* Temporary failures override hard errors. */ 1503 if (ExitVal == EX_TEMPFAIL) 1504 return ExitVal; 1505 1506 switch (num) /* Hopefully temporary errors. */ 1507 { 1508 #ifdef EDQUOT 1509 case EDQUOT: /* Disc quota exceeded */ 1510 if (BounceQuota) 1511 { 1512 ExitVal = EX_UNAVAILABLE; 1513 break; 1514 } 1515 /* FALLTHROUGH */ 1516 #endif /* EDQUOT */ 1517 #ifdef EAGAIN 1518 case EAGAIN: /* Resource temporarily unavailable */ 1519 #endif /* EAGAIN */ 1520 #ifdef EBUSY 1521 case EBUSY: /* Device busy */ 1522 #endif /* EBUSY */ 1523 #ifdef EPROCLIM 1524 case EPROCLIM: /* Too many processes */ 1525 #endif /* EPROCLIM */ 1526 #ifdef EUSERS 1527 case EUSERS: /* Too many users */ 1528 #endif /* EUSERS */ 1529 #ifdef ECONNABORTED 1530 case ECONNABORTED: /* Software caused connection abort */ 1531 #endif /* ECONNABORTED */ 1532 #ifdef ECONNREFUSED 1533 case ECONNREFUSED: /* Connection refused */ 1534 #endif /* ECONNREFUSED */ 1535 #ifdef ECONNRESET 1536 case ECONNRESET: /* Connection reset by peer */ 1537 #endif /* ECONNRESET */ 1538 #ifdef EDEADLK 1539 case EDEADLK: /* Resource deadlock avoided */ 1540 #endif /* EDEADLK */ 1541 #ifdef EFBIG 1542 case EFBIG: /* File too large */ 1543 #endif /* EFBIG */ 1544 #ifdef EHOSTDOWN 1545 case EHOSTDOWN: /* Host is down */ 1546 #endif /* EHOSTDOWN */ 1547 #ifdef EHOSTUNREACH 1548 case EHOSTUNREACH: /* No route to host */ 1549 #endif /* EHOSTUNREACH */ 1550 #ifdef EMFILE 1551 case EMFILE: /* Too many open files */ 1552 #endif /* EMFILE */ 1553 #ifdef ENETDOWN 1554 case ENETDOWN: /* Network is down */ 1555 #endif /* ENETDOWN */ 1556 #ifdef ENETRESET 1557 case ENETRESET: /* Network dropped connection on reset */ 1558 #endif /* ENETRESET */ 1559 #ifdef ENETUNREACH 1560 case ENETUNREACH: /* Network is unreachable */ 1561 #endif /* ENETUNREACH */ 1562 #ifdef ENFILE 1563 case ENFILE: /* Too many open files in system */ 1564 #endif /* ENFILE */ 1565 #ifdef ENOBUFS 1566 case ENOBUFS: /* No buffer space available */ 1567 #endif /* ENOBUFS */ 1568 #ifdef ENOMEM 1569 case ENOMEM: /* Cannot allocate memory */ 1570 #endif /* ENOMEM */ 1571 #ifdef ENOSPC 1572 case ENOSPC: /* No space left on device */ 1573 #endif /* ENOSPC */ 1574 #ifdef EROFS 1575 case EROFS: /* Read-only file system */ 1576 #endif /* EROFS */ 1577 #ifdef ESTALE 1578 case ESTALE: /* Stale NFS file handle */ 1579 #endif /* ESTALE */ 1580 #ifdef ETIMEDOUT 1581 case ETIMEDOUT: /* Connection timed out */ 1582 #endif /* ETIMEDOUT */ 1583 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK 1584 case EWOULDBLOCK: /* Operation would block. */ 1585 #endif /* defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK */ 1586 ExitVal = EX_TEMPFAIL; 1587 break; 1588 1589 default: 1590 ExitVal = EX_UNAVAILABLE; 1591 break; 1592 } 1593 return ExitVal; 1594 } 1595 1596 #if defined(ultrix) || defined(_CRAY) 1597 /* 1598 * Copyright (c) 1987, 1993 1599 * The Regents of the University of California. All rights reserved. 1600 * 1601 * Redistribution and use in source and binary forms, with or without 1602 * modification, are permitted provided that the following conditions 1603 * are met: 1604 * 1. Redistributions of source code must retain the above copyright 1605 * notice, this list of conditions and the following disclaimer. 1606 * 2. Redistributions in binary form must reproduce the above copyright 1607 * notice, this list of conditions and the following disclaimer in the 1608 * documentation and/or other materials provided with the distribution. 1609 * 3. All advertising materials mentioning features or use of this software 1610 * must display the following acknowledgement: 1611 * This product includes software developed by the University of 1612 * California, Berkeley and its contributors. 1613 * 4. Neither the name of the University nor the names of its contributors 1614 * may be used to endorse or promote products derived from this software 1615 * without specific prior written permission. 1616 * 1617 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1618 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1619 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1620 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 1621 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1622 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1623 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 1624 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 1625 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 1626 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1627 * SUCH DAMAGE. 1628 */ 1629 1630 # if defined(LIBC_SCCS) && !defined(lint) 1631 static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; 1632 # endif /* defined(LIBC_SCCS) && !defined(lint) */ 1633 1634 # include <sys/types.h> 1635 # include <sys/stat.h> 1636 # include <fcntl.h> 1637 # include <errno.h> 1638 # include <stdio.h> 1639 # include <ctype.h> 1640 1641 static int _gettemp(); 1642 1643 mkstemp(path) 1644 char *path; 1645 { 1646 int fd; 1647 1648 return (_gettemp(path, &fd) ? fd : -1); 1649 } 1650 1651 static 1652 _gettemp(path, doopen) 1653 char *path; 1654 register int *doopen; 1655 { 1656 extern int errno; 1657 register char *start, *trv; 1658 struct stat sbuf; 1659 unsigned int pid; 1660 1661 pid = getpid(); 1662 for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ 1663 while (*--trv == 'X') 1664 { 1665 *trv = (pid % 10) + '0'; 1666 pid /= 10; 1667 } 1668 1669 /* 1670 * check the target directory; if you have six X's and it 1671 * doesn't exist this runs for a *very* long time. 1672 */ 1673 for (start = trv + 1;; --trv) 1674 { 1675 if (trv <= path) 1676 break; 1677 if (*trv == '/') 1678 { 1679 *trv = '\0'; 1680 if (stat(path, &sbuf) < 0) 1681 return(0); 1682 if (!S_ISDIR(sbuf.st_mode)) 1683 { 1684 errno = ENOTDIR; 1685 return(0); 1686 } 1687 *trv = '/'; 1688 break; 1689 } 1690 } 1691 1692 for (;;) 1693 { 1694 if (doopen) 1695 { 1696 if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR, 1697 0600)) >= 0) 1698 return(1); 1699 if (errno != EEXIST) 1700 return(0); 1701 } 1702 else if (stat(path, &sbuf) < 0) 1703 return(errno == ENOENT ? 1 : 0); 1704 1705 /* tricky little algorithm for backward compatibility */ 1706 for (trv = start;;) 1707 { 1708 if (!*trv) 1709 return(0); 1710 if (*trv == 'z') 1711 *trv++ = 'a'; 1712 else 1713 { 1714 if (isascii(*trv) && isdigit(*trv)) 1715 *trv = 'a'; 1716 else 1717 ++*trv; 1718 break; 1719 } 1720 } 1721 } 1722 /* NOTREACHED */ 1723 } 1724 #endif /* defined(ultrix) || defined(_CRAY) */ 1725