1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)util.c 8.49 (Berkeley) 02/11/95"; 11 #endif /* not lint */ 12 13 # include "sendmail.h" 14 # include <sysexits.h> 15 /* 16 ** STRIPQUOTES -- Strip quotes & quote bits from a string. 17 ** 18 ** Runs through a string and strips off unquoted quote 19 ** characters and quote bits. This is done in place. 20 ** 21 ** Parameters: 22 ** s -- the string to strip. 23 ** 24 ** Returns: 25 ** none. 26 ** 27 ** Side Effects: 28 ** none. 29 ** 30 ** Called By: 31 ** deliver 32 */ 33 34 stripquotes(s) 35 char *s; 36 { 37 register char *p; 38 register char *q; 39 register char c; 40 41 if (s == NULL) 42 return; 43 44 p = q = s; 45 do 46 { 47 c = *p++; 48 if (c == '\\') 49 c = *p++; 50 else if (c == '"') 51 continue; 52 *q++ = c; 53 } while (c != '\0'); 54 } 55 /* 56 ** XALLOC -- Allocate memory and bitch wildly on failure. 57 ** 58 ** THIS IS A CLUDGE. This should be made to give a proper 59 ** error -- but after all, what can we do? 60 ** 61 ** Parameters: 62 ** sz -- size of area to allocate. 63 ** 64 ** Returns: 65 ** pointer to data region. 66 ** 67 ** Side Effects: 68 ** Memory is allocated. 69 */ 70 71 char * 72 xalloc(sz) 73 register int sz; 74 { 75 register char *p; 76 77 /* some systems can't handle size zero mallocs */ 78 if (sz <= 0) 79 sz = 1; 80 81 p = malloc((unsigned) sz); 82 if (p == NULL) 83 { 84 syserr("Out of memory!!"); 85 abort(); 86 /* exit(EX_UNAVAILABLE); */ 87 } 88 return (p); 89 } 90 /* 91 ** COPYPLIST -- copy list of pointers. 92 ** 93 ** This routine is the equivalent of newstr for lists of 94 ** pointers. 95 ** 96 ** Parameters: 97 ** list -- list of pointers to copy. 98 ** Must be NULL terminated. 99 ** copycont -- if TRUE, copy the contents of the vector 100 ** (which must be a string) also. 101 ** 102 ** Returns: 103 ** a copy of 'list'. 104 ** 105 ** Side Effects: 106 ** none. 107 */ 108 109 char ** 110 copyplist(list, copycont) 111 char **list; 112 bool copycont; 113 { 114 register char **vp; 115 register char **newvp; 116 117 for (vp = list; *vp != NULL; vp++) 118 continue; 119 120 vp++; 121 122 newvp = (char **) xalloc((int) (vp - list) * sizeof *vp); 123 bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp); 124 125 if (copycont) 126 { 127 for (vp = newvp; *vp != NULL; vp++) 128 *vp = newstr(*vp); 129 } 130 131 return (newvp); 132 } 133 /* 134 ** COPYQUEUE -- copy address queue. 135 ** 136 ** This routine is the equivalent of newstr for address queues 137 ** addresses marked with QDONTSEND aren't copied 138 ** 139 ** Parameters: 140 ** addr -- list of address structures to copy. 141 ** 142 ** Returns: 143 ** a copy of 'addr'. 144 ** 145 ** Side Effects: 146 ** none. 147 */ 148 149 ADDRESS * 150 copyqueue(addr) 151 ADDRESS *addr; 152 { 153 register ADDRESS *newaddr; 154 ADDRESS *ret; 155 register ADDRESS **tail = &ret; 156 157 while (addr != NULL) 158 { 159 if (!bitset(QDONTSEND, addr->q_flags)) 160 { 161 newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS)); 162 STRUCTCOPY(*addr, *newaddr); 163 *tail = newaddr; 164 tail = &newaddr->q_next; 165 } 166 addr = addr->q_next; 167 } 168 *tail = NULL; 169 170 return ret; 171 } 172 /* 173 ** PRINTAV -- print argument vector. 174 ** 175 ** Parameters: 176 ** av -- argument vector. 177 ** 178 ** Returns: 179 ** none. 180 ** 181 ** Side Effects: 182 ** prints av. 183 */ 184 185 printav(av) 186 register char **av; 187 { 188 while (*av != NULL) 189 { 190 if (tTd(0, 44)) 191 printf("\n\t%08x=", *av); 192 else 193 (void) putchar(' '); 194 xputs(*av++); 195 } 196 (void) putchar('\n'); 197 } 198 /* 199 ** LOWER -- turn letter into lower case. 200 ** 201 ** Parameters: 202 ** c -- character to turn into lower case. 203 ** 204 ** Returns: 205 ** c, in lower case. 206 ** 207 ** Side Effects: 208 ** none. 209 */ 210 211 char 212 lower(c) 213 register char c; 214 { 215 return((isascii(c) && isupper(c)) ? tolower(c) : c); 216 } 217 /* 218 ** XPUTS -- put string doing control escapes. 219 ** 220 ** Parameters: 221 ** s -- string to put. 222 ** 223 ** Returns: 224 ** none. 225 ** 226 ** Side Effects: 227 ** output to stdout 228 */ 229 230 xputs(s) 231 register char *s; 232 { 233 register int c; 234 register struct metamac *mp; 235 extern struct metamac MetaMacros[]; 236 237 if (s == NULL) 238 { 239 printf("<null>"); 240 return; 241 } 242 while ((c = (*s++ & 0377)) != '\0') 243 { 244 if (!isascii(c)) 245 { 246 if (c == MATCHREPL) 247 { 248 putchar('$'); 249 continue; 250 } 251 if (c == MACROEXPAND) 252 { 253 putchar('$'); 254 if (bitset(0200, *s)) 255 printf("{%s}", macname(*s++ & 0377)); 256 continue; 257 } 258 for (mp = MetaMacros; mp->metaname != '\0'; mp++) 259 { 260 if ((mp->metaval & 0377) == c) 261 { 262 printf("$%c", mp->metaname); 263 break; 264 } 265 } 266 if (mp->metaname != '\0') 267 continue; 268 (void) putchar('\\'); 269 c &= 0177; 270 } 271 if (isprint(c)) 272 { 273 putchar(c); 274 continue; 275 } 276 277 /* wasn't a meta-macro -- find another way to print it */ 278 switch (c) 279 { 280 case '\0': 281 continue; 282 283 case '\n': 284 c = 'n'; 285 break; 286 287 case '\r': 288 c = 'r'; 289 break; 290 291 case '\t': 292 c = 't'; 293 break; 294 295 default: 296 (void) putchar('^'); 297 (void) putchar(c ^ 0100); 298 continue; 299 } 300 } 301 (void) fflush(stdout); 302 } 303 /* 304 ** MAKELOWER -- Translate a line into lower case 305 ** 306 ** Parameters: 307 ** p -- the string to translate. If NULL, return is 308 ** immediate. 309 ** 310 ** Returns: 311 ** none. 312 ** 313 ** Side Effects: 314 ** String pointed to by p is translated to lower case. 315 ** 316 ** Called By: 317 ** parse 318 */ 319 320 makelower(p) 321 register char *p; 322 { 323 register char c; 324 325 if (p == NULL) 326 return; 327 for (; (c = *p) != '\0'; p++) 328 if (isascii(c) && isupper(c)) 329 *p = tolower(c); 330 } 331 /* 332 ** BUILDFNAME -- build full name from gecos style entry. 333 ** 334 ** This routine interprets the strange entry that would appear 335 ** in the GECOS field of the password file. 336 ** 337 ** Parameters: 338 ** p -- name to build. 339 ** login -- the login name of this user (for &). 340 ** buf -- place to put the result. 341 ** 342 ** Returns: 343 ** none. 344 ** 345 ** Side Effects: 346 ** none. 347 */ 348 349 buildfname(gecos, login, buf) 350 register char *gecos; 351 char *login; 352 char *buf; 353 { 354 register char *p; 355 register char *bp = buf; 356 int l; 357 358 if (*gecos == '*') 359 gecos++; 360 361 /* find length of final string */ 362 l = 0; 363 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 364 { 365 if (*p == '&') 366 l += strlen(login); 367 else 368 l++; 369 } 370 371 /* now fill in buf */ 372 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 373 { 374 if (*p == '&') 375 { 376 (void) strcpy(bp, login); 377 *bp = toupper(*bp); 378 while (*bp != '\0') 379 bp++; 380 } 381 else 382 *bp++ = *p; 383 } 384 *bp = '\0'; 385 } 386 /* 387 ** SAFEFILE -- return true if a file exists and is safe for a user. 388 ** 389 ** Parameters: 390 ** fn -- filename to check. 391 ** uid -- user id to compare against. 392 ** gid -- group id to compare against. 393 ** uname -- user name to compare against (used for group 394 ** sets). 395 ** flags -- modifiers: 396 ** SFF_MUSTOWN -- "uid" must own this file. 397 ** SFF_NOSLINK -- file cannot be a symbolic link. 398 ** mode -- mode bits that must match. 399 ** 400 ** Returns: 401 ** 0 if fn exists, is owned by uid, and matches mode. 402 ** An errno otherwise. The actual errno is cleared. 403 ** 404 ** Side Effects: 405 ** none. 406 */ 407 408 #include <grp.h> 409 410 #ifndef S_IXOTH 411 # define S_IXOTH (S_IEXEC >> 6) 412 #endif 413 414 #ifndef S_IXGRP 415 # define S_IXGRP (S_IEXEC >> 3) 416 #endif 417 418 #ifndef S_IXUSR 419 # define S_IXUSR (S_IEXEC) 420 #endif 421 422 int 423 safefile(fn, uid, gid, uname, flags, mode) 424 char *fn; 425 uid_t uid; 426 gid_t gid; 427 char *uname; 428 int flags; 429 int mode; 430 { 431 register char *p; 432 register struct group *gr = NULL; 433 struct stat stbuf; 434 435 if (tTd(54, 4)) 436 printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n", 437 fn, uid, gid, flags, mode); 438 errno = 0; 439 440 if (!bitset(SFF_NOPATHCHECK, flags) || 441 (uid == 0 && !bitset(SFF_ROOTOK, flags))) 442 { 443 /* check the path to the file for acceptability */ 444 for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/') 445 { 446 *p = '\0'; 447 if (stat(fn, &stbuf) < 0) 448 break; 449 if (uid == 0 && !bitset(SFF_ROOTOK, flags)) 450 { 451 if (bitset(S_IXOTH, stbuf.st_mode)) 452 continue; 453 break; 454 } 455 if (stbuf.st_uid == uid && 456 bitset(S_IXUSR, stbuf.st_mode)) 457 continue; 458 if (stbuf.st_gid == gid && 459 bitset(S_IXGRP, stbuf.st_mode)) 460 continue; 461 #ifndef NO_GROUP_SET 462 if (uname != NULL && 463 ((gr != NULL && gr->gr_gid == stbuf.st_gid) || 464 (gr = getgrgid(stbuf.st_gid)) != NULL)) 465 { 466 register char **gp; 467 468 for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++) 469 if (strcmp(*gp, uname) == 0) 470 break; 471 if (gp != NULL && *gp != NULL && 472 bitset(S_IXGRP, stbuf.st_mode)) 473 continue; 474 } 475 #endif 476 if (!bitset(S_IXOTH, stbuf.st_mode)) 477 break; 478 } 479 if (p != NULL) 480 { 481 int ret = errno; 482 483 if (ret == 0) 484 ret = EACCES; 485 if (tTd(54, 4)) 486 printf("\t[dir %s] %s\n", fn, errstring(ret)); 487 *p = '/'; 488 return ret; 489 } 490 } 491 492 #ifdef HASLSTAT 493 if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, &stbuf) 494 : stat(fn, &stbuf)) < 0) 495 #else 496 if (stat(fn, &stbuf) < 0) 497 #endif 498 { 499 int ret = errno; 500 501 if (tTd(54, 4)) 502 printf("\t%s\n", errstring(ret)); 503 504 errno = 0; 505 return ret; 506 } 507 508 #ifdef S_ISLNK 509 if (bitset(SFF_NOSLINK, flags) && S_ISLNK(stbuf.st_mode)) 510 { 511 if (tTd(54, 4)) 512 printf("\t[slink mode %o]\tEPERM\n", stbuf.st_mode); 513 return EPERM; 514 } 515 #endif 516 517 if (uid == 0 && !bitset(SFF_ROOTOK, flags)) 518 mode >>= 6; 519 else if (stbuf.st_uid != uid) 520 { 521 mode >>= 3; 522 if (stbuf.st_gid == gid) 523 ; 524 #ifndef NO_GROUP_SET 525 else if (uname != NULL && 526 ((gr != NULL && gr->gr_gid == stbuf.st_gid) || 527 (gr = getgrgid(stbuf.st_gid)) != NULL)) 528 { 529 register char **gp; 530 531 for (gp = gr->gr_mem; *gp != NULL; gp++) 532 if (strcmp(*gp, uname) == 0) 533 break; 534 if (*gp == NULL) 535 mode >>= 3; 536 } 537 #endif 538 else 539 mode >>= 3; 540 } 541 if (tTd(54, 4)) 542 printf("\t[uid %d, stat %o, mode %o] ", 543 stbuf.st_uid, stbuf.st_mode, mode); 544 if ((stbuf.st_uid == uid || stbuf.st_uid == 0 || 545 !bitset(SFF_MUSTOWN, flags)) && 546 (stbuf.st_mode & mode) == mode) 547 { 548 if (tTd(54, 4)) 549 printf("\tOK\n"); 550 return 0; 551 } 552 if (tTd(54, 4)) 553 printf("\tEACCES\n"); 554 return EACCES; 555 } 556 /* 557 ** FIXCRLF -- fix <CR><LF> in line. 558 ** 559 ** Looks for the <CR><LF> combination and turns it into the 560 ** UNIX canonical <NL> character. It only takes one line, 561 ** i.e., it is assumed that the first <NL> found is the end 562 ** of the line. 563 ** 564 ** Parameters: 565 ** line -- the line to fix. 566 ** stripnl -- if true, strip the newline also. 567 ** 568 ** Returns: 569 ** none. 570 ** 571 ** Side Effects: 572 ** line is changed in place. 573 */ 574 575 fixcrlf(line, stripnl) 576 char *line; 577 bool stripnl; 578 { 579 register char *p; 580 581 p = strchr(line, '\n'); 582 if (p == NULL) 583 return; 584 if (p > line && p[-1] == '\r') 585 p--; 586 if (!stripnl) 587 *p++ = '\n'; 588 *p = '\0'; 589 } 590 /* 591 ** DFOPEN -- determined file open 592 ** 593 ** This routine has the semantics of fopen, except that it will 594 ** keep trying a few times to make this happen. The idea is that 595 ** on very loaded systems, we may run out of resources (inodes, 596 ** whatever), so this tries to get around it. 597 */ 598 599 #ifndef O_ACCMODE 600 # define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) 601 #endif 602 603 struct omodes 604 { 605 int mask; 606 int mode; 607 char *farg; 608 } OpenModes[] = 609 { 610 O_ACCMODE, O_RDONLY, "r", 611 O_ACCMODE|O_APPEND, O_WRONLY, "w", 612 O_ACCMODE|O_APPEND, O_WRONLY|O_APPEND, "a", 613 O_TRUNC, 0, "w+", 614 O_APPEND, O_APPEND, "a+", 615 0, 0, "r+", 616 }; 617 618 FILE * 619 dfopen(filename, omode, cmode) 620 char *filename; 621 int omode; 622 int cmode; 623 { 624 register int tries; 625 int fd; 626 register struct omodes *om; 627 struct stat st; 628 629 for (om = OpenModes; om->mask != 0; om++) 630 if ((omode & om->mask) == om->mode) 631 break; 632 633 for (tries = 0; tries < 10; tries++) 634 { 635 sleep((unsigned) (10 * tries)); 636 errno = 0; 637 fd = open(filename, omode, cmode); 638 if (fd >= 0) 639 break; 640 switch (errno) 641 { 642 case ENFILE: /* system file table full */ 643 case EINTR: /* interrupted syscall */ 644 #ifdef ETXTBSY 645 case ETXTBSY: /* Apollo: net file locked */ 646 #endif 647 continue; 648 } 649 break; 650 } 651 if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode)) 652 { 653 int locktype; 654 655 /* lock the file to avoid accidental conflicts */ 656 if ((omode & O_ACCMODE) != O_RDONLY) 657 locktype = LOCK_EX; 658 else 659 locktype = LOCK_SH; 660 (void) lockfile(fd, filename, NULL, locktype); 661 errno = 0; 662 } 663 if (fd < 0) 664 return NULL; 665 else 666 return fdopen(fd, om->farg); 667 } 668 /* 669 ** PUTLINE -- put a line like fputs obeying SMTP conventions 670 ** 671 ** This routine always guarantees outputing a newline (or CRLF, 672 ** as appropriate) at the end of the string. 673 ** 674 ** Parameters: 675 ** l -- line to put. 676 ** mci -- the mailer connection information. 677 ** 678 ** Returns: 679 ** none 680 ** 681 ** Side Effects: 682 ** output of l to fp. 683 */ 684 685 putline(l, mci) 686 register char *l; 687 register MCI *mci; 688 { 689 register char *p; 690 register char svchar; 691 int slop = 0; 692 693 /* strip out 0200 bits -- these can look like TELNET protocol */ 694 if (bitset(MCIF_7BIT, mci->mci_flags)) 695 { 696 for (p = l; (svchar = *p) != '\0'; ++p) 697 if (bitset(0200, svchar)) 698 *p = svchar &~ 0200; 699 } 700 701 do 702 { 703 /* find the end of the line */ 704 p = strchr(l, '\n'); 705 if (p == NULL) 706 p = &l[strlen(l)]; 707 708 if (TrafficLogFile != NULL) 709 fprintf(TrafficLogFile, "%05d >>> ", getpid()); 710 711 /* check for line overflow */ 712 while (mci->mci_mailer->m_linelimit > 0 && 713 (p - l + slop) > mci->mci_mailer->m_linelimit) 714 { 715 register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; 716 717 svchar = *q; 718 *q = '\0'; 719 if (l[0] == '.' && slop == 0 && 720 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 721 { 722 (void) putc('.', mci->mci_out); 723 if (TrafficLogFile != NULL) 724 (void) putc('.', TrafficLogFile); 725 } 726 fputs(l, mci->mci_out); 727 (void) putc('!', mci->mci_out); 728 fputs(mci->mci_mailer->m_eol, mci->mci_out); 729 (void) putc(' ', mci->mci_out); 730 if (TrafficLogFile != NULL) 731 fprintf(TrafficLogFile, "%s!\n%05d >>> ", 732 l, getpid()); 733 *q = svchar; 734 l = q; 735 slop = 1; 736 } 737 738 /* output last part */ 739 if (l[0] == '.' && slop == 0 && 740 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 741 { 742 (void) putc('.', mci->mci_out); 743 if (TrafficLogFile != NULL) 744 (void) putc('.', TrafficLogFile); 745 } 746 if (TrafficLogFile != NULL) 747 fprintf(TrafficLogFile, "%.*s\n", p - l, l); 748 for ( ; l < p; ++l) 749 (void) putc(*l, mci->mci_out); 750 fputs(mci->mci_mailer->m_eol, mci->mci_out); 751 if (*l == '\n') 752 ++l; 753 } while (l[0] != '\0'); 754 } 755 /* 756 ** XUNLINK -- unlink a file, doing logging as appropriate. 757 ** 758 ** Parameters: 759 ** f -- name of file to unlink. 760 ** 761 ** Returns: 762 ** none. 763 ** 764 ** Side Effects: 765 ** f is unlinked. 766 */ 767 768 xunlink(f) 769 char *f; 770 { 771 register int i; 772 773 # ifdef LOG 774 if (LogLevel > 98) 775 syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f); 776 # endif /* LOG */ 777 778 i = unlink(f); 779 # ifdef LOG 780 if (i < 0 && LogLevel > 97) 781 syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); 782 # endif /* LOG */ 783 } 784 /* 785 ** XFCLOSE -- close a file, doing logging as appropriate. 786 ** 787 ** Parameters: 788 ** fp -- file pointer for the file to close 789 ** a, b -- miscellaneous crud to print for debugging 790 ** 791 ** Returns: 792 ** none. 793 ** 794 ** Side Effects: 795 ** fp is closed. 796 */ 797 798 xfclose(fp, a, b) 799 FILE *fp; 800 char *a, *b; 801 { 802 if (tTd(53, 99)) 803 printf("xfclose(%x) %s %s\n", fp, a, b); 804 #ifdef XDEBUG 805 if (fileno(fp) == 1) 806 syserr("xfclose(%s %s): fd = 1", a, b); 807 #endif 808 if (fclose(fp) < 0 && tTd(53, 99)) 809 printf("xfclose FAILURE: %s\n", errstring(errno)); 810 } 811 /* 812 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 813 ** 814 ** Parameters: 815 ** buf -- place to put the input line. 816 ** siz -- size of buf. 817 ** fp -- file to read from. 818 ** timeout -- the timeout before error occurs. 819 ** during -- what we are trying to read (for error messages). 820 ** 821 ** Returns: 822 ** NULL on error (including timeout). This will also leave 823 ** buf containing a null string. 824 ** buf otherwise. 825 ** 826 ** Side Effects: 827 ** none. 828 */ 829 830 static jmp_buf CtxReadTimeout; 831 static int readtimeout(); 832 833 char * 834 sfgets(buf, siz, fp, timeout, during) 835 char *buf; 836 int siz; 837 FILE *fp; 838 time_t timeout; 839 char *during; 840 { 841 register EVENT *ev = NULL; 842 register char *p; 843 844 if (fp == NULL) 845 { 846 buf[0] = '\0'; 847 return NULL; 848 } 849 850 /* set the timeout */ 851 if (timeout != 0) 852 { 853 if (setjmp(CtxReadTimeout) != 0) 854 { 855 # ifdef LOG 856 syslog(LOG_NOTICE, 857 "timeout waiting for input from %s during %s\n", 858 CurHostName? CurHostName: "local", during); 859 # endif 860 errno = 0; 861 usrerr("451 timeout waiting for input during %s", 862 during); 863 buf[0] = '\0'; 864 #ifdef XDEBUG 865 checkfd012(during); 866 #endif 867 return (NULL); 868 } 869 ev = setevent(timeout, readtimeout, 0); 870 } 871 872 /* try to read */ 873 p = NULL; 874 while (!feof(fp) && !ferror(fp)) 875 { 876 errno = 0; 877 p = fgets(buf, siz, fp); 878 if (p != NULL || errno != EINTR) 879 break; 880 clearerr(fp); 881 } 882 883 /* clear the event if it has not sprung */ 884 clrevent(ev); 885 886 /* clean up the books and exit */ 887 LineNumber++; 888 if (p == NULL) 889 { 890 buf[0] = '\0'; 891 if (TrafficLogFile != NULL) 892 fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid()); 893 return (NULL); 894 } 895 if (TrafficLogFile != NULL) 896 fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf); 897 if (SevenBitInput) 898 { 899 for (p = buf; *p != '\0'; p++) 900 *p &= ~0200; 901 } 902 else if (!HasEightBits) 903 { 904 for (p = buf; *p != '\0'; p++) 905 { 906 if (bitset(0200, *p)) 907 { 908 HasEightBits = TRUE; 909 break; 910 } 911 } 912 } 913 return (buf); 914 } 915 916 static 917 readtimeout(timeout) 918 time_t timeout; 919 { 920 longjmp(CtxReadTimeout, 1); 921 } 922 /* 923 ** FGETFOLDED -- like fgets, but know about folded lines. 924 ** 925 ** Parameters: 926 ** buf -- place to put result. 927 ** n -- bytes available. 928 ** f -- file to read from. 929 ** 930 ** Returns: 931 ** input line(s) on success, NULL on error or EOF. 932 ** This will normally be buf -- unless the line is too 933 ** long, when it will be xalloc()ed. 934 ** 935 ** Side Effects: 936 ** buf gets lines from f, with continuation lines (lines 937 ** with leading white space) appended. CRLF's are mapped 938 ** into single newlines. Any trailing NL is stripped. 939 */ 940 941 char * 942 fgetfolded(buf, n, f) 943 char *buf; 944 register int n; 945 FILE *f; 946 { 947 register char *p = buf; 948 char *bp = buf; 949 register int i; 950 951 n--; 952 while ((i = getc(f)) != EOF) 953 { 954 if (i == '\r') 955 { 956 i = getc(f); 957 if (i != '\n') 958 { 959 if (i != EOF) 960 (void) ungetc(i, f); 961 i = '\r'; 962 } 963 } 964 if (--n <= 0) 965 { 966 /* allocate new space */ 967 char *nbp; 968 int nn; 969 970 nn = (p - bp); 971 if (nn < MEMCHUNKSIZE) 972 nn *= 2; 973 else 974 nn += MEMCHUNKSIZE; 975 nbp = xalloc(nn); 976 bcopy(bp, nbp, p - bp); 977 p = &nbp[p - bp]; 978 if (bp != buf) 979 free(bp); 980 bp = nbp; 981 n = nn - (p - bp); 982 } 983 *p++ = i; 984 if (i == '\n') 985 { 986 LineNumber++; 987 i = getc(f); 988 if (i != EOF) 989 (void) ungetc(i, f); 990 if (i != ' ' && i != '\t') 991 break; 992 } 993 } 994 if (p == bp) 995 return (NULL); 996 *--p = '\0'; 997 return (bp); 998 } 999 /* 1000 ** CURTIME -- return current time. 1001 ** 1002 ** Parameters: 1003 ** none. 1004 ** 1005 ** Returns: 1006 ** the current time. 1007 ** 1008 ** Side Effects: 1009 ** none. 1010 */ 1011 1012 time_t 1013 curtime() 1014 { 1015 auto time_t t; 1016 1017 (void) time(&t); 1018 return (t); 1019 } 1020 /* 1021 ** ATOBOOL -- convert a string representation to boolean. 1022 ** 1023 ** Defaults to "TRUE" 1024 ** 1025 ** Parameters: 1026 ** s -- string to convert. Takes "tTyY" as true, 1027 ** others as false. 1028 ** 1029 ** Returns: 1030 ** A boolean representation of the string. 1031 ** 1032 ** Side Effects: 1033 ** none. 1034 */ 1035 1036 bool 1037 atobool(s) 1038 register char *s; 1039 { 1040 if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) 1041 return (TRUE); 1042 return (FALSE); 1043 } 1044 /* 1045 ** ATOOCT -- convert a string representation to octal. 1046 ** 1047 ** Parameters: 1048 ** s -- string to convert. 1049 ** 1050 ** Returns: 1051 ** An integer representing the string interpreted as an 1052 ** octal number. 1053 ** 1054 ** Side Effects: 1055 ** none. 1056 */ 1057 1058 atooct(s) 1059 register char *s; 1060 { 1061 register int i = 0; 1062 1063 while (*s >= '0' && *s <= '7') 1064 i = (i << 3) | (*s++ - '0'); 1065 return (i); 1066 } 1067 /* 1068 ** WAITFOR -- wait for a particular process id. 1069 ** 1070 ** Parameters: 1071 ** pid -- process id to wait for. 1072 ** 1073 ** Returns: 1074 ** status of pid. 1075 ** -1 if pid never shows up. 1076 ** 1077 ** Side Effects: 1078 ** none. 1079 */ 1080 1081 int 1082 waitfor(pid) 1083 int pid; 1084 { 1085 #ifdef WAITUNION 1086 union wait st; 1087 #else 1088 auto int st; 1089 #endif 1090 int i; 1091 1092 do 1093 { 1094 errno = 0; 1095 i = wait(&st); 1096 } while ((i >= 0 || errno == EINTR) && i != pid); 1097 if (i < 0) 1098 return -1; 1099 #ifdef WAITUNION 1100 return st.w_status; 1101 #else 1102 return st; 1103 #endif 1104 } 1105 /* 1106 ** BITINTERSECT -- tell if two bitmaps intersect 1107 ** 1108 ** Parameters: 1109 ** a, b -- the bitmaps in question 1110 ** 1111 ** Returns: 1112 ** TRUE if they have a non-null intersection 1113 ** FALSE otherwise 1114 ** 1115 ** Side Effects: 1116 ** none. 1117 */ 1118 1119 bool 1120 bitintersect(a, b) 1121 BITMAP a; 1122 BITMAP b; 1123 { 1124 int i; 1125 1126 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1127 if ((a[i] & b[i]) != 0) 1128 return (TRUE); 1129 return (FALSE); 1130 } 1131 /* 1132 ** BITZEROP -- tell if a bitmap is all zero 1133 ** 1134 ** Parameters: 1135 ** map -- the bit map to check 1136 ** 1137 ** Returns: 1138 ** TRUE if map is all zero. 1139 ** FALSE if there are any bits set in map. 1140 ** 1141 ** Side Effects: 1142 ** none. 1143 */ 1144 1145 bool 1146 bitzerop(map) 1147 BITMAP map; 1148 { 1149 int i; 1150 1151 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1152 if (map[i] != 0) 1153 return (FALSE); 1154 return (TRUE); 1155 } 1156 /* 1157 ** STRCONTAINEDIN -- tell if one string is contained in another 1158 ** 1159 ** Parameters: 1160 ** a -- possible substring. 1161 ** b -- possible superstring. 1162 ** 1163 ** Returns: 1164 ** TRUE if a is contained in b. 1165 ** FALSE otherwise. 1166 */ 1167 1168 bool 1169 strcontainedin(a, b) 1170 register char *a; 1171 register char *b; 1172 { 1173 int la; 1174 int lb; 1175 int c; 1176 1177 la = strlen(a); 1178 lb = strlen(b); 1179 c = *a; 1180 if (isascii(c) && isupper(c)) 1181 c = tolower(c); 1182 for (; lb-- >= la; b++) 1183 { 1184 if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c) 1185 continue; 1186 if (strncasecmp(a, b, la) == 0) 1187 return TRUE; 1188 } 1189 return FALSE; 1190 } 1191 /* 1192 ** CHECKFD012 -- check low numbered file descriptors 1193 ** 1194 ** File descriptors 0, 1, and 2 should be open at all times. 1195 ** This routine verifies that, and fixes it if not true. 1196 ** 1197 ** Parameters: 1198 ** where -- a tag printed if the assertion failed 1199 ** 1200 ** Returns: 1201 ** none 1202 */ 1203 1204 checkfd012(where) 1205 char *where; 1206 { 1207 #ifdef XDEBUG 1208 register int i; 1209 struct stat stbuf; 1210 1211 for (i = 0; i < 3; i++) 1212 { 1213 if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP) 1214 { 1215 /* oops.... */ 1216 int fd; 1217 1218 syserr("%s: fd %d not open", where, i); 1219 fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666); 1220 if (fd != i) 1221 { 1222 (void) dup2(fd, i); 1223 (void) close(fd); 1224 } 1225 } 1226 } 1227 #endif /* XDEBUG */ 1228 } 1229 /* 1230 ** PRINTOPENFDS -- print the open file descriptors (for debugging) 1231 ** 1232 ** Parameters: 1233 ** logit -- if set, send output to syslog; otherwise 1234 ** print for debugging. 1235 ** 1236 ** Returns: 1237 ** none. 1238 */ 1239 1240 #include <netdb.h> 1241 #include <arpa/inet.h> 1242 1243 printopenfds(logit) 1244 bool logit; 1245 { 1246 register int fd; 1247 extern int DtableSize; 1248 1249 for (fd = 0; fd < DtableSize; fd++) 1250 dumpfd(fd, FALSE, logit); 1251 } 1252 /* 1253 ** DUMPFD -- dump a file descriptor 1254 ** 1255 ** Parameters: 1256 ** fd -- the file descriptor to dump. 1257 ** printclosed -- if set, print a notification even if 1258 ** it is closed; otherwise print nothing. 1259 ** logit -- if set, send output to syslog instead of stdout. 1260 */ 1261 1262 dumpfd(fd, printclosed, logit) 1263 int fd; 1264 bool printclosed; 1265 bool logit; 1266 { 1267 register struct hostent *hp; 1268 register char *p; 1269 char *fmtstr; 1270 struct sockaddr_in sin; 1271 auto int slen; 1272 struct stat st; 1273 char buf[200]; 1274 1275 p = buf; 1276 sprintf(p, "%3d: ", fd); 1277 p += strlen(p); 1278 1279 if (fstat(fd, &st) < 0) 1280 { 1281 if (printclosed || errno != EBADF) 1282 { 1283 sprintf(p, "CANNOT STAT (%s)", errstring(errno)); 1284 goto printit; 1285 } 1286 return; 1287 } 1288 1289 slen = fcntl(fd, F_GETFL, NULL); 1290 if (slen != -1) 1291 { 1292 sprintf(p, "fl=0x%x, ", slen); 1293 p += strlen(p); 1294 } 1295 1296 sprintf(p, "mode=%o: ", st.st_mode); 1297 p += strlen(p); 1298 switch (st.st_mode & S_IFMT) 1299 { 1300 #ifdef S_IFSOCK 1301 case S_IFSOCK: 1302 sprintf(p, "SOCK "); 1303 p += strlen(p); 1304 slen = sizeof sin; 1305 if (getsockname(fd, (struct sockaddr *) &sin, &slen) < 0) 1306 sprintf(p, "(badsock)"); 1307 else 1308 { 1309 hp = gethostbyaddr((char *) &sin.sin_addr, 1310 INADDRSZ, AF_INET); 1311 sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr) 1312 : hp->h_name, ntohs(sin.sin_port)); 1313 } 1314 p += strlen(p); 1315 sprintf(p, "->"); 1316 p += strlen(p); 1317 slen = sizeof sin; 1318 if (getpeername(fd, (struct sockaddr *) &sin, &slen) < 0) 1319 sprintf(p, "(badsock)"); 1320 else 1321 { 1322 hp = gethostbyaddr((char *) &sin.sin_addr, 1323 INADDRSZ, AF_INET); 1324 sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr) 1325 : hp->h_name, ntohs(sin.sin_port)); 1326 } 1327 break; 1328 #endif 1329 1330 case S_IFCHR: 1331 sprintf(p, "CHR: "); 1332 p += strlen(p); 1333 goto defprint; 1334 1335 case S_IFBLK: 1336 sprintf(p, "BLK: "); 1337 p += strlen(p); 1338 goto defprint; 1339 1340 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) 1341 case S_IFIFO: 1342 sprintf(p, "FIFO: "); 1343 p += strlen(p); 1344 goto defprint; 1345 #endif 1346 1347 #ifdef S_IFDIR 1348 case S_IFDIR: 1349 sprintf(p, "DIR: "); 1350 p += strlen(p); 1351 goto defprint; 1352 #endif 1353 1354 #ifdef S_IFLNK 1355 case S_IFLNK: 1356 sprintf(p, "LNK: "); 1357 p += strlen(p); 1358 goto defprint; 1359 #endif 1360 1361 default: 1362 defprint: 1363 if (sizeof st.st_size > sizeof (long)) 1364 fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd"; 1365 else 1366 fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld"; 1367 sprintf(p, fmtstr, 1368 major(st.st_dev), minor(st.st_dev), st.st_ino, 1369 st.st_nlink, st.st_uid, st.st_gid, st.st_size); 1370 break; 1371 } 1372 1373 printit: 1374 #ifdef LOG 1375 if (logit) 1376 syslog(LOG_DEBUG, "%s", buf); 1377 else 1378 #endif 1379 printf("%s\n", buf); 1380 } 1381 /* 1382 ** SHORTENSTRING -- return short version of a string 1383 ** 1384 ** If the string is already short, just return it. If it is too 1385 ** long, return the head and tail of the string. 1386 ** 1387 ** Parameters: 1388 ** s -- the string to shorten. 1389 ** m -- the max length of the string. 1390 ** 1391 ** Returns: 1392 ** Either s or a short version of s. 1393 */ 1394 1395 #ifndef MAXSHORTSTR 1396 # define MAXSHORTSTR 203 1397 #endif 1398 1399 char * 1400 shortenstring(s, m) 1401 register char *s; 1402 int m; 1403 { 1404 int l; 1405 static char buf[MAXSHORTSTR + 1]; 1406 1407 l = strlen(s); 1408 if (l < m) 1409 return s; 1410 if (m > MAXSHORTSTR) 1411 m = MAXSHORTSTR; 1412 else if (m < 10) 1413 { 1414 if (m < 5) 1415 { 1416 strncpy(buf, s, m); 1417 buf[m] = '\0'; 1418 return buf; 1419 } 1420 strncpy(buf, s, m - 3); 1421 strcpy(buf + m - 3, "..."); 1422 return buf; 1423 } 1424 m = (m - 3) / 2; 1425 strncpy(buf, s, m); 1426 strcpy(buf + m, "..."); 1427 strcpy(buf + m + 3, s + l - m); 1428 return buf; 1429 } 1430 /* 1431 ** GET_COLUMN -- look up a Column in a line buffer 1432 ** 1433 ** Parameters: 1434 ** line -- the raw text line to search. 1435 ** col -- the column number to fetch. 1436 ** delim -- the delimiter between columns. If null, 1437 ** use white space. 1438 ** buf -- the output buffer. 1439 ** 1440 ** Returns: 1441 ** buf if successful. 1442 ** NULL otherwise. 1443 */ 1444 1445 char * 1446 get_column(line, col, delim, buf) 1447 char line[]; 1448 int col; 1449 char delim; 1450 char buf[]; 1451 { 1452 char *p; 1453 char *begin, *end; 1454 int i; 1455 char delimbuf[3]; 1456 1457 if (delim == '\0') 1458 strcpy(delimbuf, "\t "); 1459 else 1460 { 1461 delimbuf[0] = delim; 1462 delimbuf[1] = '\0'; 1463 } 1464 1465 p = line; 1466 if (*p == '\0') 1467 return NULL; /* line empty */ 1468 if (*p == delim && col == 0) 1469 return NULL; /* first column empty */ 1470 1471 begin = line; 1472 1473 if (col == 0 && delim == '\0') 1474 { 1475 while (*begin && isspace(*begin)) 1476 begin++; 1477 } 1478 1479 for (i = 0; i < col; i++) 1480 { 1481 if ((begin = strpbrk(begin, delimbuf)) == NULL) 1482 return NULL; /* no such column */ 1483 begin++; 1484 if (delim == '\0') 1485 { 1486 while (*begin && isspace(*begin)) 1487 begin++; 1488 } 1489 } 1490 1491 end = strpbrk(begin, delimbuf); 1492 if (end == NULL) 1493 { 1494 strcpy(buf, begin); 1495 } 1496 else 1497 { 1498 strncpy(buf, begin, end - begin); 1499 buf[end - begin] = '\0'; 1500 } 1501 return buf; 1502 } 1503 /* 1504 ** CLEANSTRCPY -- copy string keeping out bogus characters 1505 ** 1506 ** Parameters: 1507 ** t -- "to" string. 1508 ** f -- "from" string. 1509 ** l -- length of space available in "to" string. 1510 ** 1511 ** Returns: 1512 ** none. 1513 */ 1514 1515 void 1516 cleanstrcpy(t, f, l) 1517 register char *t; 1518 register char *f; 1519 int l; 1520 { 1521 #ifdef LOG 1522 /* check for newlines and log if necessary */ 1523 (void) denlstring(f); 1524 #endif 1525 1526 l--; 1527 while (l > 0 && *f != '\0') 1528 { 1529 if (isascii(*f) && 1530 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) 1531 { 1532 l--; 1533 *t++ = *f; 1534 } 1535 f++; 1536 } 1537 *t = '\0'; 1538 } 1539 /* 1540 ** DENLSTRING -- convert newlines in a string to spaces 1541 ** 1542 ** Parameters: 1543 ** s -- the input string 1544 ** 1545 ** Returns: 1546 ** A pointer to a version of the string with newlines 1547 ** mapped to spaces. This should be copied. 1548 */ 1549 1550 char * 1551 denlstring(s) 1552 char *s; 1553 { 1554 register char *p; 1555 int l; 1556 static char *bp = NULL; 1557 static int bl = 0; 1558 1559 if (strchr(s, '\n') == NULL) 1560 return s; 1561 1562 l = strlen(s) + 1; 1563 if (bl < l) 1564 { 1565 /* allocate more space */ 1566 if (bp != NULL) 1567 free(bp); 1568 bp = xalloc(l); 1569 bl = l; 1570 } 1571 strcpy(bp, s); 1572 for (p = bp; (p = strchr(p, '\n')) != NULL; ) 1573 *p++ = ' '; 1574 1575 #ifdef LOG 1576 p = macvalue('_', CurEnv); 1577 syslog(LOG_ALERT, "POSSIBLE ATTACK from %s: newline in string \"%s\"", 1578 p == NULL ? "[UNKNOWN]" : p, bp); 1579 #endif 1580 1581 return bp; 1582 } 1583