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