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 #ifndef lint 14 static char copyright[] = 15 "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\ 16 All rights reserved.\n\ 17 Copyright (c) 1990, 1993, 1994\n\ 18 The Regents of the University of California. All rights reserved.\n"; 19 #endif /* ! lint */ 20 21 #ifndef lint 22 static char id[] = "@(#)$Id: mail.local.c,v 8.143.4.58 2001/06/01 05:33:31 gshapiro Exp $"; 23 #endif /* ! lint */ 24 25 /* 26 ** This is not intended to work on System V derived systems 27 ** such as Solaris or HP-UX, since they use a totally different 28 ** approach to mailboxes (essentially, they have a setgid program 29 ** rather than setuid, and they rely on the ability to "give away" 30 ** files to do their work). IT IS NOT A BUG that this doesn't 31 ** work on such architectures. 32 */ 33 34 35 /* additional mode for open() */ 36 # define EXTRA_MODE 0 37 38 # include <sys/types.h> 39 # include <sys/param.h> 40 # include <sys/stat.h> 41 # include <sys/socket.h> 42 # include <sys/file.h> 43 44 # include <netinet/in.h> 45 # include <arpa/nameser.h> 46 47 # include <fcntl.h> 48 # include <netdb.h> 49 # include <pwd.h> 50 # include <stdio.h> 51 # include <stdlib.h> 52 # include <string.h> 53 # include <syslog.h> 54 # include <time.h> 55 # include <unistd.h> 56 # ifdef EX_OK 57 # undef EX_OK /* unistd.h may have another use for this */ 58 # endif /* EX_OK */ 59 # include <sysexits.h> 60 # include <ctype.h> 61 62 # ifndef __P 63 # include "sendmail/cdefs.h" 64 # endif /* ! __P */ 65 # include "sendmail/useful.h" 66 67 extern size_t strlcpy __P((char *, const char *, size_t)); 68 extern size_t strlcat __P((char *, const char *, size_t)); 69 70 # if defined(BSD4_4) || defined(__osf__) || defined(__GNU_LIBRARY__) || defined(IRIX64) || defined(IRIX5) || defined(IRIX6) 71 # ifndef HASSTRERROR 72 # define HASSTRERROR 1 73 # endif /* ! HASSTRERROR */ 74 # endif /* defined(BSD4_4) || defined(__osf__) || defined(__GNU_LIBRARY__) || defined(IRIX64) || defined(IRIX5) || defined(IRIX6) */ 75 76 # include "sendmail/errstring.h" 77 78 # ifndef LOCKTO_RM 79 # define LOCKTO_RM 300 /* timeout for stale lockfile removal */ 80 # endif /* ! LOCKTO_RM */ 81 # ifndef LOCKTO_GLOB 82 # define LOCKTO_GLOB 400 /* global timeout for lockfile creation */ 83 # endif /* ! LOCKTO_GLOB */ 84 85 # ifdef __STDC__ 86 # include <stdarg.h> 87 # define REALLOC(ptr, size) realloc(ptr, size) 88 # else /* __STDC__ */ 89 # include <varargs.h> 90 /* define a realloc() which works for NULL pointers */ 91 # define REALLOC(ptr, size) (((ptr) == NULL) ? malloc(size) : realloc(ptr, size)) 92 # endif /* __STDC__ */ 93 94 # if (defined(sun) && defined(__svr4__)) || defined(__SVR4) 95 # define USE_LOCKF 1 96 # define USE_SETEUID 1 97 # define _PATH_MAILDIR "/var/mail" 98 # endif /* (defined(sun) && defined(__svr4__)) || defined(__SVR4) */ 99 100 # ifdef NCR_MP_RAS3 101 # define USE_LOCKF 1 102 # define HASSNPRINTF 1 103 # define _PATH_MAILDIR "/var/mail" 104 # endif /* NCR_MP_RAS3 */ 105 106 # if defined(_AIX) 107 # define USE_LOCKF 1 108 # define USE_SETEUID 1 109 # endif /* defined(_AIX) */ 110 111 # if defined(__hpux) 112 # define USE_LOCKF 1 113 # define USE_SETRESUID 1 114 # endif /* defined(__hpux) */ 115 116 # ifdef DGUX 117 # define HASSNPRINTF 1 118 # define USE_LOCKF 1 119 # endif /* DGUX */ 120 121 # if defined(_CRAY) 122 # if !defined(MAXPATHLEN) 123 # define MAXPATHLEN PATHSIZE 124 # endif /* !defined(MAXPATHLEN) */ 125 # define _PATH_MAILDIR "/usr/spool/mail" 126 # endif /* defined(_CRAY) */ 127 128 # if defined(NeXT) && !defined(__APPLE__) 129 # include <libc.h> 130 # define _PATH_MAILDIR "/usr/spool/mail" 131 # define S_IRUSR S_IREAD 132 # define S_IWUSR S_IWRITE 133 # endif /* defined(NeXT) && !defined(__APPLE__) */ 134 135 # if defined(IRIX64) || defined(IRIX5) || defined(IRIX6) 136 # include <paths.h> 137 # endif /* defined(IRIX64) || defined(IRIX5) || defined(IRIX6) */ 138 139 /* 140 * If you don't have flock, you could try using lockf instead. 141 */ 142 143 # ifdef USE_LOCKF 144 # define flock(a, b) lockf(a, b, 0) 145 # ifdef LOCK_EX 146 # undef LOCK_EX 147 # endif /* LOCK_EX */ 148 # define LOCK_EX F_LOCK 149 # endif /* USE_LOCKF */ 150 151 # ifndef LOCK_EX 152 # include <sys/file.h> 153 # endif /* ! LOCK_EX */ 154 155 # if defined(BSD4_4) || defined(__GLIBC__) 156 # include <paths.h> 157 # define _PATH_LOCTMP "/tmp/local.XXXXXX" 158 # endif /* defined(BSD4_4) || defined(__GLIBC__) */ 159 160 # ifdef BSD4_4 161 # define HAS_ST_GEN 1 162 # else /* BSD4_4 */ 163 # ifndef _BSD_VA_LIST_ 164 # define _BSD_VA_LIST_ va_list 165 # endif /* ! _BSD_VA_LIST_ */ 166 # endif /* BSD4_4 */ 167 168 # if defined(BSD4_4) || defined(linux) 169 # define HASSNPRINTF 1 170 # else /* defined(BSD4_4) || defined(linux) */ 171 # ifndef ultrix 172 extern FILE *fdopen __P((int, const char *)); 173 # endif /* ! ultrix */ 174 # endif /* defined(BSD4_4) || defined(linux) */ 175 176 # if SOLARIS >= 20300 || (SOLARIS < 10000 && SOLARIS >= 203) 177 # define CONTENTLENGTH 1 /* Needs the Content-Length header */ 178 # endif /* SOLARIS >= 20300 || (SOLARIS < 10000 && SOLARIS >= 203) */ 179 180 # if SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) 181 # define HASSNPRINTF 1 /* has snprintf starting in 2.6 */ 182 # endif /* SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) */ 183 184 # ifdef HPUX11 185 # define HASSNPRINTF 1 /* has snprintf starting in 11.X */ 186 # endif /* HPUX11 */ 187 188 # if _AIX4 >= 40300 189 # define HASSNPRINTF 1 /* has snprintf starting in 4.3 */ 190 # endif /* _AIX4 >= 40300 */ 191 192 # if !HASSNPRINTF && !SFIO 193 extern int snprintf __P((char *, size_t, const char *, ...)); 194 # ifndef _CRAY 195 extern int vsnprintf __P((char *, size_t, const char *, ...)); 196 # endif /* ! _CRAY */ 197 # endif /* !HASSNPRINTF && !SFIO */ 198 199 /* 200 ** If you don't have setreuid, and you have saved uids, and you have 201 ** a seteuid() call that doesn't try to emulate using setuid(), then 202 ** you can try defining USE_SETEUID. 203 */ 204 205 # ifdef USE_SETEUID 206 # define setreuid(r, e) seteuid(e) 207 # endif /* USE_SETEUID */ 208 209 /* 210 ** And of course on hpux you have setresuid() 211 */ 212 213 # ifdef USE_SETRESUID 214 # define setreuid(r, e) setresuid(-1, e, -1) 215 # endif /* USE_SETRESUID */ 216 217 # ifndef _PATH_LOCTMP 218 # define _PATH_LOCTMP "/tmp/local.XXXXXX" 219 # endif /* ! _PATH_LOCTMP */ 220 # ifndef _PATH_MAILDIR 221 # define _PATH_MAILDIR "/var/spool/mail" 222 # endif /* ! _PATH_MAILDIR */ 223 224 # ifndef S_ISREG 225 # define S_ISREG(mode) (((mode) & _S_IFMT) == S_IFREG) 226 # endif /* ! S_ISREG */ 227 228 # ifdef MAILLOCK 229 # include <maillock.h> 230 # endif /* MAILLOCK */ 231 232 # define U_UID pw->pw_uid 233 # define U_GID pw->pw_gid 234 235 #ifndef INADDRSZ 236 # define INADDRSZ 4 /* size of an IPv4 address in bytes */ 237 #endif /* ! INADDRSZ */ 238 239 #ifndef MAILER_DAEMON 240 # define MAILER_DAEMON "MAILER-DAEMON" 241 #endif /* ! MAILER_DAEMON */ 242 243 #ifdef CONTENTLENGTH 244 char ContentHdr[40] = "Content-Length: "; 245 off_t HeaderLength; 246 off_t BodyLength; 247 #endif /* CONTENTLENGTH */ 248 249 bool EightBitMime = TRUE; /* advertise 8BITMIME in LMTP */ 250 char ErrBuf[10240]; /* error buffer */ 251 int ExitVal = EX_OK; /* sysexits.h error value. */ 252 bool HoldErrs = FALSE; /* Hold errors in ErrBuf */ 253 bool LMTPMode = FALSE; 254 bool BounceQuota = FALSE; /* permanent error when over quota */ 255 256 void deliver __P((int, char *)); 257 int e_to_sys __P((int)); 258 void notifybiff __P((char *)); 259 int store __P((char *, int, bool *)); 260 void usage __P((void)); 261 int lockmbox __P((char *)); 262 void unlockmbox __P((void)); 263 void mailerr __P((const char *, const char *, ...)); 264 void flush_error __P((void)); 265 266 267 int 268 main(argc, argv) 269 int argc; 270 char *argv[]; 271 { 272 struct passwd *pw; 273 int ch, fd; 274 uid_t uid; 275 char *from; 276 extern char *optarg; 277 extern int optind; 278 279 280 /* make sure we have some open file descriptors */ 281 for (fd = 10; fd < 30; fd++) 282 (void) close(fd); 283 284 /* use a reasonable umask */ 285 (void) umask(0077); 286 287 # ifdef LOG_MAIL 288 openlog("mail.local", 0, LOG_MAIL); 289 # else /* LOG_MAIL */ 290 openlog("mail.local", 0); 291 # endif /* LOG_MAIL */ 292 293 from = NULL; 294 while ((ch = getopt(argc, argv, "7bdf:r:l")) != -1) 295 { 296 switch(ch) 297 { 298 case '7': /* Do not advertise 8BITMIME */ 299 EightBitMime = FALSE; 300 break; 301 302 case 'b': /* bounce mail when over quota. */ 303 BounceQuota = TRUE; 304 break; 305 306 case 'd': /* Backward compatible. */ 307 break; 308 309 case 'f': 310 case 'r': /* Backward compatible. */ 311 if (from != NULL) 312 { 313 mailerr(NULL, "Multiple -f options"); 314 usage(); 315 } 316 from = optarg; 317 break; 318 319 case 'l': 320 LMTPMode = TRUE; 321 break; 322 323 case '?': 324 default: 325 usage(); 326 } 327 } 328 argc -= optind; 329 argv += optind; 330 331 /* initialize biff structures */ 332 notifybiff(NULL); 333 334 if (LMTPMode) 335 { 336 extern void dolmtp __P((void)); 337 338 if (argc > 0) 339 { 340 mailerr("421", "Users should not be specified in command line if LMTP required"); 341 exit(EX_TEMPFAIL); 342 } 343 344 dolmtp(); 345 /* NOTREACHED */ 346 exit(EX_OK); 347 } 348 349 /* Non-LMTP from here on out */ 350 if (*argv == '\0') 351 usage(); 352 353 /* 354 ** If from not specified, use the name from getlogin() if the 355 ** uid matches, otherwise, use the name from the password file 356 ** corresponding to the uid. 357 */ 358 359 uid = getuid(); 360 361 if (from == NULL && ((from = getlogin()) == NULL || 362 (pw = getpwnam(from)) == NULL || 363 pw->pw_uid != uid)) 364 from = (pw = getpwuid(uid)) != NULL ? pw->pw_name : "???"; 365 366 /* 367 ** There is no way to distinguish the error status of one delivery 368 ** from the rest of the deliveries. So, if we failed hard on one 369 ** or more deliveries, but had no failures on any of the others, we 370 ** return a hard failure. If we failed temporarily on one or more 371 ** deliveries, we return a temporary failure regardless of the other 372 ** failures. This results in the delivery being reattempted later 373 ** at the expense of repeated failures and multiple deliveries. 374 */ 375 376 HoldErrs = TRUE; 377 fd = store(from, 0, NULL); 378 HoldErrs = FALSE; 379 if (fd < 0) 380 { 381 flush_error(); 382 exit(ExitVal); 383 } 384 for (; *argv != NULL; ++argv) 385 deliver(fd, *argv); 386 exit(ExitVal); 387 /* NOTREACHED */ 388 return ExitVal; 389 } 390 391 char * 392 parseaddr(s, rcpt) 393 char *s; 394 bool rcpt; 395 { 396 char *p; 397 int l; 398 399 if (*s++ != '<') 400 return NULL; 401 402 p = s; 403 404 /* at-domain-list */ 405 while (*p == '@') 406 { 407 p++; 408 while (*p != ',' && *p != ':' && *p != '\0') 409 p++; 410 if (*p == '\0') 411 return NULL; 412 413 /* Skip over , or : */ 414 p++; 415 } 416 417 s = p; 418 419 /* local-part */ 420 while (*p != '\0' && *p != '@' && *p != '>') 421 { 422 if (*p == '\\') 423 { 424 if (*++p == '\0') 425 return NULL; 426 } 427 else if (*p == '\"') 428 { 429 p++; 430 while (*p != '\0' && *p != '\"') 431 { 432 if (*p == '\\') 433 { 434 if (*++p == '\0') 435 return NULL; 436 } 437 p++; 438 } 439 if (*p == '\0' || *(p + 1) == '\0') 440 return NULL; 441 } 442 /* +detail ? */ 443 if (*p == '+' && rcpt) 444 *p = '\0'; 445 p++; 446 } 447 448 /* @domain */ 449 if (*p == '@') 450 { 451 if (rcpt) 452 *p++ = '\0'; 453 while (*p != '\0' && *p != '>') 454 p++; 455 } 456 457 if (*p != '>') 458 return NULL; 459 else 460 *p = '\0'; 461 p++; 462 463 if (*p != '\0' && *p != ' ') 464 return NULL; 465 466 if (*s == '\0') 467 s = MAILER_DAEMON; 468 469 l = strlen(s) + 1; 470 p = malloc(l); 471 if (p == NULL) 472 { 473 mailerr("421 4.3.0", "Memory exhausted"); 474 exit(EX_TEMPFAIL); 475 } 476 477 (void) strlcpy(p, s, l); 478 return p; 479 } 480 481 char * 482 process_recipient(addr) 483 char *addr; 484 { 485 if (getpwnam(addr) == NULL) 486 return "550 5.1.1 User unknown"; 487 return NULL; 488 } 489 490 #define RCPT_GROW 30 491 492 void 493 dolmtp() 494 { 495 char *return_path = NULL; 496 char **rcpt_addr = NULL; 497 int rcpt_num = 0; 498 int rcpt_alloc = 0; 499 bool gotlhlo = FALSE; 500 char *err; 501 int msgfd; 502 char *p; 503 int i; 504 char myhostname[1024]; 505 char buf[4096]; 506 507 memset(myhostname, '\0', sizeof myhostname); 508 (void) gethostname(myhostname, sizeof myhostname - 1); 509 if (myhostname[0] == '\0') 510 strlcpy(myhostname, "localhost", sizeof myhostname); 511 512 printf("220 %s LMTP ready\r\n", myhostname); 513 for (;;) 514 { 515 (void) fflush(stdout); 516 if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) 517 exit(EX_OK); 518 p = buf + strlen(buf) - 1; 519 if (p >= buf && *p == '\n') 520 *p-- = '\0'; 521 if (p >= buf && *p == '\r') 522 *p-- = '\0'; 523 524 switch (buf[0]) 525 { 526 case 'd': 527 case 'D': 528 if (strcasecmp(buf, "data") == 0) 529 { 530 bool inbody = FALSE; 531 532 if (rcpt_num == 0) 533 { 534 mailerr("503 5.5.1", "No recipients"); 535 continue; 536 } 537 HoldErrs = TRUE; 538 msgfd = store(return_path, rcpt_num, &inbody); 539 HoldErrs = FALSE; 540 if (msgfd < 0 && !inbody) 541 { 542 flush_error(); 543 continue; 544 } 545 546 for (i = 0; i < rcpt_num; i++) 547 { 548 if (msgfd < 0) 549 { 550 /* print error for rcpt */ 551 flush_error(); 552 continue; 553 } 554 p = strchr(rcpt_addr[i], '+'); 555 if (p != NULL) 556 *p = '\0'; 557 deliver(msgfd, rcpt_addr[i]); 558 } 559 if (msgfd >= 0) 560 (void) close(msgfd); 561 goto rset; 562 } 563 goto syntaxerr; 564 /* NOTREACHED */ 565 break; 566 567 case 'l': 568 case 'L': 569 if (strncasecmp(buf, "lhlo ", 5) == 0) 570 { 571 /* check for duplicate per RFC 1651 4.2 */ 572 if (gotlhlo) 573 { 574 mailerr("503", "%s Duplicate LHLO", 575 myhostname); 576 continue; 577 } 578 gotlhlo = TRUE; 579 printf("250-%s\r\n", myhostname); 580 if (EightBitMime) 581 printf("250-8BITMIME\r\n"); 582 printf("250-ENHANCEDSTATUSCODES\r\n"); 583 printf("250 PIPELINING\r\n"); 584 continue; 585 } 586 goto syntaxerr; 587 /* NOTREACHED */ 588 break; 589 590 case 'm': 591 case 'M': 592 if (strncasecmp(buf, "mail ", 5) == 0) 593 { 594 if (return_path != NULL) 595 { 596 mailerr("503 5.5.1", 597 "Nested MAIL command"); 598 continue; 599 } 600 if (strncasecmp(buf+5, "from:", 5) != 0 || 601 ((return_path = parseaddr(buf + 10, 602 FALSE)) == NULL)) 603 { 604 mailerr("501 5.5.4", 605 "Syntax error in parameters"); 606 continue; 607 } 608 printf("250 2.5.0 Ok\r\n"); 609 continue; 610 } 611 goto syntaxerr; 612 /* NOTREACHED */ 613 break; 614 615 case 'n': 616 case 'N': 617 if (strcasecmp(buf, "noop") == 0) 618 { 619 printf("250 2.0.0 Ok\r\n"); 620 continue; 621 } 622 goto syntaxerr; 623 /* NOTREACHED */ 624 break; 625 626 case 'q': 627 case 'Q': 628 if (strcasecmp(buf, "quit") == 0) 629 { 630 printf("221 2.0.0 Bye\r\n"); 631 exit(EX_OK); 632 } 633 goto syntaxerr; 634 /* NOTREACHED */ 635 break; 636 637 case 'r': 638 case 'R': 639 if (strncasecmp(buf, "rcpt ", 5) == 0) 640 { 641 if (return_path == NULL) 642 { 643 mailerr("503 5.5.1", 644 "Need MAIL command"); 645 continue; 646 } 647 if (rcpt_num >= rcpt_alloc) 648 { 649 rcpt_alloc += RCPT_GROW; 650 rcpt_addr = (char **) 651 REALLOC((char *) rcpt_addr, 652 rcpt_alloc * 653 sizeof(char **)); 654 if (rcpt_addr == NULL) 655 { 656 mailerr("421 4.3.0", 657 "Memory exhausted"); 658 exit(EX_TEMPFAIL); 659 } 660 } 661 if (strncasecmp(buf + 5, "to:", 3) != 0 || 662 ((rcpt_addr[rcpt_num] = parseaddr(buf + 8, 663 TRUE)) == NULL)) 664 { 665 mailerr("501 5.5.4", 666 "Syntax error in parameters"); 667 continue; 668 } 669 err = process_recipient(rcpt_addr[rcpt_num]); 670 if (err != NULL) 671 { 672 mailerr(NULL, "%s", err); 673 continue; 674 } 675 rcpt_num++; 676 printf("250 2.1.5 Ok\r\n"); 677 continue; 678 } 679 else if (strcasecmp(buf, "rset") == 0) 680 { 681 printf("250 2.0.0 Ok\r\n"); 682 683 rset: 684 while (rcpt_num > 0) 685 free(rcpt_addr[--rcpt_num]); 686 if (return_path != NULL) 687 free(return_path); 688 return_path = NULL; 689 continue; 690 } 691 goto syntaxerr; 692 /* NOTREACHED */ 693 break; 694 695 case 'v': 696 case 'V': 697 if (strncasecmp(buf, "vrfy ", 5) == 0) 698 { 699 printf("252 2.3.3 Try RCPT to attempt delivery\r\n"); 700 continue; 701 } 702 goto syntaxerr; 703 /* NOTREACHED */ 704 break; 705 706 default: 707 syntaxerr: 708 mailerr("500 5.5.2", "Syntax error"); 709 continue; 710 /* NOTREACHED */ 711 break; 712 } 713 } 714 } 715 716 int 717 store(from, lmtprcpts, inbody) 718 char *from; 719 int lmtprcpts; 720 bool *inbody; 721 { 722 FILE *fp = NULL; 723 time_t tval; 724 bool eline; 725 bool fullline = TRUE; /* current line is terminated */ 726 bool prevfl; /* previous line was terminated */ 727 char line[2048]; 728 int fd; 729 char tmpbuf[sizeof _PATH_LOCTMP + 1]; 730 731 if (inbody != NULL) 732 *inbody = FALSE; 733 734 (void) umask(0077); 735 (void) strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf); 736 if ((fd = mkstemp(tmpbuf)) < 0 || (fp = fdopen(fd, "w+")) == NULL) 737 { 738 mailerr("451 4.3.0", "Unable to open temporary file"); 739 return -1; 740 } 741 (void) unlink(tmpbuf); 742 743 if (LMTPMode) 744 { 745 printf("354 Go ahead\r\n"); 746 (void) fflush(stdout); 747 } 748 if (inbody != NULL) 749 *inbody = TRUE; 750 751 (void) time(&tval); 752 (void) fprintf(fp, "From %s %s", from, ctime(&tval)); 753 754 #ifdef CONTENTLENGTH 755 HeaderLength = 0; 756 BodyLength = -1; 757 #endif /* CONTENTLENGTH */ 758 759 line[0] = '\0'; 760 eline = TRUE; 761 while (fgets(line, sizeof(line), stdin) != (char *) NULL) 762 { 763 size_t line_len = 0; 764 int peek; 765 766 prevfl = fullline; /* preserve state of previous line */ 767 while (line[line_len] != '\n' && line_len < sizeof(line) - 2) 768 line_len++; 769 line_len++; 770 771 /* Check for dot-stuffing */ 772 if (prevfl && LMTPMode && line[0] == '.') 773 { 774 if (line[1] == '\n' || 775 (line[1] == '\r' && line[2] == '\n')) 776 goto lmtpdot; 777 memcpy(line, line + 1, line_len); 778 line_len--; 779 } 780 781 /* Check to see if we have the full line from fgets() */ 782 fullline = FALSE; 783 if (line_len > 0) 784 { 785 if (line[line_len - 1] == '\n') 786 { 787 if (line_len >= 2 && 788 line[line_len - 2] == '\r') 789 { 790 line[line_len - 2] = '\n'; 791 line[line_len - 1] = '\0'; 792 line_len--; 793 } 794 fullline = TRUE; 795 } 796 else if (line[line_len - 1] == '\r') 797 { 798 /* Did we just miss the CRLF? */ 799 peek = fgetc(stdin); 800 if (peek == '\n') 801 { 802 line[line_len - 1] = '\n'; 803 fullline = TRUE; 804 } 805 else 806 (void) ungetc(peek, stdin); 807 } 808 } 809 else 810 fullline = TRUE; 811 812 #ifdef CONTENTLENGTH 813 if (prevfl && line[0] == '\n' && HeaderLength == 0) 814 { 815 eline = FALSE; 816 if (fp != NULL) 817 HeaderLength = ftell(fp); 818 if (HeaderLength <= 0) 819 { 820 /* 821 ** shouldn't happen, unless ftell() is 822 ** badly broken 823 */ 824 825 HeaderLength = -1; 826 } 827 } 828 #else /* CONTENTLENGTH */ 829 if (prevfl && line[0] == '\n') 830 eline = TRUE; 831 #endif /* CONTENTLENGTH */ 832 else 833 { 834 if (eline && line[0] == 'F' && 835 fp != NULL && 836 !memcmp(line, "From ", 5)) 837 (void) putc('>', fp); 838 eline = FALSE; 839 #ifdef CONTENTLENGTH 840 /* discard existing "Content-Length:" headers */ 841 if (prevfl && HeaderLength == 0 && 842 (line[0] == 'C' || line[0] == 'c') && 843 strncasecmp(line, ContentHdr, 15) == 0) 844 { 845 /* 846 ** be paranoid: clear the line 847 ** so no "wrong matches" may occur later 848 */ 849 line[0] = '\0'; 850 continue; 851 } 852 #endif /* CONTENTLENGTH */ 853 854 } 855 if (fp != NULL) 856 { 857 (void) fwrite(line, sizeof(char), line_len, fp); 858 if (ferror(fp)) 859 { 860 mailerr("451 4.3.0", 861 "Temporary file write error"); 862 (void) fclose(fp); 863 fp = NULL; 864 continue; 865 } 866 } 867 } 868 869 /* check if an error occurred */ 870 if (fp == NULL) 871 return -1; 872 873 if (LMTPMode) 874 { 875 /* Got a premature EOF -- toss message and exit */ 876 exit(EX_OK); 877 } 878 879 /* If message not newline terminated, need an extra. */ 880 if (fp != NULL && strchr(line, '\n') == NULL) 881 (void) putc('\n', fp); 882 883 lmtpdot: 884 885 #ifdef CONTENTLENGTH 886 if (fp != NULL) 887 BodyLength = ftell(fp); 888 if (HeaderLength == 0 && BodyLength > 0) /* empty body */ 889 { 890 HeaderLength = BodyLength; 891 BodyLength = 0; 892 } 893 else 894 BodyLength = BodyLength - HeaderLength - 1 ; 895 896 if (HeaderLength > 0 && BodyLength >= 0) 897 { 898 extern char *quad_to_string(); 899 900 if (sizeof BodyLength > sizeof(long)) 901 snprintf(line, sizeof line, "%s\n", 902 quad_to_string(BodyLength)); 903 else 904 snprintf(line, sizeof line, "%ld\n", 905 (long) BodyLength); 906 strlcpy(&ContentHdr[16], line, sizeof(ContentHdr) - 16); 907 } 908 else 909 BodyLength = -1; /* Something is wrong here */ 910 #endif /* CONTENTLENGTH */ 911 912 /* Output a newline; note, empty messages are allowed. */ 913 if (fp != NULL) 914 (void) putc('\n', fp); 915 916 if (fp == NULL || fflush(fp) == EOF || ferror(fp) != 0) 917 { 918 mailerr("451 4.3.0", "Temporary file write error"); 919 if (fp != NULL) 920 (void) fclose(fp); 921 return -1; 922 } 923 return fd; 924 } 925 926 void 927 deliver(fd, name) 928 int fd; 929 char *name; 930 { 931 struct stat fsb; 932 struct stat sb; 933 struct passwd *pw; 934 char path[MAXPATHLEN]; 935 int mbfd = -1, nr = 0, nw, off; 936 char *p; 937 char *errcode; 938 off_t curoff; 939 #ifdef CONTENTLENGTH 940 off_t headerbytes; 941 int readamount; 942 #endif /* CONTENTLENGTH */ 943 char biffmsg[100], buf[8*1024]; 944 extern char *quad_to_string(); 945 946 947 /* 948 ** Disallow delivery to unknown names -- special mailboxes can be 949 ** handled in the sendmail aliases file. 950 */ 951 952 if ((pw = getpwnam(name)) == NULL) 953 { 954 if (ExitVal == EX_TEMPFAIL) 955 errcode = "451 4.3.0"; 956 else 957 { 958 ExitVal = EX_UNAVAILABLE; 959 errcode = "550 5.1.1"; 960 } 961 mailerr(errcode, "Unknown name: %s", name); 962 return; 963 } 964 endpwent(); 965 966 /* 967 ** Keep name reasonably short to avoid buffer overruns. 968 ** This isn't necessary on BSD because of the proper 969 ** definition of snprintf(), but it can cause problems 970 ** on other systems. 971 ** Also, clear out any bogus characters. 972 */ 973 974 if (strlen(name) > 40) 975 name[40] = '\0'; 976 for (p = name; *p != '\0'; p++) 977 { 978 if (!isascii(*p)) 979 *p &= 0x7f; 980 else if (!isprint(*p)) 981 *p = '.'; 982 } 983 984 985 (void) snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name); 986 987 988 /* 989 ** If the mailbox is linked or a symlink, fail. There's an obvious 990 ** race here, that the file was replaced with a symbolic link after 991 ** the lstat returned, but before the open. We attempt to detect 992 ** this by comparing the original stat information and information 993 ** returned by an fstat of the file descriptor returned by the open. 994 ** 995 ** NB: this is a symptom of a larger problem, that the mail spooling 996 ** directory is writeable by the wrong users. If that directory is 997 ** writeable, system security is compromised for other reasons, and 998 ** it cannot be fixed here. 999 ** 1000 ** If we created the mailbox, set the owner/group. If that fails, 1001 ** just return. Another process may have already opened it, so we 1002 ** can't unlink it. Historically, binmail set the owner/group at 1003 ** each mail delivery. We no longer do this, assuming that if the 1004 ** ownership or permissions were changed there was a reason. 1005 ** 1006 ** XXX 1007 ** open(2) should support flock'ing the file. 1008 */ 1009 1010 tryagain: 1011 #ifdef MAILLOCK 1012 p = name; 1013 #else /* MAILLOCK */ 1014 p = path; 1015 #endif /* MAILLOCK */ 1016 if ((off = lockmbox(p)) != 0) 1017 { 1018 if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL) 1019 { 1020 ExitVal = EX_TEMPFAIL; 1021 errcode = "451 4.3.0"; 1022 } 1023 else 1024 errcode = "551 5.3.0"; 1025 1026 mailerr(errcode, "lockmailbox %s failed; error code %d %s", 1027 p, off, errno > 0 ? errstring(errno) : ""); 1028 return; 1029 } 1030 1031 if (lstat(path, &sb) < 0) 1032 { 1033 int save_errno; 1034 int mode = S_IRUSR|S_IWUSR; 1035 gid_t gid = U_GID; 1036 1037 #ifdef MAILGID 1038 (void) umask(0007); 1039 gid = MAILGID; 1040 mode |= S_IRGRP|S_IWGRP; 1041 #endif /* MAILGID */ 1042 1043 mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY|EXTRA_MODE, 1044 mode); 1045 save_errno = errno; 1046 1047 if (lstat(path, &sb) < 0) 1048 { 1049 ExitVal = EX_CANTCREAT; 1050 mailerr("550 5.2.0", 1051 "%s: lstat: file changed after open", path); 1052 goto err1; 1053 } 1054 if (mbfd < 0) 1055 { 1056 if (save_errno == EEXIST) 1057 goto tryagain; 1058 1059 /* open failed, don't try again */ 1060 mailerr("450 4.2.0", "%s: %s", path, 1061 errstring(save_errno)); 1062 goto err0; 1063 } 1064 else if (fchown(mbfd, U_UID, gid) < 0) 1065 { 1066 mailerr("451 4.3.0", "chown %u.%u: %s", 1067 U_UID, gid, name); 1068 goto err1; 1069 } 1070 else 1071 { 1072 /* 1073 ** open() was successful, now close it so can 1074 ** be opened as the right owner again. 1075 ** Paranoia: reset mbdf since the file descriptor 1076 ** is no longer valid; better safe than sorry. 1077 */ 1078 1079 sb.st_uid = U_UID; 1080 (void) close(mbfd); 1081 mbfd = -1; 1082 } 1083 } 1084 else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) 1085 { 1086 mailerr("550 5.2.0", "%s: irregular file", path); 1087 goto err0; 1088 } 1089 else if (sb.st_uid != U_UID) 1090 { 1091 ExitVal = EX_CANTCREAT; 1092 mailerr("550 5.2.0", "%s: wrong ownership (%d)", 1093 path, sb.st_uid); 1094 goto err0; 1095 } 1096 1097 /* change UID for quota checks */ 1098 if (setreuid(0, U_UID) < 0) 1099 { 1100 mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)", 1101 U_UID, errstring(errno), getuid(), geteuid()); 1102 goto err1; 1103 } 1104 #ifdef DEBUG 1105 fprintf(stderr, "new euid = %d\n", geteuid()); 1106 #endif /* DEBUG */ 1107 mbfd = open(path, O_APPEND|O_WRONLY|EXTRA_MODE, 0); 1108 if (mbfd < 0) 1109 { 1110 mailerr("450 4.2.0", "%s: %s", path, errstring(errno)); 1111 goto err0; 1112 } 1113 else if (fstat(mbfd, &fsb) < 0 || 1114 fsb.st_nlink != 1 || 1115 sb.st_nlink != 1 || 1116 !S_ISREG(fsb.st_mode) || 1117 sb.st_dev != fsb.st_dev || 1118 sb.st_ino != fsb.st_ino || 1119 # if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */ 1120 sb.st_gen != fsb.st_gen || 1121 # endif /* HAS_ST_GEN && 0 */ 1122 sb.st_uid != fsb.st_uid) 1123 { 1124 ExitVal = EX_TEMPFAIL; 1125 mailerr("550 5.2.0", "%s: fstat: file changed after open", 1126 path); 1127 goto err1; 1128 } 1129 1130 1131 /* Wait until we can get a lock on the file. */ 1132 if (flock(mbfd, LOCK_EX) < 0) 1133 { 1134 mailerr("450 4.2.0", "%s: %s", path, errstring(errno)); 1135 goto err1; 1136 } 1137 1138 /* Get the starting offset of the new message for biff. */ 1139 curoff = lseek(mbfd, (off_t) 0, SEEK_END); 1140 if (sizeof curoff > sizeof(long)) 1141 (void) snprintf(biffmsg, sizeof(biffmsg), "%s@%s\n", 1142 name, quad_to_string(curoff)); 1143 else 1144 (void) snprintf(biffmsg, sizeof(biffmsg), "%s@%ld\n", 1145 name, (long) curoff); 1146 1147 /* Copy the message into the file. */ 1148 if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1) 1149 { 1150 mailerr("450 4.2.0", "Temporary file: %s", 1151 errstring(errno)); 1152 goto err1; 1153 } 1154 #ifdef DEBUG 1155 fprintf(stderr, "before writing: euid = %d\n", geteuid()); 1156 #endif /* DEBUG */ 1157 #ifdef CONTENTLENGTH 1158 headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ; 1159 for (;;) 1160 { 1161 if (headerbytes == 0) 1162 { 1163 snprintf(buf, sizeof buf, "%s", ContentHdr); 1164 nr = strlen(buf); 1165 headerbytes = -1; 1166 readamount = 0; 1167 } 1168 else if (headerbytes > sizeof(buf) || headerbytes < 0) 1169 readamount = sizeof(buf); 1170 else 1171 readamount = headerbytes; 1172 if (readamount != 0) 1173 nr = read(fd, buf, readamount); 1174 if (nr <= 0) 1175 break; 1176 if (headerbytes > 0) 1177 headerbytes -= nr ; 1178 1179 #else /* CONTENTLENGTH */ 1180 while ((nr = read(fd, buf, sizeof(buf))) > 0) 1181 { 1182 #endif /* CONTENTLENGTH */ 1183 for (off = 0; off < nr; off += nw) 1184 { 1185 if ((nw = write(mbfd, buf + off, nr - off)) < 0) 1186 { 1187 errcode = "450 4.2.0"; 1188 #ifdef EDQUOT 1189 if (errno == EDQUOT && BounceQuota) 1190 errcode = "552 5.2.2"; 1191 #endif /* EDQUOT */ 1192 mailerr(errcode, "%s: %s", 1193 path, errstring(errno)); 1194 goto err3; 1195 } 1196 } 1197 } 1198 if (nr < 0) 1199 { 1200 mailerr("450 4.2.0", "Temporary file: %s", 1201 errstring(errno)); 1202 goto err3; 1203 } 1204 1205 /* Flush to disk, don't wait for update. */ 1206 if (fsync(mbfd) < 0) 1207 { 1208 mailerr("450 4.2.0", "%s: %s", path, errstring(errno)); 1209 err3: 1210 (void) setreuid(0, 0); 1211 #ifdef DEBUG 1212 fprintf(stderr, "reset euid = %d\n", geteuid()); 1213 #endif /* DEBUG */ 1214 (void) ftruncate(mbfd, curoff); 1215 err1: if (mbfd >= 0) 1216 (void) close(mbfd); 1217 err0: unlockmbox(); 1218 return; 1219 } 1220 1221 /* Close and check -- NFS doesn't write until the close. */ 1222 if (close(mbfd)) 1223 { 1224 errcode = "450 4.2.0"; 1225 #ifdef EDQUOT 1226 if (errno == EDQUOT && BounceQuota) 1227 errcode = "552 5.2.2"; 1228 #endif /* EDQUOT */ 1229 mailerr(errcode, "%s: %s", path, errstring(errno)); 1230 (void) truncate(path, curoff); 1231 } 1232 else 1233 notifybiff(biffmsg); 1234 1235 if (setreuid(0, 0) < 0) 1236 { 1237 mailerr("450 4.2.0", "setreuid(0, 0): %s", 1238 errstring(errno)); 1239 goto err0; 1240 } 1241 #ifdef DEBUG 1242 fprintf(stderr, "reset euid = %d\n", geteuid()); 1243 #endif /* DEBUG */ 1244 unlockmbox(); 1245 if (LMTPMode) 1246 printf("250 2.1.5 %s Ok\r\n", name); 1247 } 1248 1249 /* 1250 ** user.lock files are necessary for compatibility with other 1251 ** systems, e.g., when the mail spool file is NFS exported. 1252 ** Alas, mailbox locking is more than just a local matter. 1253 ** EPA 11/94. 1254 */ 1255 1256 bool Locked = FALSE; 1257 1258 #ifdef MAILLOCK 1259 int 1260 lockmbox(name) 1261 char *name; 1262 { 1263 int r = 0; 1264 1265 if (Locked) 1266 return 0; 1267 if ((r = maillock(name, 15)) == L_SUCCESS) 1268 { 1269 Locked = TRUE; 1270 return 0; 1271 } 1272 switch (r) 1273 { 1274 case L_TMPLOCK: /* Can't create tmp file */ 1275 case L_TMPWRITE: /* Can't write pid into lockfile */ 1276 case L_MAXTRYS: /* Failed after retrycnt attempts */ 1277 errno = 0; 1278 r = EX_TEMPFAIL; 1279 break; 1280 case L_ERROR: /* Check errno for reason */ 1281 r = errno; 1282 break; 1283 default: /* other permanent errors */ 1284 errno = 0; 1285 r = EX_UNAVAILABLE; 1286 break; 1287 } 1288 return r; 1289 } 1290 1291 void 1292 unlockmbox() 1293 { 1294 if (Locked) 1295 mailunlock(); 1296 Locked = FALSE; 1297 } 1298 #else /* MAILLOCK */ 1299 1300 char LockName[MAXPATHLEN]; 1301 1302 int 1303 lockmbox(path) 1304 char *path; 1305 { 1306 int statfailed = 0; 1307 time_t start; 1308 1309 if (Locked) 1310 return 0; 1311 if (strlen(path) + 6 > sizeof LockName) 1312 return EX_SOFTWARE; 1313 (void) snprintf(LockName, sizeof LockName, "%s.lock", path); 1314 (void) time(&start); 1315 for (; ; sleep(5)) 1316 { 1317 int fd; 1318 struct stat st; 1319 time_t now; 1320 1321 /* global timeout */ 1322 (void) time(&now); 1323 if (now > start + LOCKTO_GLOB) 1324 { 1325 errno = 0; 1326 return EX_TEMPFAIL; 1327 } 1328 fd = open(LockName, O_WRONLY|O_EXCL|O_CREAT, 0); 1329 if (fd >= 0) 1330 { 1331 /* defeat lock checking programs which test pid */ 1332 (void) write(fd, "0", 2); 1333 Locked = TRUE; 1334 (void) close(fd); 1335 return 0; 1336 } 1337 if (stat(LockName, &st) < 0) 1338 { 1339 if (statfailed++ > 5) 1340 { 1341 errno = 0; 1342 return EX_TEMPFAIL; 1343 } 1344 continue; 1345 } 1346 statfailed = 0; 1347 (void) time(&now); 1348 if (now < st.st_ctime + LOCKTO_RM) 1349 continue; 1350 1351 /* try to remove stale lockfile */ 1352 if (unlink(LockName) < 0) 1353 return errno; 1354 } 1355 } 1356 1357 void 1358 unlockmbox() 1359 { 1360 if (!Locked) 1361 return; 1362 (void) unlink(LockName); 1363 Locked = FALSE; 1364 } 1365 #endif /* MAILLOCK */ 1366 1367 void 1368 notifybiff(msg) 1369 char *msg; 1370 { 1371 static bool initialized = FALSE; 1372 static int f = -1; 1373 struct hostent *hp; 1374 struct servent *sp; 1375 int len; 1376 static struct sockaddr_in addr; 1377 1378 if (!initialized) 1379 { 1380 initialized = TRUE; 1381 1382 /* Be silent if biff service not available. */ 1383 if ((sp = getservbyname("biff", "udp")) == NULL || 1384 (hp = gethostbyname("localhost")) == NULL || 1385 hp->h_length != INADDRSZ) 1386 return; 1387 1388 addr.sin_family = hp->h_addrtype; 1389 memcpy(&addr.sin_addr, hp->h_addr, INADDRSZ); 1390 addr.sin_port = sp->s_port; 1391 } 1392 1393 /* No message, just return */ 1394 if (msg == NULL) 1395 return; 1396 1397 /* Couldn't initialize addr struct */ 1398 if (addr.sin_family == AF_UNSPEC) 1399 return; 1400 1401 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 1402 return; 1403 len = strlen(msg) + 1; 1404 (void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr)); 1405 } 1406 1407 void 1408 usage() 1409 { 1410 ExitVal = EX_USAGE; 1411 mailerr(NULL, "usage: mail.local [-7] [-b] [-l] [-f from] user ..."); 1412 exit(ExitVal); 1413 } 1414 1415 void 1416 #ifdef __STDC__ 1417 mailerr(const char *hdr, const char *fmt, ...) 1418 #else /* __STDC__ */ 1419 mailerr(hdr, fmt, va_alist) 1420 const char *hdr; 1421 const char *fmt; 1422 va_dcl 1423 #endif /* __STDC__ */ 1424 { 1425 size_t len = 0; 1426 va_list ap; 1427 1428 (void) e_to_sys(errno); 1429 1430 #ifdef __STDC__ 1431 va_start(ap, fmt); 1432 #else /* __STDC__ */ 1433 va_start(ap); 1434 #endif /* __STDC__ */ 1435 1436 if (LMTPMode) 1437 { 1438 if (hdr != NULL) 1439 { 1440 snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr); 1441 len = strlen(ErrBuf); 1442 } 1443 } 1444 (void) vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, 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 u_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