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