1 /* 2 * Copyright (c) 1998 Sendmail, Inc. All rights reserved. 3 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 4 * Copyright (c) 1988, 1993 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 sccsid[] = "@(#)util.c 8.167 (Berkeley) 12/1/1998"; 15 #endif /* not lint */ 16 17 # include "sendmail.h" 18 # include <sysexits.h> 19 /* 20 ** STRIPQUOTES -- Strip quotes & quote bits from a string. 21 ** 22 ** Runs through a string and strips off unquoted quote 23 ** characters and quote bits. This is done in place. 24 ** 25 ** Parameters: 26 ** s -- the string to strip. 27 ** 28 ** Returns: 29 ** none. 30 ** 31 ** Side Effects: 32 ** none. 33 ** 34 ** Called By: 35 ** deliver 36 */ 37 38 void 39 stripquotes(s) 40 char *s; 41 { 42 register char *p; 43 register char *q; 44 register char c; 45 46 if (s == NULL) 47 return; 48 49 p = q = s; 50 do 51 { 52 c = *p++; 53 if (c == '\\') 54 c = *p++; 55 else if (c == '"') 56 continue; 57 *q++ = c; 58 } while (c != '\0'); 59 } 60 /* 61 ** ADDQUOTES -- Adds quotes & quote bits to a string. 62 ** 63 ** Runs through a string and adds characters and quote bits. 64 ** 65 ** Parameters: 66 ** s -- the string to modify. 67 ** 68 ** Returns: 69 ** pointer to quoted string. 70 ** 71 ** Side Effects: 72 ** none. 73 ** 74 */ 75 76 char * 77 addquotes(s) 78 char *s; 79 { 80 int len = 0; 81 char c; 82 char *p = s, *q, *r; 83 84 if (s == NULL) 85 return NULL; 86 87 /* Find length of quoted string */ 88 while ((c = *p++) != '\0') 89 { 90 len++; 91 if (c == '\\' || c == '"') 92 len++; 93 } 94 95 q = r = xalloc(len + 3); 96 p = s; 97 98 /* add leading quote */ 99 *q++ = '"'; 100 while ((c = *p++) != '\0') 101 { 102 /* quote \ or " */ 103 if (c == '\\' || c == '"') 104 *q++ = '\\'; 105 *q++ = c; 106 } 107 *q++ = '"'; 108 *q = '\0'; 109 return r; 110 } 111 /* 112 ** RFC822_STRING -- Checks string for proper RFC822 string quoting. 113 ** 114 ** Runs through a string and verifies RFC822 special characters 115 ** are only found inside comments, quoted strings, or backslash 116 ** escaped. Also verified balanced quotes and parenthesis. 117 ** 118 ** Parameters: 119 ** s -- the string to modify. 120 ** 121 ** Returns: 122 ** TRUE -- if the string is RFC822 compliant. 123 ** FALSE -- if the string is not RFC822 compliant. 124 ** 125 ** Side Effects: 126 ** none. 127 ** 128 */ 129 130 bool 131 rfc822_string(s) 132 char *s; 133 { 134 bool quoted = FALSE; 135 int commentlev = 0; 136 char *c = s; 137 138 if (s == NULL) 139 return FALSE; 140 141 while (*c != '\0') 142 { 143 /* escaped character */ 144 if (*c == '\\') 145 { 146 c++; 147 if (*c == '\0') 148 return FALSE; 149 } 150 else if (commentlev == 0 && *c == '"') 151 quoted = !quoted; 152 else if (!quoted) 153 { 154 if (*c == ')') 155 { 156 /* unbalanced ')' */ 157 if (commentlev == 0) 158 return FALSE; 159 else 160 commentlev--; 161 } 162 else if (*c == '(') 163 commentlev++; 164 else if (commentlev == 0 && 165 strchr(MustQuoteChars, *c) != NULL) 166 return FALSE; 167 } 168 c++; 169 } 170 /* unbalanced '"' or '(' */ 171 if (quoted || commentlev != 0) 172 return FALSE; 173 else 174 return TRUE; 175 } 176 /* 177 ** SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string 178 ** 179 ** Arbitratily shorten (in place) an RFC822 string and rebalance 180 ** comments and quotes. 181 ** 182 ** Parameters: 183 ** string -- the string to shorten 184 ** length -- the maximum size, 0 if no maximum 185 ** 186 ** Returns: 187 ** TRUE if string is changed, FALSE otherwise 188 ** 189 ** Side Effects: 190 ** Changes string in place, possibly resulting 191 ** in a shorter string. 192 */ 193 194 bool 195 shorten_rfc822_string(string, length) 196 char *string; 197 size_t length; 198 { 199 bool backslash = FALSE; 200 bool modified = FALSE; 201 bool quoted = FALSE; 202 size_t slen; 203 int parencount = 0; 204 char *ptr = string; 205 206 /* 207 ** If have to rebalance an already short enough string, 208 ** need to do it within allocated space. 209 */ 210 slen = strlen(string); 211 if (length == 0 || slen < length) 212 length = slen; 213 214 while (*ptr != '\0') 215 { 216 if (backslash) 217 { 218 backslash = FALSE; 219 goto increment; 220 } 221 222 if (*ptr == '\\') 223 backslash = TRUE; 224 else if (*ptr == '(') 225 { 226 if (!quoted) 227 parencount++; 228 } 229 else if (*ptr == ')') 230 { 231 if (--parencount < 0) 232 parencount = 0; 233 } 234 235 /* Inside a comment, quotes don't matter */ 236 if (parencount <= 0 && *ptr == '"') 237 quoted = !quoted; 238 239 increment: 240 /* Check for sufficient space for next character */ 241 if (length - (ptr - string) <= ((backslash ? 1 : 0) + 242 parencount + 243 (quoted ? 1 : 0))) 244 { 245 /* Not enough, backtrack */ 246 if (*ptr == '\\') 247 backslash = FALSE; 248 else if (*ptr == '(' && !quoted) 249 parencount--; 250 else if (*ptr == '"' && parencount == 0) 251 quoted = FALSE; 252 break; 253 } 254 ptr++; 255 } 256 257 /* Rebalance */ 258 while (parencount-- > 0) 259 { 260 if (*ptr != ')') 261 { 262 modified = TRUE; 263 *ptr = ')'; 264 } 265 ptr++; 266 } 267 if (quoted) 268 { 269 if (*ptr != '"') 270 { 271 modified = TRUE; 272 *ptr = '"'; 273 } 274 ptr++; 275 } 276 if (*ptr != '\0') 277 { 278 modified = TRUE; 279 *ptr = '\0'; 280 } 281 return modified; 282 } 283 /* 284 ** FIND_CHARACTER -- find an unquoted character in an RFC822 string 285 ** 286 ** Find an unquoted, non-commented character in an RFC822 287 ** string and return a pointer to its location in the 288 ** string. 289 ** 290 ** Parameters: 291 ** string -- the string to search 292 ** character -- the character to find 293 ** 294 ** Returns: 295 ** pointer to the character, or 296 ** a pointer to the end of the line if character is not found 297 */ 298 299 char * 300 find_character(string, character) 301 char *string; 302 char character; 303 { 304 bool backslash = FALSE; 305 bool quoted = FALSE; 306 int parencount = 0; 307 308 while (string != NULL && *string != '\0') 309 { 310 if (backslash) 311 { 312 backslash = FALSE; 313 if (!quoted && character == '\\' && *string == '\\') 314 break; 315 string++; 316 continue; 317 } 318 switch (*string) 319 { 320 case '\\': 321 backslash = TRUE; 322 break; 323 324 case '(': 325 if (!quoted) 326 parencount++; 327 break; 328 329 case ')': 330 if (--parencount < 0) 331 parencount = 0; 332 break; 333 } 334 335 /* Inside a comment, nothing matters */ 336 if (parencount > 0) 337 { 338 string++; 339 continue; 340 } 341 342 if (*string == '"') 343 quoted = !quoted; 344 else if (*string == character && !quoted) 345 break; 346 string++; 347 } 348 349 /* Return pointer to the character */ 350 return string; 351 } 352 /* 353 ** XALLOC -- Allocate memory and bitch wildly on failure. 354 ** 355 ** THIS IS A CLUDGE. This should be made to give a proper 356 ** error -- but after all, what can we do? 357 ** 358 ** Parameters: 359 ** sz -- size of area to allocate. 360 ** 361 ** Returns: 362 ** pointer to data region. 363 ** 364 ** Side Effects: 365 ** Memory is allocated. 366 */ 367 368 char * 369 xalloc(sz) 370 register int sz; 371 { 372 register char *p; 373 374 /* some systems can't handle size zero mallocs */ 375 if (sz <= 0) 376 sz = 1; 377 378 p = malloc((unsigned) sz); 379 if (p == NULL) 380 { 381 syserr("!Out of memory!!"); 382 /* exit(EX_UNAVAILABLE); */ 383 } 384 return (p); 385 } 386 /* 387 ** COPYPLIST -- copy list of pointers. 388 ** 389 ** This routine is the equivalent of newstr for lists of 390 ** pointers. 391 ** 392 ** Parameters: 393 ** list -- list of pointers to copy. 394 ** Must be NULL terminated. 395 ** copycont -- if TRUE, copy the contents of the vector 396 ** (which must be a string) also. 397 ** 398 ** Returns: 399 ** a copy of 'list'. 400 ** 401 ** Side Effects: 402 ** none. 403 */ 404 405 char ** 406 copyplist(list, copycont) 407 char **list; 408 bool copycont; 409 { 410 register char **vp; 411 register char **newvp; 412 413 for (vp = list; *vp != NULL; vp++) 414 continue; 415 416 vp++; 417 418 newvp = (char **) xalloc((int) (vp - list) * sizeof *vp); 419 bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp); 420 421 if (copycont) 422 { 423 for (vp = newvp; *vp != NULL; vp++) 424 *vp = newstr(*vp); 425 } 426 427 return (newvp); 428 } 429 /* 430 ** COPYQUEUE -- copy address queue. 431 ** 432 ** This routine is the equivalent of newstr for address queues 433 ** addresses marked with QDONTSEND aren't copied 434 ** 435 ** Parameters: 436 ** addr -- list of address structures to copy. 437 ** 438 ** Returns: 439 ** a copy of 'addr'. 440 ** 441 ** Side Effects: 442 ** none. 443 */ 444 445 ADDRESS * 446 copyqueue(addr) 447 ADDRESS *addr; 448 { 449 register ADDRESS *newaddr; 450 ADDRESS *ret; 451 register ADDRESS **tail = &ret; 452 453 while (addr != NULL) 454 { 455 if (!bitset(QDONTSEND, addr->q_flags)) 456 { 457 newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS)); 458 STRUCTCOPY(*addr, *newaddr); 459 *tail = newaddr; 460 tail = &newaddr->q_next; 461 } 462 addr = addr->q_next; 463 } 464 *tail = NULL; 465 466 return ret; 467 } 468 /* 469 ** PRINTAV -- print argument vector. 470 ** 471 ** Parameters: 472 ** av -- argument vector. 473 ** 474 ** Returns: 475 ** none. 476 ** 477 ** Side Effects: 478 ** prints av. 479 */ 480 481 void 482 printav(av) 483 register char **av; 484 { 485 while (*av != NULL) 486 { 487 if (tTd(0, 44)) 488 printf("\n\t%08lx=", (u_long) *av); 489 else 490 (void) putchar(' '); 491 xputs(*av++); 492 } 493 (void) putchar('\n'); 494 } 495 /* 496 ** LOWER -- turn letter into lower case. 497 ** 498 ** Parameters: 499 ** c -- character to turn into lower case. 500 ** 501 ** Returns: 502 ** c, in lower case. 503 ** 504 ** Side Effects: 505 ** none. 506 */ 507 508 char 509 lower(c) 510 register char c; 511 { 512 return((isascii(c) && isupper(c)) ? tolower(c) : c); 513 } 514 /* 515 ** XPUTS -- put string doing control escapes. 516 ** 517 ** Parameters: 518 ** s -- string to put. 519 ** 520 ** Returns: 521 ** none. 522 ** 523 ** Side Effects: 524 ** output to stdout 525 */ 526 527 void 528 xputs(s) 529 register const char *s; 530 { 531 register int c; 532 register struct metamac *mp; 533 bool shiftout = FALSE; 534 extern struct metamac MetaMacros[]; 535 536 if (s == NULL) 537 { 538 printf("%s<null>%s", TermEscape.te_rv_on, TermEscape.te_rv_off); 539 return; 540 } 541 while ((c = (*s++ & 0377)) != '\0') 542 { 543 if (shiftout) 544 { 545 printf("%s", TermEscape.te_rv_off); 546 shiftout = FALSE; 547 } 548 if (!isascii(c)) 549 { 550 if (c == MATCHREPL) 551 { 552 printf("%s$", TermEscape.te_rv_on); 553 shiftout = TRUE; 554 if (*s == '\0') 555 continue; 556 c = *s++ & 0377; 557 goto printchar; 558 } 559 if (c == MACROEXPAND || c == MACRODEXPAND) 560 { 561 printf("%s$", TermEscape.te_rv_on); 562 if (c == MACRODEXPAND) 563 putchar('&'); 564 shiftout = TRUE; 565 if (*s == '\0') 566 continue; 567 if (strchr("=~&?", *s) != NULL) 568 putchar(*s++); 569 if (bitset(0200, *s)) 570 printf("{%s}", macname(*s++ & 0377)); 571 else 572 printf("%c", *s++); 573 continue; 574 } 575 for (mp = MetaMacros; mp->metaname != '\0'; mp++) 576 { 577 if ((mp->metaval & 0377) == c) 578 { 579 printf("%s$%c", 580 TermEscape.te_rv_on, 581 mp->metaname); 582 shiftout = TRUE; 583 break; 584 } 585 } 586 if (c == MATCHCLASS || c == MATCHNCLASS) 587 { 588 if (bitset(0200, *s)) 589 printf("{%s}", macname(*s++ & 0377)); 590 else if (*s != '\0') 591 printf("%c", *s++); 592 } 593 if (mp->metaname != '\0') 594 continue; 595 596 /* unrecognized meta character */ 597 printf("%sM-", TermEscape.te_rv_on); 598 shiftout = TRUE; 599 c &= 0177; 600 } 601 printchar: 602 if (isprint(c)) 603 { 604 putchar(c); 605 continue; 606 } 607 608 /* wasn't a meta-macro -- find another way to print it */ 609 switch (c) 610 { 611 case '\n': 612 c = 'n'; 613 break; 614 615 case '\r': 616 c = 'r'; 617 break; 618 619 case '\t': 620 c = 't'; 621 break; 622 } 623 if (!shiftout) 624 { 625 printf("%s", TermEscape.te_rv_on); 626 shiftout = TRUE; 627 } 628 if (isprint(c)) 629 { 630 (void) putchar('\\'); 631 (void) putchar(c); 632 } 633 else 634 { 635 (void) putchar('^'); 636 (void) putchar(c ^ 0100); 637 } 638 } 639 if (shiftout) 640 printf("%s", TermEscape.te_rv_off); 641 (void) fflush(stdout); 642 } 643 /* 644 ** MAKELOWER -- Translate a line into lower case 645 ** 646 ** Parameters: 647 ** p -- the string to translate. If NULL, return is 648 ** immediate. 649 ** 650 ** Returns: 651 ** none. 652 ** 653 ** Side Effects: 654 ** String pointed to by p is translated to lower case. 655 ** 656 ** Called By: 657 ** parse 658 */ 659 660 void 661 makelower(p) 662 register char *p; 663 { 664 register char c; 665 666 if (p == NULL) 667 return; 668 for (; (c = *p) != '\0'; p++) 669 if (isascii(c) && isupper(c)) 670 *p = tolower(c); 671 } 672 /* 673 ** BUILDFNAME -- build full name from gecos style entry. 674 ** 675 ** This routine interprets the strange entry that would appear 676 ** in the GECOS field of the password file. 677 ** 678 ** Parameters: 679 ** p -- name to build. 680 ** login -- the login name of this user (for &). 681 ** buf -- place to put the result. 682 ** buflen -- length of buf. 683 ** 684 ** Returns: 685 ** none. 686 ** 687 ** Side Effects: 688 ** none. 689 */ 690 691 void 692 buildfname(gecos, login, buf, buflen) 693 register char *gecos; 694 char *login; 695 char *buf; 696 int buflen; 697 { 698 register char *p; 699 register char *bp = buf; 700 701 if (*gecos == '*') 702 gecos++; 703 704 /* copy gecos, interpolating & to be full name */ 705 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 706 { 707 if (bp >= &buf[buflen - 1]) 708 { 709 /* buffer overflow -- just use login name */ 710 snprintf(buf, buflen, "%s", login); 711 return; 712 } 713 if (*p == '&') 714 { 715 /* interpolate full name */ 716 snprintf(bp, buflen - (bp - buf), "%s", login); 717 *bp = toupper(*bp); 718 bp += strlen(bp); 719 } 720 else 721 *bp++ = *p; 722 } 723 *bp = '\0'; 724 } 725 /* 726 ** FIXCRLF -- fix <CR><LF> in line. 727 ** 728 ** Looks for the <CR><LF> combination and turns it into the 729 ** UNIX canonical <NL> character. It only takes one line, 730 ** i.e., it is assumed that the first <NL> found is the end 731 ** of the line. 732 ** 733 ** Parameters: 734 ** line -- the line to fix. 735 ** stripnl -- if true, strip the newline also. 736 ** 737 ** Returns: 738 ** none. 739 ** 740 ** Side Effects: 741 ** line is changed in place. 742 */ 743 744 void 745 fixcrlf(line, stripnl) 746 char *line; 747 bool stripnl; 748 { 749 register char *p; 750 751 p = strchr(line, '\n'); 752 if (p == NULL) 753 return; 754 if (p > line && p[-1] == '\r') 755 p--; 756 if (!stripnl) 757 *p++ = '\n'; 758 *p = '\0'; 759 } 760 /* 761 ** PUTLINE -- put a line like fputs obeying SMTP conventions 762 ** 763 ** This routine always guarantees outputing a newline (or CRLF, 764 ** as appropriate) at the end of the string. 765 ** 766 ** Parameters: 767 ** l -- line to put. 768 ** mci -- the mailer connection information. 769 ** 770 ** Returns: 771 ** none 772 ** 773 ** Side Effects: 774 ** output of l to fp. 775 */ 776 777 void 778 putline(l, mci) 779 register char *l; 780 register MCI *mci; 781 { 782 putxline(l, strlen(l), mci, PXLF_MAPFROM); 783 } 784 /* 785 ** PUTXLINE -- putline with flags bits. 786 ** 787 ** This routine always guarantees outputing a newline (or CRLF, 788 ** as appropriate) at the end of the string. 789 ** 790 ** Parameters: 791 ** l -- line to put. 792 ** len -- the length of the line. 793 ** mci -- the mailer connection information. 794 ** pxflags -- flag bits: 795 ** PXLF_MAPFROM -- map From_ to >From_. 796 ** PXLF_STRIP8BIT -- strip 8th bit. 797 ** PXLF_HEADER -- map bare newline in header to newline space. 798 ** 799 ** Returns: 800 ** none 801 ** 802 ** Side Effects: 803 ** output of l to fp. 804 */ 805 806 void 807 putxline(l, len, mci, pxflags) 808 register char *l; 809 size_t len; 810 register MCI *mci; 811 int pxflags; 812 { 813 register char *p, *end; 814 int slop = 0; 815 size_t eol_len = strlen(mci->mci_mailer->m_eol); 816 817 /* strip out 0200 bits -- these can look like TELNET protocol */ 818 if (bitset(MCIF_7BIT, mci->mci_flags) || 819 bitset(PXLF_STRIP8BIT, pxflags)) 820 { 821 register char svchar; 822 823 for (p = l; (svchar = *p) != '\0'; ++p) 824 if (bitset(0200, svchar)) 825 *p = svchar &~ 0200; 826 } 827 828 end = l + len; 829 do 830 { 831 /* find the end of the line */ 832 p = memchr(l, '\n', end - l); 833 if (p == NULL) 834 p = end; 835 836 if (TrafficLogFile != NULL) 837 fprintf(TrafficLogFile, "%05d >>> ", (int) getpid()); 838 839 /* check for line overflow */ 840 while (mci->mci_mailer->m_linelimit > 0 && 841 (p - l + slop) > mci->mci_mailer->m_linelimit) 842 { 843 char *l_base = l; 844 register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; 845 846 if (l[0] == '.' && slop == 0 && 847 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 848 { 849 (void) putc('.', mci->mci_out); 850 if (!bitset(MCIF_INHEADER, mci->mci_flags)) 851 mci->mci_contentlen++; 852 if (TrafficLogFile != NULL) 853 (void) putc('.', TrafficLogFile); 854 } 855 else if (l[0] == 'F' && slop == 0 && 856 bitset(PXLF_MAPFROM, pxflags) && 857 strncmp(l, "From ", 5) == 0 && 858 bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 859 { 860 (void) putc('>', mci->mci_out); 861 if (!bitset(MCIF_INHEADER, mci->mci_flags)) 862 mci->mci_contentlen++; 863 if (TrafficLogFile != NULL) 864 (void) putc('>', TrafficLogFile); 865 } 866 while (l < q) 867 { 868 (void) putc(*l++, mci->mci_out); 869 if (!bitset(MCIF_INHEADER, mci->mci_flags)) 870 mci->mci_contentlen++; 871 } 872 (void) putc('!', mci->mci_out); 873 if (!bitset(MCIF_INHEADER, mci->mci_flags)) 874 mci->mci_contentlen++; 875 fputs(mci->mci_mailer->m_eol, mci->mci_out); 876 if (!bitset(MCIF_INHEADER, mci->mci_flags)) 877 mci->mci_contentlen += eol_len; 878 (void) putc(' ', mci->mci_out); 879 if (!bitset(MCIF_INHEADER, mci->mci_flags)) 880 mci->mci_contentlen++; 881 if (TrafficLogFile != NULL) 882 { 883 for (l = l_base; l < q; l++) 884 (void) putc(*l, TrafficLogFile); 885 fprintf(TrafficLogFile, "!\n%05d >>> ", 886 (int) getpid()); 887 } 888 slop = 1; 889 } 890 891 /* output last part */ 892 if (l[0] == '.' && slop == 0 && 893 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 894 { 895 (void) putc('.', mci->mci_out); 896 if (!bitset(MCIF_INHEADER, mci->mci_flags)) 897 mci->mci_contentlen++; 898 if (TrafficLogFile != NULL) 899 (void) putc('.', TrafficLogFile); 900 } 901 else if (l[0] == 'F' && slop == 0 && 902 bitset(PXLF_MAPFROM, pxflags) && 903 strncmp(l, "From ", 5) == 0 && 904 bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 905 { 906 (void) putc('>', mci->mci_out); 907 if (!bitset(MCIF_INHEADER, mci->mci_flags)) 908 mci->mci_contentlen++; 909 if (TrafficLogFile != NULL) 910 (void) putc('>', TrafficLogFile); 911 } 912 for ( ; l < p; ++l) 913 { 914 if (TrafficLogFile != NULL) 915 (void) putc(*l, TrafficLogFile); 916 (void) putc(*l, mci->mci_out); 917 if (!bitset(MCIF_INHEADER, mci->mci_flags)) 918 mci->mci_contentlen++; 919 } 920 if (TrafficLogFile != NULL) 921 (void) putc('\n', TrafficLogFile); 922 fputs(mci->mci_mailer->m_eol, mci->mci_out); 923 if (!bitset(MCIF_INHEADER, mci->mci_flags)) 924 mci->mci_contentlen += eol_len; 925 if (l < end && *l == '\n') 926 { 927 if (*++l != ' ' && *l != '\t' && *l != '\0' && 928 bitset(PXLF_HEADER, pxflags)) 929 { 930 (void) putc(' ', mci->mci_out); 931 if (!bitset(MCIF_INHEADER, mci->mci_flags)) 932 mci->mci_contentlen++; 933 if (TrafficLogFile != NULL) 934 (void) putc(' ', TrafficLogFile); 935 } 936 } 937 } while (l < end); 938 } 939 /* 940 ** XUNLINK -- unlink a file, doing logging as appropriate. 941 ** 942 ** Parameters: 943 ** f -- name of file to unlink. 944 ** 945 ** Returns: 946 ** none. 947 ** 948 ** Side Effects: 949 ** f is unlinked. 950 */ 951 952 void 953 xunlink(f) 954 char *f; 955 { 956 register int i; 957 958 if (LogLevel > 98) 959 sm_syslog(LOG_DEBUG, CurEnv->e_id, 960 "unlink %s", 961 f); 962 963 i = unlink(f); 964 if (i < 0 && LogLevel > 97) 965 sm_syslog(LOG_DEBUG, CurEnv->e_id, 966 "%s: unlink-fail %d", 967 f, errno); 968 } 969 /* 970 ** XFCLOSE -- close a file, doing logging as appropriate. 971 ** 972 ** Parameters: 973 ** fp -- file pointer for the file to close 974 ** a, b -- miscellaneous crud to print for debugging 975 ** 976 ** Returns: 977 ** none. 978 ** 979 ** Side Effects: 980 ** fp is closed. 981 */ 982 983 void 984 xfclose(fp, a, b) 985 FILE *fp; 986 char *a, *b; 987 { 988 if (tTd(53, 99)) 989 printf("xfclose(%lx) %s %s\n", (u_long) fp, a, b); 990 #if XDEBUG 991 if (fileno(fp) == 1) 992 syserr("xfclose(%s %s): fd = 1", a, b); 993 #endif 994 if (fclose(fp) < 0 && tTd(53, 99)) 995 printf("xfclose FAILURE: %s\n", errstring(errno)); 996 } 997 /* 998 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 999 ** 1000 ** Parameters: 1001 ** buf -- place to put the input line. 1002 ** siz -- size of buf. 1003 ** fp -- file to read from. 1004 ** timeout -- the timeout before error occurs. 1005 ** during -- what we are trying to read (for error messages). 1006 ** 1007 ** Returns: 1008 ** NULL on error (including timeout). This will also leave 1009 ** buf containing a null string. 1010 ** buf otherwise. 1011 ** 1012 ** Side Effects: 1013 ** none. 1014 */ 1015 1016 static jmp_buf CtxReadTimeout; 1017 static void readtimeout __P((time_t)); 1018 1019 char * 1020 sfgets(buf, siz, fp, timeout, during) 1021 char *buf; 1022 int siz; 1023 FILE *fp; 1024 time_t timeout; 1025 char *during; 1026 { 1027 register EVENT *ev = NULL; 1028 register char *p; 1029 1030 if (fp == NULL) 1031 { 1032 buf[0] = '\0'; 1033 return NULL; 1034 } 1035 1036 /* set the timeout */ 1037 if (timeout != 0) 1038 { 1039 if (setjmp(CtxReadTimeout) != 0) 1040 { 1041 if (LogLevel > 1) 1042 sm_syslog(LOG_NOTICE, CurEnv->e_id, 1043 "timeout waiting for input from %.100s during %s", 1044 CurHostName ? CurHostName : "local", 1045 during); 1046 errno = 0; 1047 buf[0] = '\0'; 1048 #if XDEBUG 1049 checkfd012(during); 1050 #endif 1051 if (TrafficLogFile != NULL) 1052 fprintf(TrafficLogFile, "%05d <<< [TIMEOUT]\n", 1053 (int) getpid()); 1054 return (NULL); 1055 } 1056 ev = setevent(timeout, readtimeout, 0); 1057 } 1058 1059 /* try to read */ 1060 p = NULL; 1061 while (!feof(fp) && !ferror(fp)) 1062 { 1063 errno = 0; 1064 p = fgets(buf, siz, fp); 1065 if (p != NULL || errno != EINTR) 1066 break; 1067 clearerr(fp); 1068 } 1069 1070 /* clear the event if it has not sprung */ 1071 clrevent(ev); 1072 1073 /* clean up the books and exit */ 1074 LineNumber++; 1075 if (p == NULL) 1076 { 1077 buf[0] = '\0'; 1078 if (TrafficLogFile != NULL) 1079 fprintf(TrafficLogFile, "%05d <<< [EOF]\n", (int) getpid()); 1080 return (NULL); 1081 } 1082 if (TrafficLogFile != NULL) 1083 fprintf(TrafficLogFile, "%05d <<< %s", (int) getpid(), buf); 1084 if (SevenBitInput) 1085 { 1086 for (p = buf; *p != '\0'; p++) 1087 *p &= ~0200; 1088 } 1089 else if (!HasEightBits) 1090 { 1091 for (p = buf; *p != '\0'; p++) 1092 { 1093 if (bitset(0200, *p)) 1094 { 1095 HasEightBits = TRUE; 1096 break; 1097 } 1098 } 1099 } 1100 return (buf); 1101 } 1102 1103 /* ARGSUSED */ 1104 static void 1105 readtimeout(timeout) 1106 time_t timeout; 1107 { 1108 longjmp(CtxReadTimeout, 1); 1109 } 1110 /* 1111 ** FGETFOLDED -- like fgets, but know about folded lines. 1112 ** 1113 ** Parameters: 1114 ** buf -- place to put result. 1115 ** n -- bytes available. 1116 ** f -- file to read from. 1117 ** 1118 ** Returns: 1119 ** input line(s) on success, NULL on error or EOF. 1120 ** This will normally be buf -- unless the line is too 1121 ** long, when it will be xalloc()ed. 1122 ** 1123 ** Side Effects: 1124 ** buf gets lines from f, with continuation lines (lines 1125 ** with leading white space) appended. CRLF's are mapped 1126 ** into single newlines. Any trailing NL is stripped. 1127 */ 1128 1129 char * 1130 fgetfolded(buf, n, f) 1131 char *buf; 1132 register int n; 1133 FILE *f; 1134 { 1135 register char *p = buf; 1136 char *bp = buf; 1137 register int i; 1138 1139 n--; 1140 while ((i = getc(f)) != EOF) 1141 { 1142 if (i == '\r') 1143 { 1144 i = getc(f); 1145 if (i != '\n') 1146 { 1147 if (i != EOF) 1148 (void) ungetc(i, f); 1149 i = '\r'; 1150 } 1151 } 1152 if (--n <= 0) 1153 { 1154 /* allocate new space */ 1155 char *nbp; 1156 int nn; 1157 1158 nn = (p - bp); 1159 if (nn < MEMCHUNKSIZE) 1160 nn *= 2; 1161 else 1162 nn += MEMCHUNKSIZE; 1163 nbp = xalloc(nn); 1164 bcopy(bp, nbp, p - bp); 1165 p = &nbp[p - bp]; 1166 if (bp != buf) 1167 free(bp); 1168 bp = nbp; 1169 n = nn - (p - bp); 1170 } 1171 *p++ = i; 1172 if (i == '\n') 1173 { 1174 LineNumber++; 1175 i = getc(f); 1176 if (i != EOF) 1177 (void) ungetc(i, f); 1178 if (i != ' ' && i != '\t') 1179 break; 1180 } 1181 } 1182 if (p == bp) 1183 return (NULL); 1184 if (p[-1] == '\n') 1185 p--; 1186 *p = '\0'; 1187 return (bp); 1188 } 1189 /* 1190 ** CURTIME -- return current time. 1191 ** 1192 ** Parameters: 1193 ** none. 1194 ** 1195 ** Returns: 1196 ** the current time. 1197 ** 1198 ** Side Effects: 1199 ** none. 1200 */ 1201 1202 time_t 1203 curtime() 1204 { 1205 auto time_t t; 1206 1207 (void) time(&t); 1208 return (t); 1209 } 1210 /* 1211 ** ATOBOOL -- convert a string representation to boolean. 1212 ** 1213 ** Defaults to "TRUE" 1214 ** 1215 ** Parameters: 1216 ** s -- string to convert. Takes "tTyY" as true, 1217 ** others as false. 1218 ** 1219 ** Returns: 1220 ** A boolean representation of the string. 1221 ** 1222 ** Side Effects: 1223 ** none. 1224 */ 1225 1226 bool 1227 atobool(s) 1228 register char *s; 1229 { 1230 if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) 1231 return (TRUE); 1232 return (FALSE); 1233 } 1234 /* 1235 ** ATOOCT -- convert a string representation to octal. 1236 ** 1237 ** Parameters: 1238 ** s -- string to convert. 1239 ** 1240 ** Returns: 1241 ** An integer representing the string interpreted as an 1242 ** octal number. 1243 ** 1244 ** Side Effects: 1245 ** none. 1246 */ 1247 1248 int 1249 atooct(s) 1250 register char *s; 1251 { 1252 register int i = 0; 1253 1254 while (*s >= '0' && *s <= '7') 1255 i = (i << 3) | (*s++ - '0'); 1256 return (i); 1257 } 1258 /* 1259 ** BITINTERSECT -- tell if two bitmaps intersect 1260 ** 1261 ** Parameters: 1262 ** a, b -- the bitmaps in question 1263 ** 1264 ** Returns: 1265 ** TRUE if they have a non-null intersection 1266 ** FALSE otherwise 1267 ** 1268 ** Side Effects: 1269 ** none. 1270 */ 1271 1272 bool 1273 bitintersect(a, b) 1274 BITMAP a; 1275 BITMAP b; 1276 { 1277 int i; 1278 1279 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1280 if ((a[i] & b[i]) != 0) 1281 return (TRUE); 1282 return (FALSE); 1283 } 1284 /* 1285 ** BITZEROP -- tell if a bitmap is all zero 1286 ** 1287 ** Parameters: 1288 ** map -- the bit map to check 1289 ** 1290 ** Returns: 1291 ** TRUE if map is all zero. 1292 ** FALSE if there are any bits set in map. 1293 ** 1294 ** Side Effects: 1295 ** none. 1296 */ 1297 1298 bool 1299 bitzerop(map) 1300 BITMAP map; 1301 { 1302 int i; 1303 1304 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1305 if (map[i] != 0) 1306 return (FALSE); 1307 return (TRUE); 1308 } 1309 /* 1310 ** STRCONTAINEDIN -- tell if one string is contained in another 1311 ** 1312 ** Parameters: 1313 ** a -- possible substring. 1314 ** b -- possible superstring. 1315 ** 1316 ** Returns: 1317 ** TRUE if a is contained in b. 1318 ** FALSE otherwise. 1319 */ 1320 1321 bool 1322 strcontainedin(a, b) 1323 register char *a; 1324 register char *b; 1325 { 1326 int la; 1327 int lb; 1328 int c; 1329 1330 la = strlen(a); 1331 lb = strlen(b); 1332 c = *a; 1333 if (isascii(c) && isupper(c)) 1334 c = tolower(c); 1335 for (; lb-- >= la; b++) 1336 { 1337 if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c) 1338 continue; 1339 if (strncasecmp(a, b, la) == 0) 1340 return TRUE; 1341 } 1342 return FALSE; 1343 } 1344 /* 1345 ** CHECKFD012 -- check low numbered file descriptors 1346 ** 1347 ** File descriptors 0, 1, and 2 should be open at all times. 1348 ** This routine verifies that, and fixes it if not true. 1349 ** 1350 ** Parameters: 1351 ** where -- a tag printed if the assertion failed 1352 ** 1353 ** Returns: 1354 ** none 1355 */ 1356 1357 void 1358 checkfd012(where) 1359 char *where; 1360 { 1361 #if XDEBUG 1362 register int i; 1363 1364 for (i = 0; i < 3; i++) 1365 fill_fd(i, where); 1366 #endif /* XDEBUG */ 1367 } 1368 /* 1369 ** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging 1370 ** 1371 ** Parameters: 1372 ** fd -- file descriptor to check. 1373 ** where -- tag to print on failure. 1374 ** 1375 ** Returns: 1376 ** none. 1377 */ 1378 1379 void 1380 checkfdopen(fd, where) 1381 int fd; 1382 char *where; 1383 { 1384 #if XDEBUG 1385 struct stat st; 1386 1387 if (fstat(fd, &st) < 0 && errno == EBADF) 1388 { 1389 syserr("checkfdopen(%d): %s not open as expected!", fd, where); 1390 printopenfds(TRUE); 1391 } 1392 #endif 1393 } 1394 /* 1395 ** CHECKFDS -- check for new or missing file descriptors 1396 ** 1397 ** Parameters: 1398 ** where -- tag for printing. If null, take a base line. 1399 ** 1400 ** Returns: 1401 ** none 1402 ** 1403 ** Side Effects: 1404 ** If where is set, shows changes since the last call. 1405 */ 1406 1407 void 1408 checkfds(where) 1409 char *where; 1410 { 1411 int maxfd; 1412 register int fd; 1413 bool printhdr = TRUE; 1414 int save_errno = errno; 1415 static BITMAP baseline; 1416 extern int DtableSize; 1417 1418 if (DtableSize > 256) 1419 maxfd = 256; 1420 else 1421 maxfd = DtableSize; 1422 if (where == NULL) 1423 clrbitmap(baseline); 1424 1425 for (fd = 0; fd < maxfd; fd++) 1426 { 1427 struct stat stbuf; 1428 1429 if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP) 1430 { 1431 if (!bitnset(fd, baseline)) 1432 continue; 1433 clrbitn(fd, baseline); 1434 } 1435 else if (!bitnset(fd, baseline)) 1436 setbitn(fd, baseline); 1437 else 1438 continue; 1439 1440 /* file state has changed */ 1441 if (where == NULL) 1442 continue; 1443 if (printhdr) 1444 { 1445 sm_syslog(LOG_DEBUG, CurEnv->e_id, 1446 "%s: changed fds:", 1447 where); 1448 printhdr = FALSE; 1449 } 1450 dumpfd(fd, TRUE, TRUE); 1451 } 1452 errno = save_errno; 1453 } 1454 /* 1455 ** PRINTOPENFDS -- print the open file descriptors (for debugging) 1456 ** 1457 ** Parameters: 1458 ** logit -- if set, send output to syslog; otherwise 1459 ** print for debugging. 1460 ** 1461 ** Returns: 1462 ** none. 1463 */ 1464 1465 #include <arpa/inet.h> 1466 1467 void 1468 printopenfds(logit) 1469 bool logit; 1470 { 1471 register int fd; 1472 extern int DtableSize; 1473 1474 for (fd = 0; fd < DtableSize; fd++) 1475 dumpfd(fd, FALSE, logit); 1476 } 1477 /* 1478 ** DUMPFD -- dump a file descriptor 1479 ** 1480 ** Parameters: 1481 ** fd -- the file descriptor to dump. 1482 ** printclosed -- if set, print a notification even if 1483 ** it is closed; otherwise print nothing. 1484 ** logit -- if set, send output to syslog instead of stdout. 1485 */ 1486 1487 void 1488 dumpfd(fd, printclosed, logit) 1489 int fd; 1490 bool printclosed; 1491 bool logit; 1492 { 1493 register char *p; 1494 char *hp; 1495 #ifdef S_IFSOCK 1496 SOCKADDR sa; 1497 #endif 1498 auto SOCKADDR_LEN_T slen; 1499 int i; 1500 #if STAT64 > 0 1501 struct stat64 st; 1502 #else 1503 struct stat st; 1504 #endif 1505 char buf[200]; 1506 1507 p = buf; 1508 snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); 1509 p += strlen(p); 1510 1511 if ( 1512 #if STAT64 > 0 1513 fstat64(fd, &st) 1514 #else 1515 fstat(fd, &st) 1516 #endif 1517 < 0) 1518 { 1519 if (errno != EBADF) 1520 { 1521 snprintf(p, SPACELEFT(buf, p), "CANNOT STAT (%s)", 1522 errstring(errno)); 1523 goto printit; 1524 } 1525 else if (printclosed) 1526 { 1527 snprintf(p, SPACELEFT(buf, p), "CLOSED"); 1528 goto printit; 1529 } 1530 return; 1531 } 1532 1533 i = fcntl(fd, F_GETFL, NULL); 1534 if (i != -1) 1535 { 1536 snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i); 1537 p += strlen(p); 1538 } 1539 1540 snprintf(p, SPACELEFT(buf, p), "mode=%o: ", st.st_mode); 1541 p += strlen(p); 1542 switch (st.st_mode & S_IFMT) 1543 { 1544 #ifdef S_IFSOCK 1545 case S_IFSOCK: 1546 snprintf(p, SPACELEFT(buf, p), "SOCK "); 1547 p += strlen(p); 1548 slen = sizeof sa; 1549 if (getsockname(fd, &sa.sa, &slen) < 0) 1550 snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno)); 1551 else 1552 { 1553 hp = hostnamebyanyaddr(&sa); 1554 if (sa.sa.sa_family == AF_INET) 1555 snprintf(p, SPACELEFT(buf, p), "%s/%d", 1556 hp, ntohs(sa.sin.sin_port)); 1557 else 1558 snprintf(p, SPACELEFT(buf, p), "%s", hp); 1559 } 1560 p += strlen(p); 1561 snprintf(p, SPACELEFT(buf, p), "->"); 1562 p += strlen(p); 1563 slen = sizeof sa; 1564 if (getpeername(fd, &sa.sa, &slen) < 0) 1565 snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno)); 1566 else 1567 { 1568 hp = hostnamebyanyaddr(&sa); 1569 if (sa.sa.sa_family == AF_INET) 1570 snprintf(p, SPACELEFT(buf, p), "%s/%d", 1571 hp, ntohs(sa.sin.sin_port)); 1572 else 1573 snprintf(p, SPACELEFT(buf, p), "%s", hp); 1574 } 1575 break; 1576 #endif 1577 1578 case S_IFCHR: 1579 snprintf(p, SPACELEFT(buf, p), "CHR: "); 1580 p += strlen(p); 1581 goto defprint; 1582 1583 case S_IFBLK: 1584 snprintf(p, SPACELEFT(buf, p), "BLK: "); 1585 p += strlen(p); 1586 goto defprint; 1587 1588 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) 1589 case S_IFIFO: 1590 snprintf(p, SPACELEFT(buf, p), "FIFO: "); 1591 p += strlen(p); 1592 goto defprint; 1593 #endif 1594 1595 #ifdef S_IFDIR 1596 case S_IFDIR: 1597 snprintf(p, SPACELEFT(buf, p), "DIR: "); 1598 p += strlen(p); 1599 goto defprint; 1600 #endif 1601 1602 #ifdef S_IFLNK 1603 case S_IFLNK: 1604 snprintf(p, SPACELEFT(buf, p), "LNK: "); 1605 p += strlen(p); 1606 goto defprint; 1607 #endif 1608 1609 default: 1610 defprint: 1611 if (sizeof st.st_ino > sizeof (long)) 1612 snprintf(p, SPACELEFT(buf, p), 1613 "dev=%d/%d, ino=%s, nlink=%d, u/gid=%d/%d, ", 1614 major(st.st_dev), minor(st.st_dev), 1615 quad_to_string(st.st_ino), 1616 st.st_nlink, st.st_uid, st.st_gid); 1617 else 1618 snprintf(p, SPACELEFT(buf, p), 1619 "dev=%d/%d, ino=%lu, nlink=%d, u/gid=%d/%d, ", 1620 major(st.st_dev), minor(st.st_dev), 1621 (unsigned long) st.st_ino, 1622 st.st_nlink, st.st_uid, st.st_gid); 1623 if (sizeof st.st_size > sizeof (long)) 1624 snprintf(p, SPACELEFT(buf, p), "size=%s", 1625 quad_to_string(st.st_size)); 1626 else 1627 snprintf(p, SPACELEFT(buf, p), "size=%lu", 1628 (unsigned long) st.st_size); 1629 break; 1630 } 1631 1632 printit: 1633 if (logit) 1634 sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL, 1635 "%.800s", buf); 1636 else 1637 printf("%s\n", buf); 1638 } 1639 /* 1640 ** SHORTEN_HOSTNAME -- strip local domain information off of hostname. 1641 ** 1642 ** Parameters: 1643 ** host -- the host to shorten (stripped in place). 1644 ** 1645 ** Returns: 1646 ** none. 1647 */ 1648 1649 void 1650 shorten_hostname(host) 1651 char host[]; 1652 { 1653 register char *p; 1654 char *mydom; 1655 int i; 1656 bool canon = FALSE; 1657 1658 /* strip off final dot */ 1659 p = &host[strlen(host) - 1]; 1660 if (*p == '.') 1661 { 1662 *p = '\0'; 1663 canon = TRUE; 1664 } 1665 1666 /* see if there is any domain at all -- if not, we are done */ 1667 p = strchr(host, '.'); 1668 if (p == NULL) 1669 return; 1670 1671 /* yes, we have a domain -- see if it looks like us */ 1672 mydom = macvalue('m', CurEnv); 1673 if (mydom == NULL) 1674 mydom = ""; 1675 i = strlen(++p); 1676 if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 && 1677 (mydom[i] == '.' || mydom[i] == '\0')) 1678 *--p = '\0'; 1679 } 1680 /* 1681 ** PROG_OPEN -- open a program for reading 1682 ** 1683 ** Parameters: 1684 ** argv -- the argument list. 1685 ** pfd -- pointer to a place to store the file descriptor. 1686 ** e -- the current envelope. 1687 ** 1688 ** Returns: 1689 ** pid of the process -- -1 if it failed. 1690 */ 1691 1692 int 1693 prog_open(argv, pfd, e) 1694 char **argv; 1695 int *pfd; 1696 ENVELOPE *e; 1697 { 1698 int pid; 1699 int i; 1700 int saveerrno; 1701 int fdv[2]; 1702 char *p, *q; 1703 char buf[MAXLINE + 1]; 1704 extern int DtableSize; 1705 1706 if (pipe(fdv) < 0) 1707 { 1708 syserr("%s: cannot create pipe for stdout", argv[0]); 1709 return -1; 1710 } 1711 pid = fork(); 1712 if (pid < 0) 1713 { 1714 syserr("%s: cannot fork", argv[0]); 1715 close(fdv[0]); 1716 close(fdv[1]); 1717 return -1; 1718 } 1719 if (pid > 0) 1720 { 1721 /* parent */ 1722 close(fdv[1]); 1723 *pfd = fdv[0]; 1724 return pid; 1725 } 1726 1727 /* child -- close stdin */ 1728 close(0); 1729 1730 /* stdout goes back to parent */ 1731 close(fdv[0]); 1732 if (dup2(fdv[1], 1) < 0) 1733 { 1734 syserr("%s: cannot dup2 for stdout", argv[0]); 1735 _exit(EX_OSERR); 1736 } 1737 close(fdv[1]); 1738 1739 /* stderr goes to transcript if available */ 1740 if (e->e_xfp != NULL) 1741 { 1742 if (dup2(fileno(e->e_xfp), 2) < 0) 1743 { 1744 syserr("%s: cannot dup2 for stderr", argv[0]); 1745 _exit(EX_OSERR); 1746 } 1747 } 1748 1749 /* this process has no right to the queue file */ 1750 if (e->e_lockfp != NULL) 1751 close(fileno(e->e_lockfp)); 1752 1753 /* run as default user */ 1754 endpwent(); 1755 if (setgid(DefGid) < 0 && geteuid() == 0) 1756 syserr("prog_open: setgid(%ld) failed", (long) DefGid); 1757 if (setuid(DefUid) < 0 && geteuid() == 0) 1758 syserr("prog_open: setuid(%ld) failed", (long) DefUid); 1759 1760 /* run in some directory */ 1761 if (ProgMailer != NULL) 1762 p = ProgMailer->m_execdir; 1763 else 1764 p = NULL; 1765 for (; p != NULL; p = q) 1766 { 1767 q = strchr(p, ':'); 1768 if (q != NULL) 1769 *q = '\0'; 1770 expand(p, buf, sizeof buf, e); 1771 if (q != NULL) 1772 *q++ = ':'; 1773 if (buf[0] != '\0' && chdir(buf) >= 0) 1774 break; 1775 } 1776 if (p == NULL) 1777 { 1778 /* backup directories */ 1779 if (chdir("/tmp") < 0) 1780 (void) chdir("/"); 1781 } 1782 1783 /* arrange for all the files to be closed */ 1784 for (i = 3; i < DtableSize; i++) 1785 { 1786 register int j; 1787 1788 if ((j = fcntl(i, F_GETFD, 0)) != -1) 1789 (void) fcntl(i, F_SETFD, j | 1); 1790 } 1791 1792 /* now exec the process */ 1793 execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron); 1794 1795 /* woops! failed */ 1796 saveerrno = errno; 1797 syserr("%s: cannot exec", argv[0]); 1798 if (transienterror(saveerrno)) 1799 _exit(EX_OSERR); 1800 _exit(EX_CONFIG); 1801 return -1; /* avoid compiler warning on IRIX */ 1802 } 1803 /* 1804 ** GET_COLUMN -- look up a Column in a line buffer 1805 ** 1806 ** Parameters: 1807 ** line -- the raw text line to search. 1808 ** col -- the column number to fetch. 1809 ** delim -- the delimiter between columns. If null, 1810 ** use white space. 1811 ** buf -- the output buffer. 1812 ** buflen -- the length of buf. 1813 ** 1814 ** Returns: 1815 ** buf if successful. 1816 ** NULL otherwise. 1817 */ 1818 1819 char * 1820 get_column(line, col, delim, buf, buflen) 1821 char line[]; 1822 int col; 1823 char delim; 1824 char buf[]; 1825 int buflen; 1826 { 1827 char *p; 1828 char *begin, *end; 1829 int i; 1830 char delimbuf[4]; 1831 1832 if (delim == '\0') 1833 strcpy(delimbuf, "\n\t "); 1834 else 1835 { 1836 delimbuf[0] = delim; 1837 delimbuf[1] = '\0'; 1838 } 1839 1840 p = line; 1841 if (*p == '\0') 1842 return NULL; /* line empty */ 1843 if (*p == delim && col == 0) 1844 return NULL; /* first column empty */ 1845 1846 begin = line; 1847 1848 if (col == 0 && delim == '\0') 1849 { 1850 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 1851 begin++; 1852 } 1853 1854 for (i = 0; i < col; i++) 1855 { 1856 if ((begin = strpbrk(begin, delimbuf)) == NULL) 1857 return NULL; /* no such column */ 1858 begin++; 1859 if (delim == '\0') 1860 { 1861 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 1862 begin++; 1863 } 1864 } 1865 1866 end = strpbrk(begin, delimbuf); 1867 if (end == NULL) 1868 i = strlen(begin); 1869 else 1870 i = end - begin; 1871 if (i >= buflen) 1872 i = buflen - 1; 1873 strncpy(buf, begin, i); 1874 buf[i] = '\0'; 1875 return buf; 1876 } 1877 /* 1878 ** CLEANSTRCPY -- copy string keeping out bogus characters 1879 ** 1880 ** Parameters: 1881 ** t -- "to" string. 1882 ** f -- "from" string. 1883 ** l -- length of space available in "to" string. 1884 ** 1885 ** Returns: 1886 ** none. 1887 */ 1888 1889 void 1890 cleanstrcpy(t, f, l) 1891 register char *t; 1892 register char *f; 1893 int l; 1894 { 1895 /* check for newlines and log if necessary */ 1896 (void) denlstring(f, TRUE, TRUE); 1897 1898 l--; 1899 while (l > 0 && *f != '\0') 1900 { 1901 if (isascii(*f) && 1902 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) 1903 { 1904 l--; 1905 *t++ = *f; 1906 } 1907 f++; 1908 } 1909 *t = '\0'; 1910 } 1911 /* 1912 ** DENLSTRING -- convert newlines in a string to spaces 1913 ** 1914 ** Parameters: 1915 ** s -- the input string 1916 ** strict -- if set, don't permit continuation lines. 1917 ** logattacks -- if set, log attempted attacks. 1918 ** 1919 ** Returns: 1920 ** A pointer to a version of the string with newlines 1921 ** mapped to spaces. This should be copied. 1922 */ 1923 1924 char * 1925 denlstring(s, strict, logattacks) 1926 char *s; 1927 bool strict; 1928 bool logattacks; 1929 { 1930 register char *p; 1931 int l; 1932 static char *bp = NULL; 1933 static int bl = 0; 1934 1935 p = s; 1936 while ((p = strchr(p, '\n')) != NULL) 1937 if (strict || (*++p != ' ' && *p != '\t')) 1938 break; 1939 if (p == NULL) 1940 return s; 1941 1942 l = strlen(s) + 1; 1943 if (bl < l) 1944 { 1945 /* allocate more space */ 1946 if (bp != NULL) 1947 free(bp); 1948 bp = xalloc(l); 1949 bl = l; 1950 } 1951 strcpy(bp, s); 1952 for (p = bp; (p = strchr(p, '\n')) != NULL; ) 1953 *p++ = ' '; 1954 1955 if (logattacks) 1956 { 1957 sm_syslog(LOG_NOTICE, CurEnv->e_id, 1958 "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", 1959 RealHostName == NULL ? "[UNKNOWN]" : RealHostName, 1960 shortenstring(bp, MAXSHORTSTR)); 1961 } 1962 1963 return bp; 1964 } 1965 /* 1966 ** PATH_IS_DIR -- check to see if file exists and is a directory. 1967 ** 1968 ** There are some additional checks for security violations in 1969 ** here. This routine is intended to be used for the host status 1970 ** support. 1971 ** 1972 ** Parameters: 1973 ** pathname -- pathname to check for directory-ness. 1974 ** createflag -- if set, create directory if needed. 1975 ** 1976 ** Returns: 1977 ** TRUE -- if the indicated pathname is a directory 1978 ** FALSE -- otherwise 1979 */ 1980 1981 int 1982 path_is_dir(pathname, createflag) 1983 char *pathname; 1984 bool createflag; 1985 { 1986 struct stat statbuf; 1987 1988 #if HASLSTAT 1989 if (lstat(pathname, &statbuf) < 0) 1990 #else 1991 if (stat(pathname, &statbuf) < 0) 1992 #endif 1993 { 1994 if (errno != ENOENT || !createflag) 1995 return FALSE; 1996 if (mkdir(pathname, 0755) < 0) 1997 return FALSE; 1998 return TRUE; 1999 } 2000 if (!S_ISDIR(statbuf.st_mode)) 2001 { 2002 errno = ENOTDIR; 2003 return FALSE; 2004 } 2005 2006 /* security: don't allow writable directories */ 2007 if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode)) 2008 { 2009 errno = EACCES; 2010 return FALSE; 2011 } 2012 2013 return TRUE; 2014 } 2015 /* 2016 ** PROC_LIST_ADD -- add process id to list of our children 2017 ** 2018 ** Parameters: 2019 ** pid -- pid to add to list. 2020 ** 2021 ** Returns: 2022 ** none 2023 */ 2024 2025 struct procs 2026 { 2027 pid_t proc_pid; 2028 char *proc_task; 2029 }; 2030 2031 static struct procs *ProcListVec = NULL; 2032 static int ProcListSize = 0; 2033 2034 #define NO_PID ((pid_t) 0) 2035 #ifndef PROC_LIST_SEG 2036 # define PROC_LIST_SEG 32 /* number of pids to alloc at a time */ 2037 #endif 2038 2039 void 2040 proc_list_add(pid, task) 2041 pid_t pid; 2042 char *task; 2043 { 2044 int i; 2045 2046 for (i = 0; i < ProcListSize; i++) 2047 { 2048 if (ProcListVec[i].proc_pid == NO_PID) 2049 break; 2050 } 2051 if (i >= ProcListSize) 2052 { 2053 /* probe the existing vector to avoid growing infinitely */ 2054 proc_list_probe(); 2055 2056 /* now scan again */ 2057 for (i = 0; i < ProcListSize; i++) 2058 { 2059 if (ProcListVec[i].proc_pid == NO_PID) 2060 break; 2061 } 2062 } 2063 if (i >= ProcListSize) 2064 { 2065 /* grow process list */ 2066 struct procs *npv; 2067 2068 npv = (struct procs *) xalloc(sizeof (struct procs) * (ProcListSize + PROC_LIST_SEG)); 2069 if (ProcListSize > 0) 2070 { 2071 bcopy(ProcListVec, npv, ProcListSize * 2072 sizeof (struct procs)); 2073 free(ProcListVec); 2074 } 2075 for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++) 2076 { 2077 npv[i].proc_pid = NO_PID; 2078 npv[i].proc_task = NULL; 2079 } 2080 i = ProcListSize; 2081 ProcListSize += PROC_LIST_SEG; 2082 ProcListVec = npv; 2083 } 2084 ProcListVec[i].proc_pid = pid; 2085 ProcListVec[i].proc_task = newstr(task); 2086 2087 /* if process adding itself, it's not a child */ 2088 if (pid != getpid()) 2089 CurChildren++; 2090 } 2091 /* 2092 ** PROC_LIST_SET -- set pid task in process list 2093 ** 2094 ** Parameters: 2095 ** pid -- pid to set 2096 ** task -- task of pid 2097 ** 2098 ** Returns: 2099 ** none. 2100 */ 2101 2102 void 2103 proc_list_set(pid, task) 2104 pid_t pid; 2105 char *task; 2106 { 2107 int i; 2108 2109 for (i = 0; i < ProcListSize; i++) 2110 { 2111 if (ProcListVec[i].proc_pid == pid) 2112 { 2113 if (ProcListVec[i].proc_task != NULL) 2114 free(ProcListVec[i].proc_task); 2115 ProcListVec[i].proc_task = newstr(task); 2116 break; 2117 } 2118 } 2119 } 2120 /* 2121 ** PROC_LIST_DROP -- drop pid from process list 2122 ** 2123 ** Parameters: 2124 ** pid -- pid to drop 2125 ** 2126 ** Returns: 2127 ** none. 2128 */ 2129 2130 void 2131 proc_list_drop(pid) 2132 pid_t pid; 2133 { 2134 int i; 2135 2136 for (i = 0; i < ProcListSize; i++) 2137 { 2138 if (ProcListVec[i].proc_pid == pid) 2139 { 2140 ProcListVec[i].proc_pid = NO_PID; 2141 if (ProcListVec[i].proc_task != NULL) 2142 { 2143 free(ProcListVec[i].proc_task); 2144 ProcListVec[i].proc_task = NULL; 2145 } 2146 break; 2147 } 2148 } 2149 if (CurChildren > 0) 2150 CurChildren--; 2151 } 2152 /* 2153 ** PROC_LIST_CLEAR -- clear the process list 2154 ** 2155 ** Parameters: 2156 ** none. 2157 ** 2158 ** Returns: 2159 ** none. 2160 */ 2161 2162 void 2163 proc_list_clear() 2164 { 2165 int i; 2166 2167 /* start from 1 since 0 is the daemon itself */ 2168 for (i = 1; i < ProcListSize; i++) 2169 { 2170 ProcListVec[i].proc_pid = NO_PID; 2171 if (ProcListVec[i].proc_task != NULL) 2172 { 2173 free(ProcListVec[i].proc_task); 2174 ProcListVec[i].proc_task = NULL; 2175 } 2176 } 2177 CurChildren = 0; 2178 } 2179 /* 2180 ** PROC_LIST_PROBE -- probe processes in the list to see if they still exist 2181 ** 2182 ** Parameters: 2183 ** none 2184 ** 2185 ** Returns: 2186 ** none 2187 */ 2188 2189 void 2190 proc_list_probe() 2191 { 2192 int i; 2193 2194 /* start from 1 since 0 is the daemon itself */ 2195 for (i = 1; i < ProcListSize; i++) 2196 { 2197 if (ProcListVec[i].proc_pid == NO_PID) 2198 continue; 2199 if (kill(ProcListVec[i].proc_pid, 0) < 0) 2200 { 2201 if (LogLevel > 3) 2202 sm_syslog(LOG_DEBUG, CurEnv->e_id, 2203 "proc_list_probe: lost pid %d", 2204 (int) ProcListVec[i].proc_pid); 2205 ProcListVec[i].proc_pid = NO_PID; 2206 if (ProcListVec[i].proc_task != NULL) 2207 { 2208 free(ProcListVec[i].proc_task); 2209 ProcListVec[i].proc_task = NULL; 2210 } 2211 CurChildren--; 2212 } 2213 } 2214 if (CurChildren < 0) 2215 CurChildren = 0; 2216 } 2217 /* 2218 ** PROC_LIST_DISPLAY -- display the process list 2219 ** 2220 ** Parameters: 2221 ** out -- output file pointer 2222 ** 2223 ** Returns: 2224 ** none. 2225 */ 2226 2227 void 2228 proc_list_display(out) 2229 FILE *out; 2230 { 2231 int i; 2232 2233 for (i = 0; i < ProcListSize; i++) 2234 { 2235 if (ProcListVec[i].proc_pid == NO_PID) 2236 continue; 2237 2238 fprintf(out, "%d %s%s\n", (int) ProcListVec[i].proc_pid, 2239 ProcListVec[i].proc_task != NULL ? 2240 ProcListVec[i].proc_task : "(unknown)", 2241 (OpMode == MD_SMTP || 2242 OpMode == MD_DAEMON || 2243 OpMode == MD_ARPAFTP) ? "\r" : ""); 2244 } 2245 } 2246 /* 2247 ** SM_STRCASECMP -- 8-bit clean version of strcasecmp 2248 ** 2249 ** Thank you, vendors, for making this all necessary. 2250 */ 2251 2252 /* 2253 * Copyright (c) 1987, 1993 2254 * The Regents of the University of California. All rights reserved. 2255 * 2256 * Redistribution and use in source and binary forms, with or without 2257 * modification, are permitted provided that the following conditions 2258 * are met: 2259 * 1. Redistributions of source code must retain the above copyright 2260 * notice, this list of conditions and the following disclaimer. 2261 * 2. Redistributions in binary form must reproduce the above copyright 2262 * notice, this list of conditions and the following disclaimer in the 2263 * documentation and/or other materials provided with the distribution. 2264 * 3. All advertising materials mentioning features or use of this software 2265 * must display the following acknowledgement: 2266 * This product includes software developed by the University of 2267 * California, Berkeley and its contributors. 2268 * 4. Neither the name of the University nor the names of its contributors 2269 * may be used to endorse or promote products derived from this software 2270 * without specific prior written permission. 2271 * 2272 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2273 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2274 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2275 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2276 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2277 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2278 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2279 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2280 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2281 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2282 * SUCH DAMAGE. 2283 */ 2284 2285 #if defined(LIBC_SCCS) && !defined(lint) 2286 static char sccsid[] = "@(#)strcasecmp.c 8.1 (Berkeley) 6/4/93"; 2287 #endif /* LIBC_SCCS and not lint */ 2288 2289 /* 2290 * This array is designed for mapping upper and lower case letter 2291 * together for a case independent comparison. The mappings are 2292 * based upon ascii character sequences. 2293 */ 2294 static const u_char charmap[] = { 2295 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, 2296 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, 2297 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027, 2298 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037, 2299 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, 2300 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, 2301 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, 2302 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, 2303 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147, 2304 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, 2305 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, 2306 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137, 2307 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147, 2308 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, 2309 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, 2310 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 2311 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, 2312 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, 2313 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, 2314 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, 2315 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, 2316 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, 2317 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, 2318 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, 2319 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, 2320 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, 2321 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, 2322 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, 2323 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, 2324 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, 2325 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, 2326 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377, 2327 }; 2328 2329 int 2330 sm_strcasecmp(s1, s2) 2331 const char *s1, *s2; 2332 { 2333 register const u_char *cm = charmap, 2334 *us1 = (const u_char *)s1, 2335 *us2 = (const u_char *)s2; 2336 2337 while (cm[*us1] == cm[*us2++]) 2338 if (*us1++ == '\0') 2339 return (0); 2340 return (cm[*us1] - cm[*--us2]); 2341 } 2342 2343 int 2344 sm_strncasecmp(s1, s2, n) 2345 const char *s1, *s2; 2346 register size_t n; 2347 { 2348 if (n != 0) { 2349 register const u_char *cm = charmap, 2350 *us1 = (const u_char *)s1, 2351 *us2 = (const u_char *)s2; 2352 2353 do { 2354 if (cm[*us1] != cm[*us2++]) 2355 return (cm[*us1] - cm[*--us2]); 2356 if (*us1++ == '\0') 2357 break; 2358 } while (--n != 0); 2359 } 2360 return (0); 2361 } 2362