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