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