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.168 (Berkeley) 1/21/1999"; 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 int save_errno; 1030 1031 if (fp == NULL) 1032 { 1033 buf[0] = '\0'; 1034 return NULL; 1035 } 1036 1037 /* set the timeout */ 1038 if (timeout != 0) 1039 { 1040 if (setjmp(CtxReadTimeout) != 0) 1041 { 1042 if (LogLevel > 1) 1043 sm_syslog(LOG_NOTICE, CurEnv->e_id, 1044 "timeout waiting for input from %.100s during %s", 1045 CurHostName ? CurHostName : "local", 1046 during); 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 errno = 0; 1055 return (NULL); 1056 } 1057 ev = setevent(timeout, readtimeout, 0); 1058 } 1059 1060 /* try to read */ 1061 p = NULL; 1062 errno = 0; 1063 while (!feof(fp) && !ferror(fp)) 1064 { 1065 errno = 0; 1066 p = fgets(buf, siz, fp); 1067 if (p != NULL || errno != EINTR) 1068 break; 1069 clearerr(fp); 1070 } 1071 save_errno = errno; 1072 1073 /* clear the event if it has not sprung */ 1074 clrevent(ev); 1075 1076 /* clean up the books and exit */ 1077 LineNumber++; 1078 if (p == NULL) 1079 { 1080 buf[0] = '\0'; 1081 if (TrafficLogFile != NULL) 1082 fprintf(TrafficLogFile, "%05d <<< [EOF]\n", (int) getpid()); 1083 errno = save_errno; 1084 return (NULL); 1085 } 1086 if (TrafficLogFile != NULL) 1087 fprintf(TrafficLogFile, "%05d <<< %s", (int) getpid(), buf); 1088 if (SevenBitInput) 1089 { 1090 for (p = buf; *p != '\0'; p++) 1091 *p &= ~0200; 1092 } 1093 else if (!HasEightBits) 1094 { 1095 for (p = buf; *p != '\0'; p++) 1096 { 1097 if (bitset(0200, *p)) 1098 { 1099 HasEightBits = TRUE; 1100 break; 1101 } 1102 } 1103 } 1104 return (buf); 1105 } 1106 1107 /* ARGSUSED */ 1108 static void 1109 readtimeout(timeout) 1110 time_t timeout; 1111 { 1112 longjmp(CtxReadTimeout, 1); 1113 } 1114 /* 1115 ** FGETFOLDED -- like fgets, but know about folded lines. 1116 ** 1117 ** Parameters: 1118 ** buf -- place to put result. 1119 ** n -- bytes available. 1120 ** f -- file to read from. 1121 ** 1122 ** Returns: 1123 ** input line(s) on success, NULL on error or EOF. 1124 ** This will normally be buf -- unless the line is too 1125 ** long, when it will be xalloc()ed. 1126 ** 1127 ** Side Effects: 1128 ** buf gets lines from f, with continuation lines (lines 1129 ** with leading white space) appended. CRLF's are mapped 1130 ** into single newlines. Any trailing NL is stripped. 1131 */ 1132 1133 char * 1134 fgetfolded(buf, n, f) 1135 char *buf; 1136 register int n; 1137 FILE *f; 1138 { 1139 register char *p = buf; 1140 char *bp = buf; 1141 register int i; 1142 1143 n--; 1144 while ((i = getc(f)) != EOF) 1145 { 1146 if (i == '\r') 1147 { 1148 i = getc(f); 1149 if (i != '\n') 1150 { 1151 if (i != EOF) 1152 (void) ungetc(i, f); 1153 i = '\r'; 1154 } 1155 } 1156 if (--n <= 0) 1157 { 1158 /* allocate new space */ 1159 char *nbp; 1160 int nn; 1161 1162 nn = (p - bp); 1163 if (nn < MEMCHUNKSIZE) 1164 nn *= 2; 1165 else 1166 nn += MEMCHUNKSIZE; 1167 nbp = xalloc(nn); 1168 bcopy(bp, nbp, p - bp); 1169 p = &nbp[p - bp]; 1170 if (bp != buf) 1171 free(bp); 1172 bp = nbp; 1173 n = nn - (p - bp); 1174 } 1175 *p++ = i; 1176 if (i == '\n') 1177 { 1178 LineNumber++; 1179 i = getc(f); 1180 if (i != EOF) 1181 (void) ungetc(i, f); 1182 if (i != ' ' && i != '\t') 1183 break; 1184 } 1185 } 1186 if (p == bp) 1187 return (NULL); 1188 if (p[-1] == '\n') 1189 p--; 1190 *p = '\0'; 1191 return (bp); 1192 } 1193 /* 1194 ** CURTIME -- return current time. 1195 ** 1196 ** Parameters: 1197 ** none. 1198 ** 1199 ** Returns: 1200 ** the current time. 1201 ** 1202 ** Side Effects: 1203 ** none. 1204 */ 1205 1206 time_t 1207 curtime() 1208 { 1209 auto time_t t; 1210 1211 (void) time(&t); 1212 return (t); 1213 } 1214 /* 1215 ** ATOBOOL -- convert a string representation to boolean. 1216 ** 1217 ** Defaults to "TRUE" 1218 ** 1219 ** Parameters: 1220 ** s -- string to convert. Takes "tTyY" as true, 1221 ** others as false. 1222 ** 1223 ** Returns: 1224 ** A boolean representation of the string. 1225 ** 1226 ** Side Effects: 1227 ** none. 1228 */ 1229 1230 bool 1231 atobool(s) 1232 register char *s; 1233 { 1234 if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) 1235 return (TRUE); 1236 return (FALSE); 1237 } 1238 /* 1239 ** ATOOCT -- convert a string representation to octal. 1240 ** 1241 ** Parameters: 1242 ** s -- string to convert. 1243 ** 1244 ** Returns: 1245 ** An integer representing the string interpreted as an 1246 ** octal number. 1247 ** 1248 ** Side Effects: 1249 ** none. 1250 */ 1251 1252 int 1253 atooct(s) 1254 register char *s; 1255 { 1256 register int i = 0; 1257 1258 while (*s >= '0' && *s <= '7') 1259 i = (i << 3) | (*s++ - '0'); 1260 return (i); 1261 } 1262 /* 1263 ** BITINTERSECT -- tell if two bitmaps intersect 1264 ** 1265 ** Parameters: 1266 ** a, b -- the bitmaps in question 1267 ** 1268 ** Returns: 1269 ** TRUE if they have a non-null intersection 1270 ** FALSE otherwise 1271 ** 1272 ** Side Effects: 1273 ** none. 1274 */ 1275 1276 bool 1277 bitintersect(a, b) 1278 BITMAP a; 1279 BITMAP b; 1280 { 1281 int i; 1282 1283 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1284 if ((a[i] & b[i]) != 0) 1285 return (TRUE); 1286 return (FALSE); 1287 } 1288 /* 1289 ** BITZEROP -- tell if a bitmap is all zero 1290 ** 1291 ** Parameters: 1292 ** map -- the bit map to check 1293 ** 1294 ** Returns: 1295 ** TRUE if map is all zero. 1296 ** FALSE if there are any bits set in map. 1297 ** 1298 ** Side Effects: 1299 ** none. 1300 */ 1301 1302 bool 1303 bitzerop(map) 1304 BITMAP map; 1305 { 1306 int i; 1307 1308 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1309 if (map[i] != 0) 1310 return (FALSE); 1311 return (TRUE); 1312 } 1313 /* 1314 ** STRCONTAINEDIN -- tell if one string is contained in another 1315 ** 1316 ** Parameters: 1317 ** a -- possible substring. 1318 ** b -- possible superstring. 1319 ** 1320 ** Returns: 1321 ** TRUE if a is contained in b. 1322 ** FALSE otherwise. 1323 */ 1324 1325 bool 1326 strcontainedin(a, b) 1327 register char *a; 1328 register char *b; 1329 { 1330 int la; 1331 int lb; 1332 int c; 1333 1334 la = strlen(a); 1335 lb = strlen(b); 1336 c = *a; 1337 if (isascii(c) && isupper(c)) 1338 c = tolower(c); 1339 for (; lb-- >= la; b++) 1340 { 1341 if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c) 1342 continue; 1343 if (strncasecmp(a, b, la) == 0) 1344 return TRUE; 1345 } 1346 return FALSE; 1347 } 1348 /* 1349 ** CHECKFD012 -- check low numbered file descriptors 1350 ** 1351 ** File descriptors 0, 1, and 2 should be open at all times. 1352 ** This routine verifies that, and fixes it if not true. 1353 ** 1354 ** Parameters: 1355 ** where -- a tag printed if the assertion failed 1356 ** 1357 ** Returns: 1358 ** none 1359 */ 1360 1361 void 1362 checkfd012(where) 1363 char *where; 1364 { 1365 #if XDEBUG 1366 register int i; 1367 1368 for (i = 0; i < 3; i++) 1369 fill_fd(i, where); 1370 #endif /* XDEBUG */ 1371 } 1372 /* 1373 ** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging 1374 ** 1375 ** Parameters: 1376 ** fd -- file descriptor to check. 1377 ** where -- tag to print on failure. 1378 ** 1379 ** Returns: 1380 ** none. 1381 */ 1382 1383 void 1384 checkfdopen(fd, where) 1385 int fd; 1386 char *where; 1387 { 1388 #if XDEBUG 1389 struct stat st; 1390 1391 if (fstat(fd, &st) < 0 && errno == EBADF) 1392 { 1393 syserr("checkfdopen(%d): %s not open as expected!", fd, where); 1394 printopenfds(TRUE); 1395 } 1396 #endif 1397 } 1398 /* 1399 ** CHECKFDS -- check for new or missing file descriptors 1400 ** 1401 ** Parameters: 1402 ** where -- tag for printing. If null, take a base line. 1403 ** 1404 ** Returns: 1405 ** none 1406 ** 1407 ** Side Effects: 1408 ** If where is set, shows changes since the last call. 1409 */ 1410 1411 void 1412 checkfds(where) 1413 char *where; 1414 { 1415 int maxfd; 1416 register int fd; 1417 bool printhdr = TRUE; 1418 int save_errno = errno; 1419 static BITMAP baseline; 1420 extern int DtableSize; 1421 1422 if (DtableSize > 256) 1423 maxfd = 256; 1424 else 1425 maxfd = DtableSize; 1426 if (where == NULL) 1427 clrbitmap(baseline); 1428 1429 for (fd = 0; fd < maxfd; fd++) 1430 { 1431 struct stat stbuf; 1432 1433 if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP) 1434 { 1435 if (!bitnset(fd, baseline)) 1436 continue; 1437 clrbitn(fd, baseline); 1438 } 1439 else if (!bitnset(fd, baseline)) 1440 setbitn(fd, baseline); 1441 else 1442 continue; 1443 1444 /* file state has changed */ 1445 if (where == NULL) 1446 continue; 1447 if (printhdr) 1448 { 1449 sm_syslog(LOG_DEBUG, CurEnv->e_id, 1450 "%s: changed fds:", 1451 where); 1452 printhdr = FALSE; 1453 } 1454 dumpfd(fd, TRUE, TRUE); 1455 } 1456 errno = save_errno; 1457 } 1458 /* 1459 ** PRINTOPENFDS -- print the open file descriptors (for debugging) 1460 ** 1461 ** Parameters: 1462 ** logit -- if set, send output to syslog; otherwise 1463 ** print for debugging. 1464 ** 1465 ** Returns: 1466 ** none. 1467 */ 1468 1469 #include <arpa/inet.h> 1470 1471 void 1472 printopenfds(logit) 1473 bool logit; 1474 { 1475 register int fd; 1476 extern int DtableSize; 1477 1478 for (fd = 0; fd < DtableSize; fd++) 1479 dumpfd(fd, FALSE, logit); 1480 } 1481 /* 1482 ** DUMPFD -- dump a file descriptor 1483 ** 1484 ** Parameters: 1485 ** fd -- the file descriptor to dump. 1486 ** printclosed -- if set, print a notification even if 1487 ** it is closed; otherwise print nothing. 1488 ** logit -- if set, send output to syslog instead of stdout. 1489 */ 1490 1491 void 1492 dumpfd(fd, printclosed, logit) 1493 int fd; 1494 bool printclosed; 1495 bool logit; 1496 { 1497 register char *p; 1498 char *hp; 1499 #ifdef S_IFSOCK 1500 SOCKADDR sa; 1501 #endif 1502 auto SOCKADDR_LEN_T slen; 1503 int i; 1504 #if STAT64 > 0 1505 struct stat64 st; 1506 #else 1507 struct stat st; 1508 #endif 1509 char buf[200]; 1510 1511 p = buf; 1512 snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); 1513 p += strlen(p); 1514 1515 if ( 1516 #if STAT64 > 0 1517 fstat64(fd, &st) 1518 #else 1519 fstat(fd, &st) 1520 #endif 1521 < 0) 1522 { 1523 if (errno != EBADF) 1524 { 1525 snprintf(p, SPACELEFT(buf, p), "CANNOT STAT (%s)", 1526 errstring(errno)); 1527 goto printit; 1528 } 1529 else if (printclosed) 1530 { 1531 snprintf(p, SPACELEFT(buf, p), "CLOSED"); 1532 goto printit; 1533 } 1534 return; 1535 } 1536 1537 i = fcntl(fd, F_GETFL, NULL); 1538 if (i != -1) 1539 { 1540 snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i); 1541 p += strlen(p); 1542 } 1543 1544 snprintf(p, SPACELEFT(buf, p), "mode=%o: ", st.st_mode); 1545 p += strlen(p); 1546 switch (st.st_mode & S_IFMT) 1547 { 1548 #ifdef S_IFSOCK 1549 case S_IFSOCK: 1550 snprintf(p, SPACELEFT(buf, p), "SOCK "); 1551 p += strlen(p); 1552 slen = sizeof sa; 1553 if (getsockname(fd, &sa.sa, &slen) < 0) 1554 snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno)); 1555 else 1556 { 1557 hp = hostnamebyanyaddr(&sa); 1558 if (sa.sa.sa_family == AF_INET) 1559 snprintf(p, SPACELEFT(buf, p), "%s/%d", 1560 hp, ntohs(sa.sin.sin_port)); 1561 else 1562 snprintf(p, SPACELEFT(buf, p), "%s", hp); 1563 } 1564 p += strlen(p); 1565 snprintf(p, SPACELEFT(buf, p), "->"); 1566 p += strlen(p); 1567 slen = sizeof sa; 1568 if (getpeername(fd, &sa.sa, &slen) < 0) 1569 snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno)); 1570 else 1571 { 1572 hp = hostnamebyanyaddr(&sa); 1573 if (sa.sa.sa_family == AF_INET) 1574 snprintf(p, SPACELEFT(buf, p), "%s/%d", 1575 hp, ntohs(sa.sin.sin_port)); 1576 else 1577 snprintf(p, SPACELEFT(buf, p), "%s", hp); 1578 } 1579 break; 1580 #endif 1581 1582 case S_IFCHR: 1583 snprintf(p, SPACELEFT(buf, p), "CHR: "); 1584 p += strlen(p); 1585 goto defprint; 1586 1587 case S_IFBLK: 1588 snprintf(p, SPACELEFT(buf, p), "BLK: "); 1589 p += strlen(p); 1590 goto defprint; 1591 1592 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) 1593 case S_IFIFO: 1594 snprintf(p, SPACELEFT(buf, p), "FIFO: "); 1595 p += strlen(p); 1596 goto defprint; 1597 #endif 1598 1599 #ifdef S_IFDIR 1600 case S_IFDIR: 1601 snprintf(p, SPACELEFT(buf, p), "DIR: "); 1602 p += strlen(p); 1603 goto defprint; 1604 #endif 1605 1606 #ifdef S_IFLNK 1607 case S_IFLNK: 1608 snprintf(p, SPACELEFT(buf, p), "LNK: "); 1609 p += strlen(p); 1610 goto defprint; 1611 #endif 1612 1613 default: 1614 defprint: 1615 if (sizeof st.st_ino > sizeof (long)) 1616 snprintf(p, SPACELEFT(buf, p), 1617 "dev=%d/%d, ino=%s, nlink=%d, u/gid=%d/%d, ", 1618 major(st.st_dev), minor(st.st_dev), 1619 quad_to_string(st.st_ino), 1620 st.st_nlink, st.st_uid, st.st_gid); 1621 else 1622 snprintf(p, SPACELEFT(buf, p), 1623 "dev=%d/%d, ino=%lu, nlink=%d, u/gid=%d/%d, ", 1624 major(st.st_dev), minor(st.st_dev), 1625 (unsigned long) st.st_ino, 1626 st.st_nlink, st.st_uid, st.st_gid); 1627 if (sizeof st.st_size > sizeof (long)) 1628 snprintf(p, SPACELEFT(buf, p), "size=%s", 1629 quad_to_string(st.st_size)); 1630 else 1631 snprintf(p, SPACELEFT(buf, p), "size=%lu", 1632 (unsigned long) st.st_size); 1633 break; 1634 } 1635 1636 printit: 1637 if (logit) 1638 sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL, 1639 "%.800s", buf); 1640 else 1641 printf("%s\n", buf); 1642 } 1643 /* 1644 ** SHORTEN_HOSTNAME -- strip local domain information off of hostname. 1645 ** 1646 ** Parameters: 1647 ** host -- the host to shorten (stripped in place). 1648 ** 1649 ** Returns: 1650 ** none. 1651 */ 1652 1653 void 1654 shorten_hostname(host) 1655 char host[]; 1656 { 1657 register char *p; 1658 char *mydom; 1659 int i; 1660 bool canon = FALSE; 1661 1662 /* strip off final dot */ 1663 p = &host[strlen(host) - 1]; 1664 if (*p == '.') 1665 { 1666 *p = '\0'; 1667 canon = TRUE; 1668 } 1669 1670 /* see if there is any domain at all -- if not, we are done */ 1671 p = strchr(host, '.'); 1672 if (p == NULL) 1673 return; 1674 1675 /* yes, we have a domain -- see if it looks like us */ 1676 mydom = macvalue('m', CurEnv); 1677 if (mydom == NULL) 1678 mydom = ""; 1679 i = strlen(++p); 1680 if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 && 1681 (mydom[i] == '.' || mydom[i] == '\0')) 1682 *--p = '\0'; 1683 } 1684 /* 1685 ** PROG_OPEN -- open a program for reading 1686 ** 1687 ** Parameters: 1688 ** argv -- the argument list. 1689 ** pfd -- pointer to a place to store the file descriptor. 1690 ** e -- the current envelope. 1691 ** 1692 ** Returns: 1693 ** pid of the process -- -1 if it failed. 1694 */ 1695 1696 int 1697 prog_open(argv, pfd, e) 1698 char **argv; 1699 int *pfd; 1700 ENVELOPE *e; 1701 { 1702 int pid; 1703 int i; 1704 int saveerrno; 1705 int fdv[2]; 1706 char *p, *q; 1707 char buf[MAXLINE + 1]; 1708 extern int DtableSize; 1709 1710 if (pipe(fdv) < 0) 1711 { 1712 syserr("%s: cannot create pipe for stdout", argv[0]); 1713 return -1; 1714 } 1715 pid = fork(); 1716 if (pid < 0) 1717 { 1718 syserr("%s: cannot fork", argv[0]); 1719 close(fdv[0]); 1720 close(fdv[1]); 1721 return -1; 1722 } 1723 if (pid > 0) 1724 { 1725 /* parent */ 1726 close(fdv[1]); 1727 *pfd = fdv[0]; 1728 return pid; 1729 } 1730 1731 /* child -- close stdin */ 1732 close(0); 1733 1734 /* stdout goes back to parent */ 1735 close(fdv[0]); 1736 if (dup2(fdv[1], 1) < 0) 1737 { 1738 syserr("%s: cannot dup2 for stdout", argv[0]); 1739 _exit(EX_OSERR); 1740 } 1741 close(fdv[1]); 1742 1743 /* stderr goes to transcript if available */ 1744 if (e->e_xfp != NULL) 1745 { 1746 if (dup2(fileno(e->e_xfp), 2) < 0) 1747 { 1748 syserr("%s: cannot dup2 for stderr", argv[0]); 1749 _exit(EX_OSERR); 1750 } 1751 } 1752 1753 /* this process has no right to the queue file */ 1754 if (e->e_lockfp != NULL) 1755 close(fileno(e->e_lockfp)); 1756 1757 /* run as default user */ 1758 endpwent(); 1759 if (setgid(DefGid) < 0 && geteuid() == 0) 1760 syserr("prog_open: setgid(%ld) failed", (long) DefGid); 1761 if (setuid(DefUid) < 0 && geteuid() == 0) 1762 syserr("prog_open: setuid(%ld) failed", (long) DefUid); 1763 1764 /* run in some directory */ 1765 if (ProgMailer != NULL) 1766 p = ProgMailer->m_execdir; 1767 else 1768 p = NULL; 1769 for (; p != NULL; p = q) 1770 { 1771 q = strchr(p, ':'); 1772 if (q != NULL) 1773 *q = '\0'; 1774 expand(p, buf, sizeof buf, e); 1775 if (q != NULL) 1776 *q++ = ':'; 1777 if (buf[0] != '\0' && chdir(buf) >= 0) 1778 break; 1779 } 1780 if (p == NULL) 1781 { 1782 /* backup directories */ 1783 if (chdir("/tmp") < 0) 1784 (void) chdir("/"); 1785 } 1786 1787 /* arrange for all the files to be closed */ 1788 for (i = 3; i < DtableSize; i++) 1789 { 1790 register int j; 1791 1792 if ((j = fcntl(i, F_GETFD, 0)) != -1) 1793 (void) fcntl(i, F_SETFD, j | 1); 1794 } 1795 1796 /* now exec the process */ 1797 execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron); 1798 1799 /* woops! failed */ 1800 saveerrno = errno; 1801 syserr("%s: cannot exec", argv[0]); 1802 if (transienterror(saveerrno)) 1803 _exit(EX_OSERR); 1804 _exit(EX_CONFIG); 1805 return -1; /* avoid compiler warning on IRIX */ 1806 } 1807 /* 1808 ** GET_COLUMN -- look up a Column in a line buffer 1809 ** 1810 ** Parameters: 1811 ** line -- the raw text line to search. 1812 ** col -- the column number to fetch. 1813 ** delim -- the delimiter between columns. If null, 1814 ** use white space. 1815 ** buf -- the output buffer. 1816 ** buflen -- the length of buf. 1817 ** 1818 ** Returns: 1819 ** buf if successful. 1820 ** NULL otherwise. 1821 */ 1822 1823 char * 1824 get_column(line, col, delim, buf, buflen) 1825 char line[]; 1826 int col; 1827 char delim; 1828 char buf[]; 1829 int buflen; 1830 { 1831 char *p; 1832 char *begin, *end; 1833 int i; 1834 char delimbuf[4]; 1835 1836 if (delim == '\0') 1837 strcpy(delimbuf, "\n\t "); 1838 else 1839 { 1840 delimbuf[0] = delim; 1841 delimbuf[1] = '\0'; 1842 } 1843 1844 p = line; 1845 if (*p == '\0') 1846 return NULL; /* line empty */ 1847 if (*p == delim && col == 0) 1848 return NULL; /* first column empty */ 1849 1850 begin = line; 1851 1852 if (col == 0 && delim == '\0') 1853 { 1854 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 1855 begin++; 1856 } 1857 1858 for (i = 0; i < col; i++) 1859 { 1860 if ((begin = strpbrk(begin, delimbuf)) == NULL) 1861 return NULL; /* no such column */ 1862 begin++; 1863 if (delim == '\0') 1864 { 1865 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 1866 begin++; 1867 } 1868 } 1869 1870 end = strpbrk(begin, delimbuf); 1871 if (end == NULL) 1872 i = strlen(begin); 1873 else 1874 i = end - begin; 1875 if (i >= buflen) 1876 i = buflen - 1; 1877 strncpy(buf, begin, i); 1878 buf[i] = '\0'; 1879 return buf; 1880 } 1881 /* 1882 ** CLEANSTRCPY -- copy string keeping out bogus characters 1883 ** 1884 ** Parameters: 1885 ** t -- "to" string. 1886 ** f -- "from" string. 1887 ** l -- length of space available in "to" string. 1888 ** 1889 ** Returns: 1890 ** none. 1891 */ 1892 1893 void 1894 cleanstrcpy(t, f, l) 1895 register char *t; 1896 register char *f; 1897 int l; 1898 { 1899 /* check for newlines and log if necessary */ 1900 (void) denlstring(f, TRUE, TRUE); 1901 1902 l--; 1903 while (l > 0 && *f != '\0') 1904 { 1905 if (isascii(*f) && 1906 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) 1907 { 1908 l--; 1909 *t++ = *f; 1910 } 1911 f++; 1912 } 1913 *t = '\0'; 1914 } 1915 /* 1916 ** DENLSTRING -- convert newlines in a string to spaces 1917 ** 1918 ** Parameters: 1919 ** s -- the input string 1920 ** strict -- if set, don't permit continuation lines. 1921 ** logattacks -- if set, log attempted attacks. 1922 ** 1923 ** Returns: 1924 ** A pointer to a version of the string with newlines 1925 ** mapped to spaces. This should be copied. 1926 */ 1927 1928 char * 1929 denlstring(s, strict, logattacks) 1930 char *s; 1931 bool strict; 1932 bool logattacks; 1933 { 1934 register char *p; 1935 int l; 1936 static char *bp = NULL; 1937 static int bl = 0; 1938 1939 p = s; 1940 while ((p = strchr(p, '\n')) != NULL) 1941 if (strict || (*++p != ' ' && *p != '\t')) 1942 break; 1943 if (p == NULL) 1944 return s; 1945 1946 l = strlen(s) + 1; 1947 if (bl < l) 1948 { 1949 /* allocate more space */ 1950 if (bp != NULL) 1951 free(bp); 1952 bp = xalloc(l); 1953 bl = l; 1954 } 1955 strcpy(bp, s); 1956 for (p = bp; (p = strchr(p, '\n')) != NULL; ) 1957 *p++ = ' '; 1958 1959 if (logattacks) 1960 { 1961 sm_syslog(LOG_NOTICE, CurEnv->e_id, 1962 "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", 1963 RealHostName == NULL ? "[UNKNOWN]" : RealHostName, 1964 shortenstring(bp, MAXSHORTSTR)); 1965 } 1966 1967 return bp; 1968 } 1969 /* 1970 ** PATH_IS_DIR -- check to see if file exists and is a directory. 1971 ** 1972 ** There are some additional checks for security violations in 1973 ** here. This routine is intended to be used for the host status 1974 ** support. 1975 ** 1976 ** Parameters: 1977 ** pathname -- pathname to check for directory-ness. 1978 ** createflag -- if set, create directory if needed. 1979 ** 1980 ** Returns: 1981 ** TRUE -- if the indicated pathname is a directory 1982 ** FALSE -- otherwise 1983 */ 1984 1985 int 1986 path_is_dir(pathname, createflag) 1987 char *pathname; 1988 bool createflag; 1989 { 1990 struct stat statbuf; 1991 1992 #if HASLSTAT 1993 if (lstat(pathname, &statbuf) < 0) 1994 #else 1995 if (stat(pathname, &statbuf) < 0) 1996 #endif 1997 { 1998 if (errno != ENOENT || !createflag) 1999 return FALSE; 2000 if (mkdir(pathname, 0755) < 0) 2001 return FALSE; 2002 return TRUE; 2003 } 2004 if (!S_ISDIR(statbuf.st_mode)) 2005 { 2006 errno = ENOTDIR; 2007 return FALSE; 2008 } 2009 2010 /* security: don't allow writable directories */ 2011 if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode)) 2012 { 2013 errno = EACCES; 2014 return FALSE; 2015 } 2016 2017 return TRUE; 2018 } 2019 /* 2020 ** PROC_LIST_ADD -- add process id to list of our children 2021 ** 2022 ** Parameters: 2023 ** pid -- pid to add to list. 2024 ** 2025 ** Returns: 2026 ** none 2027 */ 2028 2029 struct procs 2030 { 2031 pid_t proc_pid; 2032 char *proc_task; 2033 }; 2034 2035 static struct procs *ProcListVec = NULL; 2036 static int ProcListSize = 0; 2037 2038 #define NO_PID ((pid_t) 0) 2039 #ifndef PROC_LIST_SEG 2040 # define PROC_LIST_SEG 32 /* number of pids to alloc at a time */ 2041 #endif 2042 2043 void 2044 proc_list_add(pid, task) 2045 pid_t pid; 2046 char *task; 2047 { 2048 int i; 2049 2050 for (i = 0; i < ProcListSize; i++) 2051 { 2052 if (ProcListVec[i].proc_pid == NO_PID) 2053 break; 2054 } 2055 if (i >= ProcListSize) 2056 { 2057 /* probe the existing vector to avoid growing infinitely */ 2058 proc_list_probe(); 2059 2060 /* now scan again */ 2061 for (i = 0; i < ProcListSize; i++) 2062 { 2063 if (ProcListVec[i].proc_pid == NO_PID) 2064 break; 2065 } 2066 } 2067 if (i >= ProcListSize) 2068 { 2069 /* grow process list */ 2070 struct procs *npv; 2071 2072 npv = (struct procs *) xalloc(sizeof (struct procs) * (ProcListSize + PROC_LIST_SEG)); 2073 if (ProcListSize > 0) 2074 { 2075 bcopy(ProcListVec, npv, ProcListSize * 2076 sizeof (struct procs)); 2077 free(ProcListVec); 2078 } 2079 for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++) 2080 { 2081 npv[i].proc_pid = NO_PID; 2082 npv[i].proc_task = NULL; 2083 } 2084 i = ProcListSize; 2085 ProcListSize += PROC_LIST_SEG; 2086 ProcListVec = npv; 2087 } 2088 ProcListVec[i].proc_pid = pid; 2089 ProcListVec[i].proc_task = newstr(task); 2090 2091 /* if process adding itself, it's not a child */ 2092 if (pid != getpid()) 2093 CurChildren++; 2094 } 2095 /* 2096 ** PROC_LIST_SET -- set pid task in process list 2097 ** 2098 ** Parameters: 2099 ** pid -- pid to set 2100 ** task -- task of pid 2101 ** 2102 ** Returns: 2103 ** none. 2104 */ 2105 2106 void 2107 proc_list_set(pid, task) 2108 pid_t pid; 2109 char *task; 2110 { 2111 int i; 2112 2113 for (i = 0; i < ProcListSize; i++) 2114 { 2115 if (ProcListVec[i].proc_pid == pid) 2116 { 2117 if (ProcListVec[i].proc_task != NULL) 2118 free(ProcListVec[i].proc_task); 2119 ProcListVec[i].proc_task = newstr(task); 2120 break; 2121 } 2122 } 2123 } 2124 /* 2125 ** PROC_LIST_DROP -- drop pid from process list 2126 ** 2127 ** Parameters: 2128 ** pid -- pid to drop 2129 ** 2130 ** Returns: 2131 ** none. 2132 */ 2133 2134 void 2135 proc_list_drop(pid) 2136 pid_t pid; 2137 { 2138 int i; 2139 2140 for (i = 0; i < ProcListSize; i++) 2141 { 2142 if (ProcListVec[i].proc_pid == pid) 2143 { 2144 ProcListVec[i].proc_pid = NO_PID; 2145 if (ProcListVec[i].proc_task != NULL) 2146 { 2147 free(ProcListVec[i].proc_task); 2148 ProcListVec[i].proc_task = NULL; 2149 } 2150 break; 2151 } 2152 } 2153 if (CurChildren > 0) 2154 CurChildren--; 2155 } 2156 /* 2157 ** PROC_LIST_CLEAR -- clear the process list 2158 ** 2159 ** Parameters: 2160 ** none. 2161 ** 2162 ** Returns: 2163 ** none. 2164 */ 2165 2166 void 2167 proc_list_clear() 2168 { 2169 int i; 2170 2171 /* start from 1 since 0 is the daemon itself */ 2172 for (i = 1; i < ProcListSize; i++) 2173 { 2174 ProcListVec[i].proc_pid = NO_PID; 2175 if (ProcListVec[i].proc_task != NULL) 2176 { 2177 free(ProcListVec[i].proc_task); 2178 ProcListVec[i].proc_task = NULL; 2179 } 2180 } 2181 CurChildren = 0; 2182 } 2183 /* 2184 ** PROC_LIST_PROBE -- probe processes in the list to see if they still exist 2185 ** 2186 ** Parameters: 2187 ** none 2188 ** 2189 ** Returns: 2190 ** none 2191 */ 2192 2193 void 2194 proc_list_probe() 2195 { 2196 int i; 2197 2198 /* start from 1 since 0 is the daemon itself */ 2199 for (i = 1; i < ProcListSize; i++) 2200 { 2201 if (ProcListVec[i].proc_pid == NO_PID) 2202 continue; 2203 if (kill(ProcListVec[i].proc_pid, 0) < 0) 2204 { 2205 if (LogLevel > 3) 2206 sm_syslog(LOG_DEBUG, CurEnv->e_id, 2207 "proc_list_probe: lost pid %d", 2208 (int) ProcListVec[i].proc_pid); 2209 ProcListVec[i].proc_pid = NO_PID; 2210 if (ProcListVec[i].proc_task != NULL) 2211 { 2212 free(ProcListVec[i].proc_task); 2213 ProcListVec[i].proc_task = NULL; 2214 } 2215 CurChildren--; 2216 } 2217 } 2218 if (CurChildren < 0) 2219 CurChildren = 0; 2220 } 2221 /* 2222 ** PROC_LIST_DISPLAY -- display the process list 2223 ** 2224 ** Parameters: 2225 ** out -- output file pointer 2226 ** 2227 ** Returns: 2228 ** none. 2229 */ 2230 2231 void 2232 proc_list_display(out) 2233 FILE *out; 2234 { 2235 int i; 2236 2237 for (i = 0; i < ProcListSize; i++) 2238 { 2239 if (ProcListVec[i].proc_pid == NO_PID) 2240 continue; 2241 2242 fprintf(out, "%d %s%s\n", (int) ProcListVec[i].proc_pid, 2243 ProcListVec[i].proc_task != NULL ? 2244 ProcListVec[i].proc_task : "(unknown)", 2245 (OpMode == MD_SMTP || 2246 OpMode == MD_DAEMON || 2247 OpMode == MD_ARPAFTP) ? "\r" : ""); 2248 } 2249 } 2250 /* 2251 ** SM_STRCASECMP -- 8-bit clean version of strcasecmp 2252 ** 2253 ** Thank you, vendors, for making this all necessary. 2254 */ 2255 2256 /* 2257 * Copyright (c) 1987, 1993 2258 * The Regents of the University of California. All rights reserved. 2259 * 2260 * Redistribution and use in source and binary forms, with or without 2261 * modification, are permitted provided that the following conditions 2262 * are met: 2263 * 1. Redistributions of source code must retain the above copyright 2264 * notice, this list of conditions and the following disclaimer. 2265 * 2. Redistributions in binary form must reproduce the above copyright 2266 * notice, this list of conditions and the following disclaimer in the 2267 * documentation and/or other materials provided with the distribution. 2268 * 3. All advertising materials mentioning features or use of this software 2269 * must display the following acknowledgement: 2270 * This product includes software developed by the University of 2271 * California, Berkeley and its contributors. 2272 * 4. Neither the name of the University nor the names of its contributors 2273 * may be used to endorse or promote products derived from this software 2274 * without specific prior written permission. 2275 * 2276 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2277 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2278 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2279 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2280 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2281 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2282 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2283 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2284 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2285 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2286 * SUCH DAMAGE. 2287 */ 2288 2289 #if defined(LIBC_SCCS) && !defined(lint) 2290 static char sccsid[] = "@(#)strcasecmp.c 8.1 (Berkeley) 6/4/93"; 2291 #endif /* LIBC_SCCS and not lint */ 2292 2293 /* 2294 * This array is designed for mapping upper and lower case letter 2295 * together for a case independent comparison. The mappings are 2296 * based upon ascii character sequences. 2297 */ 2298 static const u_char charmap[] = { 2299 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, 2300 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, 2301 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027, 2302 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037, 2303 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, 2304 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, 2305 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, 2306 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, 2307 0100, 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, 0133, 0134, 0135, 0136, 0137, 2311 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147, 2312 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, 2313 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, 2314 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 2315 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, 2316 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, 2317 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, 2318 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, 2319 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, 2320 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, 2321 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, 2322 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, 2323 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, 2324 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, 2325 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, 2326 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, 2327 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, 2328 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, 2329 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, 2330 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377, 2331 }; 2332 2333 int 2334 sm_strcasecmp(s1, s2) 2335 const char *s1, *s2; 2336 { 2337 register const u_char *cm = charmap, 2338 *us1 = (const u_char *)s1, 2339 *us2 = (const u_char *)s2; 2340 2341 while (cm[*us1] == cm[*us2++]) 2342 if (*us1++ == '\0') 2343 return (0); 2344 return (cm[*us1] - cm[*--us2]); 2345 } 2346 2347 int 2348 sm_strncasecmp(s1, s2, n) 2349 const char *s1, *s2; 2350 register size_t n; 2351 { 2352 if (n != 0) { 2353 register const u_char *cm = charmap, 2354 *us1 = (const u_char *)s1, 2355 *us2 = (const u_char *)s2; 2356 2357 do { 2358 if (cm[*us1] != cm[*us2++]) 2359 return (cm[*us1] - cm[*--us2]); 2360 if (*us1++ == '\0') 2361 break; 2362 } while (--n != 0); 2363 } 2364 return (0); 2365 } 2366